From 67ae8def2ede7f2eff119625be65c6da577fc3cb Mon Sep 17 00:00:00 2001 From: flub Date: Fri, 22 May 2026 12:58:08 +0000 Subject: [PATCH] deploy: de146261caf6edcbd261a4d233d2c32cbf996045 --- pr/672/docs/.lock | 0 pr/672/docs/bench/all.html | 1 + pr/672/docs/bench/enum.CipherSuite.html | 34 + pr/672/docs/bench/enum.RuntimeType.html | 28 + .../fn.configure_tracing_subscriber.html | 1 + pr/672/docs/bench/fn.connect_client.html | 6 + pr/672/docs/bench/fn.drain_stream.html | 4 + pr/672/docs/bench/fn.rt.html | 1 + pr/672/docs/bench/fn.send_data_on_stream.html | 4 + pr/672/docs/bench/fn.server_endpoint.html | 7 + pr/672/docs/bench/fn.transport_config.html | 1 + pr/672/docs/bench/index.html | 1 + pr/672/docs/bench/sidebar-items.js | 1 + .../docs/bench/stats/fn.throughput_bps.html | 1 + pr/672/docs/bench/stats/index.html | 1 + pr/672/docs/bench/stats/sidebar-items.js | 1 + pr/672/docs/bench/stats/struct.Stats.html | 28 + .../docs/bench/stats/struct.StreamStats.html | 26 + .../bench/stats/struct.TransferResult.html | 27 + pr/672/docs/bench/struct.Opt.html | 69 + pr/672/docs/bulk/all.html | 1 + pr/672/docs/bulk/fn.client.html | 5 + pr/672/docs/bulk/fn.handle_client_stream.html | 5 + pr/672/docs/bulk/fn.main.html | 1 + pr/672/docs/bulk/fn.server.html | 1 + pr/672/docs/bulk/index.html | 1 + pr/672/docs/bulk/sidebar-items.js | 1 + pr/672/docs/bulk/struct.ClientStats.html | 26 + pr/672/docs/certificate/all.html | 1 + .../docs/certificate/fn.configure_client.html | 1 + .../fn.generate_self_signed_cert.html | 1 + pr/672/docs/certificate/fn.main.html | 1 + .../certificate/fn.read_certs_from_file.html | 1 + pr/672/docs/certificate/index.html | 1 + pr/672/docs/certificate/sidebar-items.js | 1 + .../struct.SkipServerVerification.html | 43 + pr/672/docs/crates.js | 2 + pr/672/docs/data_transfer/all.html | 1 + pr/672/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/672/docs/data_transfer/index.html | 1 + pr/672/docs/data_transfer/sidebar-items.js | 1 + pr/672/docs/help.html | 1 + pr/672/docs/noq/all.html | 1 + pr/672/docs/noq/congestion/index.html | 4 + pr/672/docs/noq/congestion/sidebar-items.js | 1 + pr/672/docs/noq/congestion/struct.Bbr3.html | 60 + .../noq/congestion/struct.Bbr3Config.html | 39 + .../congestion/struct.ControllerMetrics.html | 35 + pr/672/docs/noq/congestion/struct.Cubic.html | 55 + .../noq/congestion/struct.CubicConfig.html | 32 + .../docs/noq/congestion/struct.NewReno.html | 59 + .../noq/congestion/struct.NewRenoConfig.html | 33 + .../docs/noq/congestion/trait.Controller.html | 107 + .../congestion/trait.ControllerFactory.html | 10 + .../connection/enum.SendDatagramError.html | 11 + .../docs/noq/connection/struct.AcceptBi.html | 11 + .../docs/noq/connection/struct.AcceptUni.html | 11 + pr/672/docs/noq/connection/struct.Closed.html | 11 + .../noq/connection/struct.Connecting.html | 11 + .../noq/connection/struct.Connection.html | 11 + .../docs/noq/connection/struct.OnClosed.html | 11 + pr/672/docs/noq/connection/struct.OpenBi.html | 11 + .../docs/noq/connection/struct.OpenUni.html | 11 + .../noq/connection/struct.ReadDatagram.html | 11 + .../noq/connection/struct.SendDatagram.html | 11 + .../struct.WeakConnectionHandle.html | 11 + .../connection/struct.ZeroRttAccepted.html | 11 + pr/672/docs/noq/crypto/index.html | 8 + pr/672/docs/noq/crypto/rustls/enum.Error.html | 100 + pr/672/docs/noq/crypto/rustls/index.html | 2 + .../docs/noq/crypto/rustls/sidebar-items.js | 1 + .../crypto/rustls/struct.HandshakeData.html | 34 + .../rustls/struct.NoInitialCipherSuite.html | 30 + .../rustls/struct.QuicClientConfig.html | 54 + .../rustls/struct.QuicServerConfig.html | 58 + .../noq/crypto/rustls/struct.TlsSession.html | 37 + pr/672/docs/noq/crypto/sidebar-items.js | 1 + .../docs/noq/crypto/struct.CryptoError.html | 24 + .../struct.ExportKeyingMaterialError.html | 30 + pr/672/docs/noq/crypto/struct.KeyPair.html | 35 + pr/672/docs/noq/crypto/struct.Keys.html | 29 + .../noq/crypto/struct.UnsupportedVersion.html | 24 + .../docs/noq/crypto/trait.ClientConfig.html | 16 + .../noq/crypto/trait.HandshakeTokenKey.html | 23 + pr/672/docs/noq/crypto/trait.HeaderKey.html | 10 + pr/672/docs/noq/crypto/trait.HmacKey.html | 10 + pr/672/docs/noq/crypto/trait.PacketKey.html | 51 + .../docs/noq/crypto/trait.ServerConfig.html | 39 + pr/672/docs/noq/crypto/trait.Session.html | 69 + pr/672/docs/noq/endpoint/struct.Accept.html | 11 + pr/672/docs/noq/endpoint/struct.Endpoint.html | 11 + .../noq/endpoint/struct.EndpointStats.html | 11 + pr/672/docs/noq/enum.ClosePathError.html | 40 + pr/672/docs/noq/enum.ConfigError.html | 35 + pr/672/docs/noq/enum.ConnectError.html | 50 + pr/672/docs/noq/enum.ConnectionError.html | 53 + pr/672/docs/noq/enum.Dir.html | 53 + pr/672/docs/noq/enum.EcnCodepoint.html | 40 + pr/672/docs/noq/enum.FrameType.html | 78 + pr/672/docs/noq/enum.PathError.html | 45 + pr/672/docs/noq/enum.PathEvent.html | 71 + pr/672/docs/noq/enum.PathStatus.html | 42 + pr/672/docs/noq/enum.ReadError.html | 44 + pr/672/docs/noq/enum.ReadExactError.html | 37 + pr/672/docs/noq/enum.ReadToEndError.html | 37 + pr/672/docs/noq/enum.ResetError.html | 39 + pr/672/docs/noq/enum.SendDatagramError.html | 43 + pr/672/docs/noq/enum.SetPathStatusError.html | 37 + pr/672/docs/noq/enum.Side.html | 54 + pr/672/docs/noq/enum.StoppedError.html | 39 + pr/672/docs/noq/enum.WriteError.html | 44 + .../docs/noq/event_stream/struct.Lagged.html | 11 + .../struct.NatTraversalUpdates.html | 11 + .../struct.ObservedExternalAddr.html | 11 + .../noq/event_stream/struct.PathEvents.html | 11 + pr/672/docs/noq/fn.default_runtime.html | 5 + pr/672/docs/noq/incoming/struct.Incoming.html | 11 + .../noq/incoming/struct.IncomingFuture.html | 11 + .../docs/noq/incoming/struct.RetryError.html | 11 + pr/672/docs/noq/index.html | 34 + .../noq/path/struct.AddressDiscovery.html | 11 + pr/672/docs/noq/path/struct.OpenPath.html | 11 + pr/672/docs/noq/path/struct.Path.html | 11 + .../docs/noq/path/struct.WeakPathHandle.html | 11 + .../docs/noq/recv_stream/enum.ReadError.html | 11 + .../noq/recv_stream/enum.ReadExactError.html | 11 + .../noq/recv_stream/enum.ReadToEndError.html | 11 + .../docs/noq/recv_stream/enum.ResetError.html | 11 + .../noq/recv_stream/struct.RecvStream.html | 11 + .../struct.UnorderedRecvStream.html | 11 + .../docs/noq/runtime/fn.default_runtime.html | 11 + .../noq/runtime/smol/struct.SmolRuntime.html | 11 + .../runtime/tokio/struct.TokioRuntime.html | 11 + pr/672/docs/noq/runtime/trait.AsyncTimer.html | 11 + .../noq/runtime/trait.AsyncUdpSocket.html | 11 + pr/672/docs/noq/runtime/trait.Runtime.html | 11 + pr/672/docs/noq/runtime/trait.UdpSender.html | 11 + .../noq/send_stream/enum.StoppedError.html | 11 + .../docs/noq/send_stream/enum.WriteError.html | 11 + .../noq/send_stream/struct.SendStream.html | 11 + .../docs/noq/send_stream/struct.Stopped.html | 11 + pr/672/docs/noq/sidebar-items.js | 1 + pr/672/docs/noq/struct.Accept.html | 49 + pr/672/docs/noq/struct.AcceptBi.html | 53 + pr/672/docs/noq/struct.AcceptUni.html | 53 + .../docs/noq/struct.AckFrequencyConfig.html | 64 + pr/672/docs/noq/struct.AddressDiscovery.html | 172 + pr/672/docs/noq/struct.ApplicationClose.html | 37 + pr/672/docs/noq/struct.BloomTokenLog.html | 47 + pr/672/docs/noq/struct.Chunk.html | 34 + pr/672/docs/noq/struct.ClientConfig.html | 48 + pr/672/docs/noq/struct.Closed.html | 37 + pr/672/docs/noq/struct.ClosedPath.html | 32 + pr/672/docs/noq/struct.ClosedStream.html | 32 + pr/672/docs/noq/struct.Connecting.html | 100 + pr/672/docs/noq/struct.Connection.html | 222 + pr/672/docs/noq/struct.ConnectionClose.html | 39 + pr/672/docs/noq/struct.ConnectionId.html | 2468 ++++++ pr/672/docs/noq/struct.ConnectionStats.html | 41 + pr/672/docs/noq/struct.DecryptedInitial.html | 30 + pr/672/docs/noq/struct.Endpoint.html | 130 + pr/672/docs/noq/struct.EndpointConfig.html | 68 + pr/672/docs/noq/struct.EndpointStats.html | 35 + pr/672/docs/noq/struct.FourTuple.html | 44 + pr/672/docs/noq/struct.FrameStats.html | 69 + pr/672/docs/noq/struct.IdleTimeout.html | 51 + pr/672/docs/noq/struct.Incoming.html | 48 + pr/672/docs/noq/struct.IncomingFuture.html | 52 + pr/672/docs/noq/struct.InvalidCid.html | 26 + pr/672/docs/noq/struct.Lagged.html | 34 + .../docs/noq/struct.MtuDiscoveryConfig.html | 87 + .../docs/noq/struct.NatTraversalUpdates.html | 174 + pr/672/docs/noq/struct.NoneTokenLog.html | 30 + pr/672/docs/noq/struct.NoneTokenStore.html | 24 + .../docs/noq/struct.ObservedExternalAddr.html | 175 + pr/672/docs/noq/struct.OnClosed.html | 49 + pr/672/docs/noq/struct.OpenBi.html | 53 + pr/672/docs/noq/struct.OpenPath.html | 56 + pr/672/docs/noq/struct.OpenUni.html | 53 + pr/672/docs/noq/struct.Path.html | 66 + pr/672/docs/noq/struct.PathEvents.html | 174 + pr/672/docs/noq/struct.PathId.html | 52 + pr/672/docs/noq/struct.PathStats.html | 62 + pr/672/docs/noq/struct.QlogConfig.html | 33 + pr/672/docs/noq/struct.QlogFileFactory.html | 38 + pr/672/docs/noq/struct.ReadDatagram.html | 53 + pr/672/docs/noq/struct.RecvStream.html | 201 + pr/672/docs/noq/struct.RetryError.html | 26 + pr/672/docs/noq/struct.SendDatagram.html | 53 + pr/672/docs/noq/struct.SendStream.html | 216 + pr/672/docs/noq/struct.ServerConfig.html | 113 + pr/672/docs/noq/struct.SmolRuntime.html | 24 + pr/672/docs/noq/struct.StdSystemTime.html | 25 + pr/672/docs/noq/struct.Stopped.html | 53 + pr/672/docs/noq/struct.StreamId.html | 54 + pr/672/docs/noq/struct.TokenMemoryCache.html | 30 + pr/672/docs/noq/struct.TokenReuseError.html | 24 + pr/672/docs/noq/struct.TokioRuntime.html | 24 + pr/672/docs/noq/struct.Transmit.html | 36 + pr/672/docs/noq/struct.TransportConfig.html | 255 + .../docs/noq/struct.TransportErrorCode.html | 56 + pr/672/docs/noq/struct.UdpStats.html | 41 + .../docs/noq/struct.UnorderedRecvStream.html | 53 + .../noq/struct.ValidationTokenConfig.html | 73 + pr/672/docs/noq/struct.VarInt.html | 64 + .../docs/noq/struct.VarIntBoundsExceeded.html | 32 + .../docs/noq/struct.WeakConnectionHandle.html | 31 + pr/672/docs/noq/struct.WeakPathHandle.html | 37 + pr/672/docs/noq/struct.ZeroRttAccepted.html | 50 + pr/672/docs/noq/trait.AsyncTimer.html | 11 + pr/672/docs/noq/trait.AsyncUdpSocket.html | 38 + .../docs/noq/trait.ConnectionIdGenerator.html | 21 + pr/672/docs/noq/trait.NetworkChangeHint.html | 15 + pr/672/docs/noq/trait.QlogFactory.html | 23 + pr/672/docs/noq/trait.Runtime.html | 19 + pr/672/docs/noq/trait.TimeSource.html | 7 + pr/672/docs/noq/trait.TokenLog.html | 45 + pr/672/docs/noq/trait.TokenStore.html | 13 + pr/672/docs/noq/trait.UdpSender.html | 30 + pr/672/docs/noq_perf/all.html | 1 + pr/672/docs/noq_perf/enum.Commands.html | 39 + pr/672/docs/noq_perf/fn.main.html | 1 + pr/672/docs/noq_perf/index.html | 1 + pr/672/docs/noq_perf/sidebar-items.js | 1 + pr/672/docs/noq_perf/struct.Cli.html | 44 + pr/672/docs/noq_proto/all.html | 1 + .../docs/noq_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 + .../noq_proto/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 + pr/672/docs/noq_proto/coding/index.html | 2 + pr/672/docs/noq_proto/coding/sidebar-items.js | 1 + .../coding/struct.UnexpectedEnd.html | 32 + .../noq_proto/coding/trait.Decodable.html | 6 + .../noq_proto/coding/trait.Encodable.html | 6 + pr/672/docs/noq_proto/coding/type.Result.html | 7 + .../noq_proto/config/enum.ConfigError.html | 11 + .../config/qlog/struct.QlogConfig.html | 11 + .../config/qlog/struct.QlogFileFactory.html | 11 + .../config/qlog/trait.QlogFactory.html | 11 + .../noq_proto/config/struct.ClientConfig.html | 11 + .../config/struct.EndpointConfig.html | 11 + .../noq_proto/config/struct.ServerConfig.html | 11 + .../config/struct.StdSystemTime.html | 11 + .../config/struct.ValidationTokenConfig.html | 11 + .../noq_proto/config/trait.TimeSource.html | 11 + .../transport/struct.AckFrequencyConfig.html | 11 + .../config/transport/struct.IdleTimeout.html | 11 + .../transport/struct.MtuDiscoveryConfig.html | 11 + .../transport/struct.TransportConfig.html | 11 + .../congestion/bbr3/struct.Bbr3.html | 11 + .../congestion/bbr3/struct.Bbr3Config.html | 11 + .../congestion/cubic/struct.Cubic.html | 11 + .../congestion/cubic/struct.CubicConfig.html | 11 + pr/672/docs/noq_proto/congestion/index.html | 4 + .../congestion/new_reno/struct.NewReno.html | 11 + .../new_reno/struct.NewRenoConfig.html | 11 + .../noq_proto/congestion/sidebar-items.js | 1 + .../noq_proto/congestion/struct.Bbr3.html | 60 + .../congestion/struct.Bbr3Config.html | 39 + .../congestion/struct.ControllerMetrics.html | 35 + .../noq_proto/congestion/struct.Cubic.html | 55 + .../congestion/struct.CubicConfig.html | 28 + .../noq_proto/congestion/struct.NewReno.html | 55 + .../congestion/struct.NewRenoConfig.html | 29 + .../congestion/trait.Controller.html | 107 + .../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 + .../docs/noq_proto/connection/enum.Event.html | 11 + .../noq_proto/connection/enum.PathError.html | 11 + .../paths/enum.PathAbandonReason.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/struct.ClosedStream.html | 11 + .../connection/streams/struct.RecvStream.html | 11 + .../connection/streams/struct.SendStream.html | 11 + .../streams/struct.ShouldTransmit.html | 11 + .../connection/streams/struct.Streams.html | 11 + .../connection/struct.Connection.html | 11 + .../struct.MultipathNotNegotiated.html | 11 + .../connection/trait.NetworkChangeHint.html | 11 + .../constant.DEFAULT_SUPPORTED_VERSIONS.html | 2 + pr/672/docs/noq_proto/crypto/index.html | 8 + .../noq_proto/crypto/rustls/enum.Error.html | 100 + .../docs/noq_proto/crypto/rustls/index.html | 2 + .../noq_proto/crypto/rustls/sidebar-items.js | 1 + .../crypto/rustls/struct.HandshakeData.html | 34 + .../rustls/struct.NoInitialCipherSuite.html | 30 + .../rustls/struct.QuicClientConfig.html | 50 + .../rustls/struct.QuicServerConfig.html | 54 + .../crypto/rustls/struct.TlsSession.html | 39 + pr/672/docs/noq_proto/crypto/sidebar-items.js | 1 + .../noq_proto/crypto/struct.CryptoError.html | 24 + .../struct.ExportKeyingMaterialError.html | 30 + .../docs/noq_proto/crypto/struct.KeyPair.html | 35 + pr/672/docs/noq_proto/crypto/struct.Keys.html | 29 + .../crypto/struct.UnsupportedVersion.html | 24 + .../noq_proto/crypto/trait.ClientConfig.html | 16 + .../crypto/trait.HandshakeTokenKey.html | 23 + .../noq_proto/crypto/trait.HeaderKey.html | 10 + .../docs/noq_proto/crypto/trait.HmacKey.html | 10 + .../noq_proto/crypto/trait.PacketKey.html | 51 + .../noq_proto/crypto/trait.ServerConfig.html | 39 + .../docs/noq_proto/crypto/trait.Session.html | 73 + .../noq_proto/endpoint/enum.ConnectError.html | 11 + .../endpoint/enum.DatagramEvent.html | 11 + .../endpoint/struct.AcceptError.html | 11 + .../endpoint/struct.ConnectionHandle.html | 11 + .../endpoint/struct.DecryptedInitial.html | 11 + .../noq_proto/endpoint/struct.Endpoint.html | 11 + .../noq_proto/endpoint/struct.Incoming.html | 11 + .../endpoint/struct.IncomingAlpns.html | 11 + .../noq_proto/endpoint/struct.RetryError.html | 11 + .../docs/noq_proto/enum.ClosePathError.html | 40 + pr/672/docs/noq_proto/enum.ConfigError.html | 35 + pr/672/docs/noq_proto/enum.ConnectError.html | 50 + .../docs/noq_proto/enum.ConnectionError.html | 53 + pr/672/docs/noq_proto/enum.DatagramEvent.html | 31 + pr/672/docs/noq_proto/enum.Dir.html | 52 + pr/672/docs/noq_proto/enum.EcnCodepoint.html | 40 + pr/672/docs/noq_proto/enum.Event.html | 50 + pr/672/docs/noq_proto/enum.FinishError.html | 39 + pr/672/docs/noq_proto/enum.FrameType.html | 76 + pr/672/docs/noq_proto/enum.LongType.html | 36 + pr/672/docs/noq_proto/enum.MaybeFrame.html | 39 + .../noq_proto/enum.PacketDecodeError.html | 54 + .../noq_proto/enum.PathAbandonReason.html | 48 + pr/672/docs/noq_proto/enum.PathError.html | 45 + pr/672/docs/noq_proto/enum.PathEvent.html | 71 + pr/672/docs/noq_proto/enum.PathStatus.html | 42 + .../docs/noq_proto/enum.ProtectedHeader.html | 74 + pr/672/docs/noq_proto/enum.ReadError.html | 50 + pr/672/docs/noq_proto/enum.ReadableError.html | 49 + .../noq_proto/enum.SendDatagramError.html | 53 + .../noq_proto/enum.SetPathStatusError.html | 37 + pr/672/docs/noq_proto/enum.Side.html | 53 + pr/672/docs/noq_proto/enum.StreamEvent.html | 63 + pr/672/docs/noq_proto/enum.WriteError.html | 53 + .../docs/noq_proto/frame/enum.FrameType.html | 11 + .../docs/noq_proto/frame/enum.MaybeFrame.html | 11 + .../frame/struct.ApplicationClose.html | 11 + .../frame/struct.ConnectionClose.html | 11 + .../docs/noq_proto/frame/struct.Datagram.html | 11 + .../noq_proto/frame/struct.DatagramInfo.html | 11 + .../frame/struct.InvalidFrameId.html | 11 + .../noq_proto/frame/struct.StreamInfo.html | 11 + pr/672/docs/noq_proto/index.html | 14 + .../n0_nat_traversal/enum.Error.html | 38 + .../n0_nat_traversal/enum.Event.html | 31 + .../noq_proto/n0_nat_traversal/index.html | 2 + .../n0_nat_traversal/sidebar-items.js | 1 + .../docs/noq_proto/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 + .../noq_proto/shared/enum.EcnCodepoint.html | 11 + .../shared/struct.ConnectionEvent.html | 11 + .../noq_proto/shared/struct.ConnectionId.html | 11 + .../shared/struct.EndpointEvent.html | 11 + pr/672/docs/noq_proto/sidebar-items.js | 1 + pr/672/docs/noq_proto/struct.AcceptError.html | 29 + .../noq_proto/struct.AckFrequencyConfig.html | 58 + .../noq_proto/struct.ApplicationClose.html | 37 + .../docs/noq_proto/struct.BloomTokenLog.html | 47 + pr/672/docs/noq_proto/struct.Chunk.html | 34 + pr/672/docs/noq_proto/struct.Chunks.html | 40 + .../docs/noq_proto/struct.ClientConfig.html | 45 + pr/672/docs/noq_proto/struct.ClosedPath.html | 32 + .../docs/noq_proto/struct.ClosedStream.html | 32 + pr/672/docs/noq_proto/struct.Connection.html | 277 + .../noq_proto/struct.ConnectionClose.html | 39 + .../noq_proto/struct.ConnectionEvent.html | 24 + .../noq_proto/struct.ConnectionHandle.html | 41 + .../docs/noq_proto/struct.ConnectionId.html | 2467 ++++++ .../noq_proto/struct.ConnectionStats.html | 41 + pr/672/docs/noq_proto/struct.Datagram.html | 30 + .../docs/noq_proto/struct.DatagramInfo.html | 31 + pr/672/docs/noq_proto/struct.Datagrams.html | 43 + .../noq_proto/struct.DecryptedInitial.html | 30 + pr/672/docs/noq_proto/struct.Endpoint.html | 77 + .../docs/noq_proto/struct.EndpointConfig.html | 65 + .../docs/noq_proto/struct.EndpointEvent.html | 30 + .../struct.FixedLengthConnectionIdParser.html | 25 + pr/672/docs/noq_proto/struct.FourTuple.html | 43 + pr/672/docs/noq_proto/struct.FrameStats.html | 69 + .../struct.HashedConnectionIdGenerator.html | 32 + pr/672/docs/noq_proto/struct.IdleTimeout.html | 48 + pr/672/docs/noq_proto/struct.Incoming.html | 38 + .../docs/noq_proto/struct.IncomingAlpns.html | 639 ++ pr/672/docs/noq_proto/struct.InvalidCid.html | 26 + .../docs/noq_proto/struct.InvalidFrameId.html | 30 + .../noq_proto/struct.MtuDiscoveryConfig.html | 84 + .../struct.MultipathNotNegotiated.html | 27 + .../docs/noq_proto/struct.NoneTokenLog.html | 30 + .../docs/noq_proto/struct.NoneTokenStore.html | 24 + .../docs/noq_proto/struct.PartialDecode.html | 41 + pr/672/docs/noq_proto/struct.PathId.html | 48 + pr/672/docs/noq_proto/struct.PathStats.html | 62 + .../struct.ProtectedInitialHeader.html | 37 + pr/672/docs/noq_proto/struct.QlogConfig.html | 33 + .../noq_proto/struct.QlogFileFactory.html | 38 + .../struct.RandomConnectionIdGenerator.html | 32 + pr/672/docs/noq_proto/struct.RecvStream.html | 46 + pr/672/docs/noq_proto/struct.RetryError.html | 26 + .../docs/noq_proto/struct.RttEstimator.html | 31 + pr/672/docs/noq_proto/struct.SendStream.html | 52 + .../docs/noq_proto/struct.ServerConfig.html | 101 + .../docs/noq_proto/struct.ShouldTransmit.html | 34 + .../docs/noq_proto/struct.StdSystemTime.html | 25 + pr/672/docs/noq_proto/struct.StreamId.html | 51 + pr/672/docs/noq_proto/struct.StreamInfo.html | 31 + pr/672/docs/noq_proto/struct.Streams.html | 34 + .../noq_proto/struct.TokenMemoryCache.html | 27 + .../noq_proto/struct.TokenReuseError.html | 24 + pr/672/docs/noq_proto/struct.Transmit.html | 36 + .../noq_proto/struct.TransportConfig.html | 222 + .../docs/noq_proto/struct.TransportError.html | 44 + .../noq_proto/struct.TransportErrorCode.html | 54 + pr/672/docs/noq_proto/struct.UdpStats.html | 41 + .../struct.ValidationTokenConfig.html | 73 + pr/672/docs/noq_proto/struct.VarInt.html | 61 + .../struct.VarIntBoundsExceeded.html | 32 + .../noq_proto/token/struct.NoneTokenLog.html | 11 + .../token/struct.NoneTokenStore.html | 11 + .../token/struct.TokenReuseError.html | 11 + .../docs/noq_proto/token/trait.TokenLog.html | 11 + .../noq_proto/token/trait.TokenStore.html | 11 + .../struct.TokenMemoryCache.html | 11 + .../trait.ConnectionIdGenerator.html | 21 + .../noq_proto/trait.ConnectionIdParser.html | 9 + .../noq_proto/trait.NetworkChangeHint.html | 15 + pr/672/docs/noq_proto/trait.QlogFactory.html | 23 + pr/672/docs/noq_proto/trait.TimeSource.html | 7 + pr/672/docs/noq_proto/trait.TokenLog.html | 45 + pr/672/docs/noq_proto/trait.TokenStore.html | 13 + .../transport_error/struct.Code.html | 11 + .../transport_error/struct.Error.html | 11 + .../transport_parameters/enum.Error.html | 37 + .../noq_proto/transport_parameters/index.html | 7 + .../transport_parameters/sidebar-items.js | 1 + .../struct.TransportParameters.html | 37 + .../docs/noq_proto/varint/struct.VarInt.html | 11 + .../varint/struct.VarIntBoundsExceeded.html | 11 + pr/672/docs/noq_udp/all.html | 1 + pr/672/docs/noq_udp/constant.BATCH_SIZE.html | 2 + pr/672/docs/noq_udp/enum.EcnCodepoint.html | 28 + .../noq_udp/imp/struct.UdpSocketState.html | 11 + pr/672/docs/noq_udp/index.html | 22 + pr/672/docs/noq_udp/sidebar-items.js | 1 + pr/672/docs/noq_udp/struct.RecvMeta.html | 41 + pr/672/docs/noq_udp/struct.Transmit.html | 31 + pr/672/docs/noq_udp/struct.UdpSockRef.html | 19 + .../docs/noq_udp/struct.UdpSocketState.html | 62 + pr/672/docs/perf/all.html | 1 + pr/672/docs/perf/client/fn.run.html | 1 + pr/672/docs/perf/client/index.html | 1 + pr/672/docs/perf/client/sidebar-items.js | 1 + pr/672/docs/perf/client/struct.Opt.html | 42 + .../docs/perf/enum.CongestionAlgorithm.html | 29 + pr/672/docs/perf/fn.parse_byte_size.html | 1 + pr/672/docs/perf/index.html | 1 + pr/672/docs/perf/noprotection/index.html | 1 + .../docs/perf/noprotection/sidebar-items.js | 1 + .../struct.NoProtectionClientConfig.html | 30 + .../struct.NoProtectionServerConfig.html | 38 + pr/672/docs/perf/server/fn.run.html | 1 + pr/672/docs/perf/server/index.html | 1 + pr/672/docs/perf/server/sidebar-items.js | 1 + pr/672/docs/perf/server/struct.Opt.html | 41 + pr/672/docs/perf/sidebar-items.js | 1 + .../docs/perf/static.PERF_CIPHER_SUITES.html | 1 + pr/672/docs/perf/stats/index.html | 1 + pr/672/docs/perf/stats/sidebar-items.js | 1 + .../perf/stats/struct.OpenStreamStats.html | 34 + pr/672/docs/perf/stats/struct.Stats.html | 23 + .../docs/perf/stats/struct.StreamStats.html | 23 + pr/672/docs/perf/struct.CommonOpt.html | 83 + pr/672/docs/search.index/000ba6d7fd92.js | 1 + pr/672/docs/search.index/00ee768feb28.js | 1 + pr/672/docs/search.index/0198ee31ee3b.js | 1 + pr/672/docs/search.index/023324239cca.js | 1 + pr/672/docs/search.index/02e07382c03b.js | 1 + pr/672/docs/search.index/035dbd7fa572.js | 1 + pr/672/docs/search.index/03a9c483da4d.js | 1 + pr/672/docs/search.index/0425426ec9ab.js | 1 + pr/672/docs/search.index/05aae8a6fc63.js | 1 + pr/672/docs/search.index/05b8ba3e24ea.js | 1 + pr/672/docs/search.index/05ba80c533eb.js | 1 + pr/672/docs/search.index/0613632b906a.js | 1 + pr/672/docs/search.index/06c693dd9ca3.js | 1 + pr/672/docs/search.index/06f31413a772.js | 1 + pr/672/docs/search.index/06fa28d2800a.js | 1 + pr/672/docs/search.index/072d10679fb0.js | 1 + pr/672/docs/search.index/0867d8c9f05d.js | 1 + pr/672/docs/search.index/09a203b4464b.js | 1 + pr/672/docs/search.index/0a2e52a6ccac.js | 1 + pr/672/docs/search.index/0a99e2bd71de.js | 1 + pr/672/docs/search.index/0b73e38f9a07.js | 1 + pr/672/docs/search.index/0d39a6ae8014.js | 1 + pr/672/docs/search.index/0d3f98434995.js | 1 + pr/672/docs/search.index/0d9568b14335.js | 1 + pr/672/docs/search.index/0e6582e80826.js | 1 + pr/672/docs/search.index/1089838db3bf.js | 1 + pr/672/docs/search.index/1105a51fe78c.js | 1 + pr/672/docs/search.index/140824e73115.js | 1 + pr/672/docs/search.index/148ca916a394.js | 1 + pr/672/docs/search.index/1573b4768191.js | 1 + pr/672/docs/search.index/161c195e5cd2.js | 1 + pr/672/docs/search.index/16a23e4d7f90.js | 1 + pr/672/docs/search.index/1727786f31fa.js | 1 + pr/672/docs/search.index/1736d97117b1.js | 1 + pr/672/docs/search.index/175082f63529.js | 1 + pr/672/docs/search.index/18d8732f90bc.js | 1 + pr/672/docs/search.index/191d6e7f041f.js | 1 + pr/672/docs/search.index/195d6fe9d0c7.js | 1 + pr/672/docs/search.index/19fb50c323e0.js | 1 + pr/672/docs/search.index/1aeec3eb1f17.js | 1 + pr/672/docs/search.index/1b0a08e9b744.js | 1 + pr/672/docs/search.index/1b5ac0a0d3eb.js | 1 + pr/672/docs/search.index/1c80fc1385ea.js | 1 + pr/672/docs/search.index/1d72d41a89e5.js | 1 + pr/672/docs/search.index/1d79643796e7.js | 1 + pr/672/docs/search.index/1dbb8572c4de.js | 1 + pr/672/docs/search.index/1ed8c6ed4cd9.js | 1 + pr/672/docs/search.index/1fae25a0d497.js | 1 + pr/672/docs/search.index/1fee3e0237a8.js | 1 + pr/672/docs/search.index/206a22bb12ba.js | 1 + pr/672/docs/search.index/2084af11f5a7.js | 1 + pr/672/docs/search.index/20b60bb6dc02.js | 1 + pr/672/docs/search.index/228447d4f222.js | 1 + pr/672/docs/search.index/22b744f970a4.js | 1 + pr/672/docs/search.index/22eb75e289be.js | 1 + pr/672/docs/search.index/233f5ec5c226.js | 1 + pr/672/docs/search.index/23a40eca4762.js | 1 + pr/672/docs/search.index/240f796d6ccd.js | 1 + pr/672/docs/search.index/2424bb274ca9.js | 1 + pr/672/docs/search.index/2618e545b18c.js | 1 + pr/672/docs/search.index/289f0a680251.js | 1 + pr/672/docs/search.index/28cab8635ca9.js | 1 + pr/672/docs/search.index/29212c997b44.js | 1 + pr/672/docs/search.index/2923606a2790.js | 1 + pr/672/docs/search.index/29667f8ae9a2.js | 1 + pr/672/docs/search.index/297772620241.js | 1 + pr/672/docs/search.index/297c2deef0ac.js | 1 + pr/672/docs/search.index/298fec6e5a30.js | 1 + pr/672/docs/search.index/29a80323f87b.js | 1 + pr/672/docs/search.index/29d2100cf07e.js | 1 + pr/672/docs/search.index/29f7585ca898.js | 1 + pr/672/docs/search.index/2b576bc01ad5.js | 1 + pr/672/docs/search.index/2b9e99969beb.js | 1 + pr/672/docs/search.index/2ce5f6d932f6.js | 1 + pr/672/docs/search.index/2d9a6dddfba0.js | 1 + pr/672/docs/search.index/2da0a77b4fbc.js | 1 + pr/672/docs/search.index/2dd3fa22e8d4.js | 1 + pr/672/docs/search.index/2eede6b213b7.js | 1 + pr/672/docs/search.index/3067b7ed1d8a.js | 1 + pr/672/docs/search.index/30dacb34bdb2.js | 1 + pr/672/docs/search.index/31541a51e8db.js | 1 + pr/672/docs/search.index/31eb7fecbe0b.js | 1 + pr/672/docs/search.index/32222f9e344c.js | 1 + pr/672/docs/search.index/336c95938b04.js | 1 + pr/672/docs/search.index/34d17abef16f.js | 1 + pr/672/docs/search.index/3545cf7f836f.js | 1 + pr/672/docs/search.index/357d49476acc.js | 1 + pr/672/docs/search.index/358b5f8021f2.js | 1 + pr/672/docs/search.index/358dcb9f0de5.js | 1 + pr/672/docs/search.index/35eb33e7a6e8.js | 1 + pr/672/docs/search.index/36ce02943eb1.js | 1 + pr/672/docs/search.index/36ce51aa446c.js | 1 + pr/672/docs/search.index/378b1aab08c9.js | 1 + pr/672/docs/search.index/37cb471d121c.js | 1 + pr/672/docs/search.index/37eaa116de94.js | 1 + pr/672/docs/search.index/380eb6ff2905.js | 1 + pr/672/docs/search.index/3840c747afc6.js | 1 + pr/672/docs/search.index/38b51321ef64.js | 1 + pr/672/docs/search.index/3a1ad02b1851.js | 1 + pr/672/docs/search.index/3a8f25986add.js | 1 + pr/672/docs/search.index/3b45dd23db88.js | 1 + pr/672/docs/search.index/3be8e84e40bd.js | 1 + pr/672/docs/search.index/3c4d6a050636.js | 1 + pr/672/docs/search.index/3fae53857d69.js | 1 + pr/672/docs/search.index/40fbc4e3aee2.js | 1 + pr/672/docs/search.index/4129fde217fe.js | 1 + pr/672/docs/search.index/4143e8d28ee3.js | 1 + pr/672/docs/search.index/4177e23c85bd.js | 1 + pr/672/docs/search.index/4239d228a482.js | 1 + pr/672/docs/search.index/426c45f94d9c.js | 1 + pr/672/docs/search.index/4372412479ef.js | 1 + pr/672/docs/search.index/43819ce322e0.js | 1 + pr/672/docs/search.index/4471911fea22.js | 1 + pr/672/docs/search.index/45ccc7ad5291.js | 1 + pr/672/docs/search.index/45d9dd3f98fe.js | 1 + pr/672/docs/search.index/46364023d7e3.js | 1 + pr/672/docs/search.index/482faea55872.js | 1 + pr/672/docs/search.index/487bb0c57643.js | 1 + pr/672/docs/search.index/48dd45a6efaf.js | 1 + pr/672/docs/search.index/48ff0a8ecdcf.js | 1 + pr/672/docs/search.index/4920da678e00.js | 1 + pr/672/docs/search.index/4986f6b50c9a.js | 1 + pr/672/docs/search.index/4a3a9fa5310b.js | 1 + pr/672/docs/search.index/4ba0a8bbc176.js | 1 + pr/672/docs/search.index/4c3658ed6e5f.js | 1 + pr/672/docs/search.index/4c4e5cf71a95.js | 1 + pr/672/docs/search.index/4c6ccd5ef6f9.js | 1 + pr/672/docs/search.index/4ce1093f624a.js | 1 + pr/672/docs/search.index/4d1a3a5d7a12.js | 1 + pr/672/docs/search.index/4db7cfff3d56.js | 1 + pr/672/docs/search.index/4dd22e56451b.js | 1 + pr/672/docs/search.index/4ede297cd13d.js | 1 + pr/672/docs/search.index/4ef056118e58.js | 1 + pr/672/docs/search.index/4f020f79d021.js | 1 + pr/672/docs/search.index/4f7d52082fe6.js | 1 + pr/672/docs/search.index/504ca52d3ae9.js | 1 + pr/672/docs/search.index/5106fdea4a6c.js | 1 + pr/672/docs/search.index/5168b1c9fd9a.js | 1 + pr/672/docs/search.index/51a383abcfdb.js | 1 + pr/672/docs/search.index/51d0879dcf6d.js | 1 + pr/672/docs/search.index/5219aed485bd.js | 1 + pr/672/docs/search.index/52ddf1446ab6.js | 1 + pr/672/docs/search.index/536af0dd1eb6.js | 1 + pr/672/docs/search.index/53a370bed26e.js | 1 + pr/672/docs/search.index/53a9362a0d4b.js | 1 + pr/672/docs/search.index/53ffd91bdfe9.js | 1 + pr/672/docs/search.index/5450ecaeec3d.js | 1 + pr/672/docs/search.index/5525682f4073.js | 1 + pr/672/docs/search.index/5540c5ab7cb3.js | 1 + pr/672/docs/search.index/5560d878572f.js | 1 + pr/672/docs/search.index/560bd128efec.js | 1 + pr/672/docs/search.index/5687f616548f.js | 1 + pr/672/docs/search.index/57030fb90cf6.js | 1 + pr/672/docs/search.index/5718d5014774.js | 1 + pr/672/docs/search.index/5853623e6846.js | 1 + pr/672/docs/search.index/58beb8724380.js | 1 + pr/672/docs/search.index/58cbe3793786.js | 1 + pr/672/docs/search.index/59718cd9c46f.js | 1 + pr/672/docs/search.index/59d3892a5c9c.js | 1 + pr/672/docs/search.index/5b3910277d15.js | 1 + pr/672/docs/search.index/5bbc672548ec.js | 1 + pr/672/docs/search.index/5c06b39c26ac.js | 1 + pr/672/docs/search.index/5c9fb1c311dd.js | 1 + pr/672/docs/search.index/5e777cfd64d1.js | 1 + pr/672/docs/search.index/5ed95ff36a5f.js | 1 + pr/672/docs/search.index/5fb5abc1fd26.js | 1 + pr/672/docs/search.index/6026531291d3.js | 1 + pr/672/docs/search.index/60287e11cc71.js | 1 + pr/672/docs/search.index/608a5d02477c.js | 1 + pr/672/docs/search.index/6094984aadd1.js | 1 + pr/672/docs/search.index/60e7eb5d335c.js | 1 + pr/672/docs/search.index/60e98a5178e4.js | 1 + pr/672/docs/search.index/60ea120b017f.js | 1 + pr/672/docs/search.index/60eabdb6a059.js | 1 + pr/672/docs/search.index/60f27d69272c.js | 1 + pr/672/docs/search.index/62158b71bbd3.js | 1 + pr/672/docs/search.index/624cc81e0578.js | 1 + pr/672/docs/search.index/62a274538f89.js | 1 + pr/672/docs/search.index/62ea63f61793.js | 1 + pr/672/docs/search.index/63002ba13ba6.js | 1 + pr/672/docs/search.index/644655b6a37b.js | 1 + pr/672/docs/search.index/647990943ac3.js | 1 + pr/672/docs/search.index/650b84df086a.js | 1 + pr/672/docs/search.index/651d241c3c65.js | 1 + pr/672/docs/search.index/651ea497c196.js | 1 + pr/672/docs/search.index/65427ca2c609.js | 1 + pr/672/docs/search.index/66c7e9777791.js | 1 + pr/672/docs/search.index/66d031770208.js | 1 + pr/672/docs/search.index/686d5bf1a575.js | 1 + pr/672/docs/search.index/6995c7586e21.js | 1 + pr/672/docs/search.index/6ae15219502a.js | 1 + pr/672/docs/search.index/6b95550a6642.js | 1 + pr/672/docs/search.index/6be7775146ac.js | 1 + pr/672/docs/search.index/6c223b5d689a.js | 1 + pr/672/docs/search.index/6c39ad1a7a87.js | 1 + pr/672/docs/search.index/6ca18fe2a088.js | 1 + pr/672/docs/search.index/6cec104bb6e3.js | 1 + pr/672/docs/search.index/6d172713b950.js | 1 + pr/672/docs/search.index/6d22a2adcab1.js | 1 + pr/672/docs/search.index/6dc7b167965d.js | 1 + pr/672/docs/search.index/6e88e59bfb82.js | 1 + pr/672/docs/search.index/6eb9e85c5b88.js | 1 + pr/672/docs/search.index/6f8ed6ef61e6.js | 1 + pr/672/docs/search.index/70531bfc34d4.js | 1 + pr/672/docs/search.index/7063c86102c3.js | 1 + pr/672/docs/search.index/7093873e356a.js | 1 + pr/672/docs/search.index/712b58a1cc15.js | 1 + pr/672/docs/search.index/715b0a6426a1.js | 1 + pr/672/docs/search.index/719b4a1ba67f.js | 1 + pr/672/docs/search.index/73a9ad245c25.js | 1 + pr/672/docs/search.index/74503773b7e7.js | 1 + pr/672/docs/search.index/747b64cfeb14.js | 1 + pr/672/docs/search.index/758d161430bc.js | 1 + pr/672/docs/search.index/75c77b7919db.js | 1 + pr/672/docs/search.index/763c0d5d4643.js | 1 + pr/672/docs/search.index/769cab48f482.js | 1 + pr/672/docs/search.index/76bc946ad95f.js | 1 + pr/672/docs/search.index/77dc9e44aee7.js | 1 + pr/672/docs/search.index/780b49f04404.js | 1 + pr/672/docs/search.index/7811abcc9a5f.js | 1 + pr/672/docs/search.index/7867cdb9af20.js | 1 + pr/672/docs/search.index/7b4e167c9f1f.js | 1 + pr/672/docs/search.index/7ca9a5d9b5a7.js | 1 + pr/672/docs/search.index/7ce496ab7c6d.js | 1 + pr/672/docs/search.index/7d1520a0ac6f.js | 1 + pr/672/docs/search.index/7f66cf4dc275.js | 1 + .../docs/search.index/alias/a762150c532c.js | 1 + .../search.index/crateNames/2450c9cb4357.js | 1 + pr/672/docs/search.index/desc/5f4b62053dd2.js | 1 + .../docs/search.index/entry/843e82bff23e.js | 1 + .../search.index/function/8915390c6a23.js | 1 + .../generic_inverted_index/11aba052bb82.js | 1 + pr/672/docs/search.index/name/7f055d9d7409.js | 1 + .../normalizedName/c413c0680ead.js | 1 + pr/672/docs/search.index/path/813227ed5be5.js | 1 + pr/672/docs/search.index/root.js | 1 + pr/672/docs/search.index/type/3625b511c301.js | 1 + pr/672/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/672/docs/set_up_connection/fn.client.html | 1 + pr/672/docs/set_up_connection/fn.main.html | 1 + pr/672/docs/set_up_connection/fn.server.html | 1 + pr/672/docs/set_up_connection/index.html | 1 + .../docs/set_up_connection/sidebar-items.js | 1 + pr/672/docs/settings.html | 1 + pr/672/docs/src-files.js | 2 + pr/672/docs/src/bench/lib.rs.html | 294 + pr/672/docs/src/bench/stats.rs.html | 95 + pr/672/docs/src/bulk/bulk.rs.html | 219 + .../docs/src/certificate/certificate.rs.html | 106 + .../src/data_transfer/data-transfer.rs.html | 56 + pr/672/docs/src/noq/connection.rs.html | 1911 ++++ pr/672/docs/src/noq/endpoint.rs.html | 1044 +++ pr/672/docs/src/noq/event_stream.rs.html | 114 + pr/672/docs/src/noq/incoming.rs.html | 160 + pr/672/docs/src/noq/lib.rs.html | 144 + pr/672/docs/src/noq/mutex.rs.html | 163 + pr/672/docs/src/noq/path.rs.html | 478 + pr/672/docs/src/noq/recv_stream.rs.html | 788 ++ pr/672/docs/src/noq/runtime/mod.rs.html | 251 + pr/672/docs/src/noq/runtime/smol.rs.html | 102 + pr/672/docs/src/noq/runtime/tokio.rs.html | 106 + pr/672/docs/src/noq/send_stream.rs.html | 488 ++ pr/672/docs/src/noq/work_limiter.rs.html | 224 + pr/672/docs/src/noq_perf/perf.rs.html | 46 + .../src/noq_proto/address_discovery.rs.html | 126 + .../src/noq_proto/bloom_token_log.rs.html | 368 + .../docs/src/noq_proto/cid_generator.rs.html | 181 + pr/672/docs/src/noq_proto/cid_queue.rs.html | 519 ++ pr/672/docs/src/noq_proto/coding.rs.html | 152 + pr/672/docs/src/noq_proto/config/mod.rs.html | 685 ++ pr/672/docs/src/noq_proto/config/qlog.rs.html | 173 + .../src/noq_proto/config/transport.rs.html | 908 ++ pr/672/docs/src/noq_proto/congestion.rs.html | 148 + .../congestion/bbr3/max_filter.rs.html | 151 + .../src/noq_proto/congestion/bbr3/mod.rs.html | 1743 ++++ .../src/noq_proto/congestion/cubic.rs.html | 302 + .../src/noq_proto/congestion/new_reno.rs.html | 176 + .../connection/ack_frequency.rs.html | 159 + .../noq_proto/connection/assembler.rs.html | 846 ++ .../noq_proto/connection/cid_state.rs.html | 228 + .../noq_proto/connection/datagrams.rs.html | 221 + .../docs/src/noq_proto/connection/mod.rs.html | 7727 +++++++++++++++++ .../src/noq_proto/connection/mtud.rs.html | 982 +++ .../src/noq_proto/connection/pacing.rs.html | 444 + .../connection/packet_builder.rs.html | 432 + .../connection/packet_crypto.rs.html | 509 ++ .../src/noq_proto/connection/paths.rs.html | 1166 +++ .../src/noq_proto/connection/qlog.rs.html | 1297 +++ .../noq_proto/connection/send_buffer.rs.html | 762 ++ .../src/noq_proto/connection/spaces.rs.html | 1595 ++++ .../src/noq_proto/connection/state.rs.html | 440 + .../src/noq_proto/connection/stats.rs.html | 367 + .../noq_proto/connection/streams/mod.rs.html | 573 ++ .../noq_proto/connection/streams/recv.rs.html | 544 ++ .../noq_proto/connection/streams/send.rs.html | 414 + .../connection/streams/state.rs.html | 2230 +++++ .../src/noq_proto/connection/timer.rs.html | 538 ++ .../noq_proto/connection/transmit_buf.rs.html | 236 + .../docs/src/noq_proto/constant_time.rs.html | 22 + pr/672/docs/src/noq_proto/crypto.rs.html | 228 + .../src/noq_proto/crypto/ring_like.rs.html | 75 + .../docs/src/noq_proto/crypto/rustls.rs.html | 680 ++ pr/672/docs/src/noq_proto/endpoint.rs.html | 1648 ++++ pr/672/docs/src/noq_proto/frame.rs.html | 2867 ++++++ pr/672/docs/src/noq_proto/lib.rs.html | 484 ++ .../src/noq_proto/n0_nat_traversal.rs.html | 1075 +++ pr/672/docs/src/noq_proto/packet.rs.html | 1095 +++ .../range_set/array_range_set.rs.html | 253 + .../docs/src/noq_proto/range_set/mod.rs.html | 5 + pr/672/docs/src/noq_proto/shared.rs.html | 215 + pr/672/docs/src/noq_proto/token.rs.html | 492 ++ .../src/noq_proto/token_memory_cache.rs.html | 247 + .../src/noq_proto/transport_error.rs.html | 166 + .../noq_proto/transport_parameters.rs.html | 980 +++ pr/672/docs/src/noq_proto/varint.rs.html | 233 + pr/672/docs/src/noq_udp/cmsg/mod.rs.html | 157 + pr/672/docs/src/noq_udp/cmsg/unix.rs.html | 81 + pr/672/docs/src/noq_udp/lib.rs.html | 302 + pr/672/docs/src/noq_udp/unix.rs.html | 1258 +++ pr/672/docs/src/perf/client.rs.html | 426 + pr/672/docs/src/perf/lib.rs.html | 219 + pr/672/docs/src/perf/noprotection.rs.html | 205 + pr/672/docs/src/perf/server.rs.html | 209 + pr/672/docs/src/perf/stats.rs.html | 412 + .../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/672/docs/static.files/favicon-044be391.svg | 24 + .../static.files/favicon-32x32-eab170b8.png | Bin 0 -> 690 bytes pr/672/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/672/docs/static.files/rustdoc-ca0dd0c4.css | 86 + .../static.files/scrape-examples-2bbcccac.js | 1 + pr/672/docs/static.files/search-8d3311b9.js | 5 + pr/672/docs/static.files/settings-c38705f0.js | 17 + .../docs/static.files/src-script-813739b1.js | 1 + pr/672/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/672/docs/trait.impl/core/cmp/trait.Eq.js | 9 + pr/672/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 + .../iter/traits/iterator/trait.Iterator.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 + .../trait.impl/core/ops/arith/trait.Add.js | 9 + .../core/ops/arith/trait.AddAssign.js | 9 + .../docs/trait.impl/core/ops/bit/trait.Not.js | 9 + .../trait.impl/core/ops/deref/trait.Deref.js | 9 + .../core/ops/deref/trait.DerefMut.js | 9 + .../trait.impl/core/ops/drop/trait.Drop.js | 9 + .../trait.impl/core/ops/index/trait.Index.js | 9 + .../core/ops/index/trait.IndexMut.js | 9 + .../panic/unwind_safe/trait.RefUnwindSafe.js | 9 + .../panic/unwind_safe/trait.UnwindSafe.js | 9 + .../core/str/traits/trait.FromStr.js | 9 + .../futures_core/stream/trait.Stream.js | 9 + .../futures_io/if_std/trait.AsyncRead.js | 9 + .../futures_io/if_std/trait.AsyncWrite.js | 9 + .../noq/runtime/trait.AsyncTimer.js | 9 + .../trait.impl/noq/runtime/trait.Runtime.js | 9 + .../trait.ConnectionIdGenerator.js | 9 + .../noq_proto/coding/trait.Decodable.js | 9 + .../noq_proto/coding/trait.Encodable.js | 9 + .../config/qlog/trait.QlogFactory.js | 9 + .../noq_proto/config/trait.TimeSource.js | 9 + .../noq_proto/congestion/trait.Controller.js | 9 + .../congestion/trait.ControllerFactory.js | 9 + .../noq_proto/crypto/trait.ClientConfig.js | 9 + .../noq_proto/crypto/trait.HeaderKey.js | 9 + .../noq_proto/crypto/trait.HmacKey.js | 9 + .../noq_proto/crypto/trait.PacketKey.js | 9 + .../noq_proto/crypto/trait.ServerConfig.js | 9 + .../noq_proto/crypto/trait.Session.js | 9 + .../packet/trait.ConnectionIdParser.js | 9 + .../noq_proto/token/trait.TokenLog.js | 9 + .../noq_proto/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 + 942 files changed, 70398 insertions(+) create mode 100644 pr/672/docs/.lock create mode 100644 pr/672/docs/bench/all.html create mode 100644 pr/672/docs/bench/enum.CipherSuite.html create mode 100644 pr/672/docs/bench/enum.RuntimeType.html create mode 100644 pr/672/docs/bench/fn.configure_tracing_subscriber.html create mode 100644 pr/672/docs/bench/fn.connect_client.html create mode 100644 pr/672/docs/bench/fn.drain_stream.html create mode 100644 pr/672/docs/bench/fn.rt.html create mode 100644 pr/672/docs/bench/fn.send_data_on_stream.html create mode 100644 pr/672/docs/bench/fn.server_endpoint.html create mode 100644 pr/672/docs/bench/fn.transport_config.html create mode 100644 pr/672/docs/bench/index.html create mode 100644 pr/672/docs/bench/sidebar-items.js create mode 100644 pr/672/docs/bench/stats/fn.throughput_bps.html create mode 100644 pr/672/docs/bench/stats/index.html create mode 100644 pr/672/docs/bench/stats/sidebar-items.js create mode 100644 pr/672/docs/bench/stats/struct.Stats.html create mode 100644 pr/672/docs/bench/stats/struct.StreamStats.html create mode 100644 pr/672/docs/bench/stats/struct.TransferResult.html create mode 100644 pr/672/docs/bench/struct.Opt.html create mode 100644 pr/672/docs/bulk/all.html create mode 100644 pr/672/docs/bulk/fn.client.html create mode 100644 pr/672/docs/bulk/fn.handle_client_stream.html create mode 100644 pr/672/docs/bulk/fn.main.html create mode 100644 pr/672/docs/bulk/fn.server.html create mode 100644 pr/672/docs/bulk/index.html create mode 100644 pr/672/docs/bulk/sidebar-items.js create mode 100644 pr/672/docs/bulk/struct.ClientStats.html create mode 100644 pr/672/docs/certificate/all.html create mode 100644 pr/672/docs/certificate/fn.configure_client.html create mode 100644 pr/672/docs/certificate/fn.generate_self_signed_cert.html create mode 100644 pr/672/docs/certificate/fn.main.html create mode 100644 pr/672/docs/certificate/fn.read_certs_from_file.html create mode 100644 pr/672/docs/certificate/index.html create mode 100644 pr/672/docs/certificate/sidebar-items.js create mode 100644 pr/672/docs/certificate/struct.SkipServerVerification.html create mode 100644 pr/672/docs/crates.js create mode 100644 pr/672/docs/data_transfer/all.html create mode 100644 pr/672/docs/data_transfer/fn.main.html create mode 100644 pr/672/docs/data_transfer/fn.open_bidirectional_stream.html create mode 100644 pr/672/docs/data_transfer/fn.open_unidirectional_stream.html create mode 100644 pr/672/docs/data_transfer/fn.receive_bidirectional_stream.html create mode 100644 pr/672/docs/data_transfer/fn.receive_datagram.html create mode 100644 pr/672/docs/data_transfer/fn.receive_unidirectional_stream.html create mode 100644 pr/672/docs/data_transfer/fn.send_unreliable.html create mode 100644 pr/672/docs/data_transfer/index.html create mode 100644 pr/672/docs/data_transfer/sidebar-items.js create mode 100644 pr/672/docs/help.html create mode 100644 pr/672/docs/noq/all.html create mode 100644 pr/672/docs/noq/congestion/index.html create mode 100644 pr/672/docs/noq/congestion/sidebar-items.js create mode 100644 pr/672/docs/noq/congestion/struct.Bbr3.html create mode 100644 pr/672/docs/noq/congestion/struct.Bbr3Config.html create mode 100644 pr/672/docs/noq/congestion/struct.ControllerMetrics.html create mode 100644 pr/672/docs/noq/congestion/struct.Cubic.html create mode 100644 pr/672/docs/noq/congestion/struct.CubicConfig.html create mode 100644 pr/672/docs/noq/congestion/struct.NewReno.html create mode 100644 pr/672/docs/noq/congestion/struct.NewRenoConfig.html create mode 100644 pr/672/docs/noq/congestion/trait.Controller.html create mode 100644 pr/672/docs/noq/congestion/trait.ControllerFactory.html create mode 100644 pr/672/docs/noq/connection/enum.SendDatagramError.html create mode 100644 pr/672/docs/noq/connection/struct.AcceptBi.html create mode 100644 pr/672/docs/noq/connection/struct.AcceptUni.html create mode 100644 pr/672/docs/noq/connection/struct.Closed.html create mode 100644 pr/672/docs/noq/connection/struct.Connecting.html create mode 100644 pr/672/docs/noq/connection/struct.Connection.html create mode 100644 pr/672/docs/noq/connection/struct.OnClosed.html create mode 100644 pr/672/docs/noq/connection/struct.OpenBi.html create mode 100644 pr/672/docs/noq/connection/struct.OpenUni.html create mode 100644 pr/672/docs/noq/connection/struct.ReadDatagram.html create mode 100644 pr/672/docs/noq/connection/struct.SendDatagram.html create mode 100644 pr/672/docs/noq/connection/struct.WeakConnectionHandle.html create mode 100644 pr/672/docs/noq/connection/struct.ZeroRttAccepted.html create mode 100644 pr/672/docs/noq/crypto/index.html create mode 100644 pr/672/docs/noq/crypto/rustls/enum.Error.html create mode 100644 pr/672/docs/noq/crypto/rustls/index.html create mode 100644 pr/672/docs/noq/crypto/rustls/sidebar-items.js create mode 100644 pr/672/docs/noq/crypto/rustls/struct.HandshakeData.html create mode 100644 pr/672/docs/noq/crypto/rustls/struct.NoInitialCipherSuite.html create mode 100644 pr/672/docs/noq/crypto/rustls/struct.QuicClientConfig.html create mode 100644 pr/672/docs/noq/crypto/rustls/struct.QuicServerConfig.html create mode 100644 pr/672/docs/noq/crypto/rustls/struct.TlsSession.html create mode 100644 pr/672/docs/noq/crypto/sidebar-items.js create mode 100644 pr/672/docs/noq/crypto/struct.CryptoError.html create mode 100644 pr/672/docs/noq/crypto/struct.ExportKeyingMaterialError.html create mode 100644 pr/672/docs/noq/crypto/struct.KeyPair.html create mode 100644 pr/672/docs/noq/crypto/struct.Keys.html create mode 100644 pr/672/docs/noq/crypto/struct.UnsupportedVersion.html create mode 100644 pr/672/docs/noq/crypto/trait.ClientConfig.html create mode 100644 pr/672/docs/noq/crypto/trait.HandshakeTokenKey.html create mode 100644 pr/672/docs/noq/crypto/trait.HeaderKey.html create mode 100644 pr/672/docs/noq/crypto/trait.HmacKey.html create mode 100644 pr/672/docs/noq/crypto/trait.PacketKey.html create mode 100644 pr/672/docs/noq/crypto/trait.ServerConfig.html create mode 100644 pr/672/docs/noq/crypto/trait.Session.html create mode 100644 pr/672/docs/noq/endpoint/struct.Accept.html create mode 100644 pr/672/docs/noq/endpoint/struct.Endpoint.html create mode 100644 pr/672/docs/noq/endpoint/struct.EndpointStats.html create mode 100644 pr/672/docs/noq/enum.ClosePathError.html create mode 100644 pr/672/docs/noq/enum.ConfigError.html create mode 100644 pr/672/docs/noq/enum.ConnectError.html create mode 100644 pr/672/docs/noq/enum.ConnectionError.html create mode 100644 pr/672/docs/noq/enum.Dir.html create mode 100644 pr/672/docs/noq/enum.EcnCodepoint.html create mode 100644 pr/672/docs/noq/enum.FrameType.html create mode 100644 pr/672/docs/noq/enum.PathError.html create mode 100644 pr/672/docs/noq/enum.PathEvent.html create mode 100644 pr/672/docs/noq/enum.PathStatus.html create mode 100644 pr/672/docs/noq/enum.ReadError.html create mode 100644 pr/672/docs/noq/enum.ReadExactError.html create mode 100644 pr/672/docs/noq/enum.ReadToEndError.html create mode 100644 pr/672/docs/noq/enum.ResetError.html create mode 100644 pr/672/docs/noq/enum.SendDatagramError.html create mode 100644 pr/672/docs/noq/enum.SetPathStatusError.html create mode 100644 pr/672/docs/noq/enum.Side.html create mode 100644 pr/672/docs/noq/enum.StoppedError.html create mode 100644 pr/672/docs/noq/enum.WriteError.html create mode 100644 pr/672/docs/noq/event_stream/struct.Lagged.html create mode 100644 pr/672/docs/noq/event_stream/struct.NatTraversalUpdates.html create mode 100644 pr/672/docs/noq/event_stream/struct.ObservedExternalAddr.html create mode 100644 pr/672/docs/noq/event_stream/struct.PathEvents.html create mode 100644 pr/672/docs/noq/fn.default_runtime.html create mode 100644 pr/672/docs/noq/incoming/struct.Incoming.html create mode 100644 pr/672/docs/noq/incoming/struct.IncomingFuture.html create mode 100644 pr/672/docs/noq/incoming/struct.RetryError.html create mode 100644 pr/672/docs/noq/index.html create mode 100644 pr/672/docs/noq/path/struct.AddressDiscovery.html create mode 100644 pr/672/docs/noq/path/struct.OpenPath.html create mode 100644 pr/672/docs/noq/path/struct.Path.html create mode 100644 pr/672/docs/noq/path/struct.WeakPathHandle.html create mode 100644 pr/672/docs/noq/recv_stream/enum.ReadError.html create mode 100644 pr/672/docs/noq/recv_stream/enum.ReadExactError.html create mode 100644 pr/672/docs/noq/recv_stream/enum.ReadToEndError.html create mode 100644 pr/672/docs/noq/recv_stream/enum.ResetError.html create mode 100644 pr/672/docs/noq/recv_stream/struct.RecvStream.html create mode 100644 pr/672/docs/noq/recv_stream/struct.UnorderedRecvStream.html create mode 100644 pr/672/docs/noq/runtime/fn.default_runtime.html create mode 100644 pr/672/docs/noq/runtime/smol/struct.SmolRuntime.html create mode 100644 pr/672/docs/noq/runtime/tokio/struct.TokioRuntime.html create mode 100644 pr/672/docs/noq/runtime/trait.AsyncTimer.html create mode 100644 pr/672/docs/noq/runtime/trait.AsyncUdpSocket.html create mode 100644 pr/672/docs/noq/runtime/trait.Runtime.html create mode 100644 pr/672/docs/noq/runtime/trait.UdpSender.html create mode 100644 pr/672/docs/noq/send_stream/enum.StoppedError.html create mode 100644 pr/672/docs/noq/send_stream/enum.WriteError.html create mode 100644 pr/672/docs/noq/send_stream/struct.SendStream.html create mode 100644 pr/672/docs/noq/send_stream/struct.Stopped.html create mode 100644 pr/672/docs/noq/sidebar-items.js create mode 100644 pr/672/docs/noq/struct.Accept.html create mode 100644 pr/672/docs/noq/struct.AcceptBi.html create mode 100644 pr/672/docs/noq/struct.AcceptUni.html create mode 100644 pr/672/docs/noq/struct.AckFrequencyConfig.html create mode 100644 pr/672/docs/noq/struct.AddressDiscovery.html create mode 100644 pr/672/docs/noq/struct.ApplicationClose.html create mode 100644 pr/672/docs/noq/struct.BloomTokenLog.html create mode 100644 pr/672/docs/noq/struct.Chunk.html create mode 100644 pr/672/docs/noq/struct.ClientConfig.html create mode 100644 pr/672/docs/noq/struct.Closed.html create mode 100644 pr/672/docs/noq/struct.ClosedPath.html create mode 100644 pr/672/docs/noq/struct.ClosedStream.html create mode 100644 pr/672/docs/noq/struct.Connecting.html create mode 100644 pr/672/docs/noq/struct.Connection.html create mode 100644 pr/672/docs/noq/struct.ConnectionClose.html create mode 100644 pr/672/docs/noq/struct.ConnectionId.html create mode 100644 pr/672/docs/noq/struct.ConnectionStats.html create mode 100644 pr/672/docs/noq/struct.DecryptedInitial.html create mode 100644 pr/672/docs/noq/struct.Endpoint.html create mode 100644 pr/672/docs/noq/struct.EndpointConfig.html create mode 100644 pr/672/docs/noq/struct.EndpointStats.html create mode 100644 pr/672/docs/noq/struct.FourTuple.html create mode 100644 pr/672/docs/noq/struct.FrameStats.html create mode 100644 pr/672/docs/noq/struct.IdleTimeout.html create mode 100644 pr/672/docs/noq/struct.Incoming.html create mode 100644 pr/672/docs/noq/struct.IncomingFuture.html create mode 100644 pr/672/docs/noq/struct.InvalidCid.html create mode 100644 pr/672/docs/noq/struct.Lagged.html create mode 100644 pr/672/docs/noq/struct.MtuDiscoveryConfig.html create mode 100644 pr/672/docs/noq/struct.NatTraversalUpdates.html create mode 100644 pr/672/docs/noq/struct.NoneTokenLog.html create mode 100644 pr/672/docs/noq/struct.NoneTokenStore.html create mode 100644 pr/672/docs/noq/struct.ObservedExternalAddr.html create mode 100644 pr/672/docs/noq/struct.OnClosed.html create mode 100644 pr/672/docs/noq/struct.OpenBi.html create mode 100644 pr/672/docs/noq/struct.OpenPath.html create mode 100644 pr/672/docs/noq/struct.OpenUni.html create mode 100644 pr/672/docs/noq/struct.Path.html create mode 100644 pr/672/docs/noq/struct.PathEvents.html create mode 100644 pr/672/docs/noq/struct.PathId.html create mode 100644 pr/672/docs/noq/struct.PathStats.html create mode 100644 pr/672/docs/noq/struct.QlogConfig.html create mode 100644 pr/672/docs/noq/struct.QlogFileFactory.html create mode 100644 pr/672/docs/noq/struct.ReadDatagram.html create mode 100644 pr/672/docs/noq/struct.RecvStream.html create mode 100644 pr/672/docs/noq/struct.RetryError.html create mode 100644 pr/672/docs/noq/struct.SendDatagram.html create mode 100644 pr/672/docs/noq/struct.SendStream.html create mode 100644 pr/672/docs/noq/struct.ServerConfig.html create mode 100644 pr/672/docs/noq/struct.SmolRuntime.html create mode 100644 pr/672/docs/noq/struct.StdSystemTime.html create mode 100644 pr/672/docs/noq/struct.Stopped.html create mode 100644 pr/672/docs/noq/struct.StreamId.html create mode 100644 pr/672/docs/noq/struct.TokenMemoryCache.html create mode 100644 pr/672/docs/noq/struct.TokenReuseError.html create mode 100644 pr/672/docs/noq/struct.TokioRuntime.html create mode 100644 pr/672/docs/noq/struct.Transmit.html create mode 100644 pr/672/docs/noq/struct.TransportConfig.html create mode 100644 pr/672/docs/noq/struct.TransportErrorCode.html create mode 100644 pr/672/docs/noq/struct.UdpStats.html create mode 100644 pr/672/docs/noq/struct.UnorderedRecvStream.html create mode 100644 pr/672/docs/noq/struct.ValidationTokenConfig.html create mode 100644 pr/672/docs/noq/struct.VarInt.html create mode 100644 pr/672/docs/noq/struct.VarIntBoundsExceeded.html create mode 100644 pr/672/docs/noq/struct.WeakConnectionHandle.html create mode 100644 pr/672/docs/noq/struct.WeakPathHandle.html create mode 100644 pr/672/docs/noq/struct.ZeroRttAccepted.html create mode 100644 pr/672/docs/noq/trait.AsyncTimer.html create mode 100644 pr/672/docs/noq/trait.AsyncUdpSocket.html create mode 100644 pr/672/docs/noq/trait.ConnectionIdGenerator.html create mode 100644 pr/672/docs/noq/trait.NetworkChangeHint.html create mode 100644 pr/672/docs/noq/trait.QlogFactory.html create mode 100644 pr/672/docs/noq/trait.Runtime.html create mode 100644 pr/672/docs/noq/trait.TimeSource.html create mode 100644 pr/672/docs/noq/trait.TokenLog.html create mode 100644 pr/672/docs/noq/trait.TokenStore.html create mode 100644 pr/672/docs/noq/trait.UdpSender.html create mode 100644 pr/672/docs/noq_perf/all.html create mode 100644 pr/672/docs/noq_perf/enum.Commands.html create mode 100644 pr/672/docs/noq_perf/fn.main.html create mode 100644 pr/672/docs/noq_perf/index.html create mode 100644 pr/672/docs/noq_perf/sidebar-items.js create mode 100644 pr/672/docs/noq_perf/struct.Cli.html create mode 100644 pr/672/docs/noq_proto/all.html create mode 100644 pr/672/docs/noq_proto/bench_exports/index.html create mode 100644 pr/672/docs/noq_proto/bench_exports/send_buffer_benches/fn.get_into_many_segments.html create mode 100644 pr/672/docs/noq_proto/bench_exports/send_buffer_benches/fn.get_loop_many_segments.html create mode 100644 pr/672/docs/noq_proto/bench_exports/send_buffer_benches/index.html create mode 100644 pr/672/docs/noq_proto/bench_exports/send_buffer_benches/sidebar-items.js create mode 100644 pr/672/docs/noq_proto/bench_exports/sidebar-items.js create mode 100644 pr/672/docs/noq_proto/bloom_token_log/struct.BloomTokenLog.html create mode 100644 pr/672/docs/noq_proto/cid_generator/struct.HashedConnectionIdGenerator.html create mode 100644 pr/672/docs/noq_proto/cid_generator/struct.InvalidCid.html create mode 100644 pr/672/docs/noq_proto/cid_generator/struct.RandomConnectionIdGenerator.html create mode 100644 pr/672/docs/noq_proto/cid_generator/trait.ConnectionIdGenerator.html create mode 100644 pr/672/docs/noq_proto/coding/index.html create mode 100644 pr/672/docs/noq_proto/coding/sidebar-items.js create mode 100644 pr/672/docs/noq_proto/coding/struct.UnexpectedEnd.html create mode 100644 pr/672/docs/noq_proto/coding/trait.Decodable.html create mode 100644 pr/672/docs/noq_proto/coding/trait.Encodable.html create mode 100644 pr/672/docs/noq_proto/coding/type.Result.html create mode 100644 pr/672/docs/noq_proto/config/enum.ConfigError.html create mode 100644 pr/672/docs/noq_proto/config/qlog/struct.QlogConfig.html create mode 100644 pr/672/docs/noq_proto/config/qlog/struct.QlogFileFactory.html create mode 100644 pr/672/docs/noq_proto/config/qlog/trait.QlogFactory.html create mode 100644 pr/672/docs/noq_proto/config/struct.ClientConfig.html create mode 100644 pr/672/docs/noq_proto/config/struct.EndpointConfig.html create mode 100644 pr/672/docs/noq_proto/config/struct.ServerConfig.html create mode 100644 pr/672/docs/noq_proto/config/struct.StdSystemTime.html create mode 100644 pr/672/docs/noq_proto/config/struct.ValidationTokenConfig.html create mode 100644 pr/672/docs/noq_proto/config/trait.TimeSource.html create mode 100644 pr/672/docs/noq_proto/config/transport/struct.AckFrequencyConfig.html create mode 100644 pr/672/docs/noq_proto/config/transport/struct.IdleTimeout.html create mode 100644 pr/672/docs/noq_proto/config/transport/struct.MtuDiscoveryConfig.html create mode 100644 pr/672/docs/noq_proto/config/transport/struct.TransportConfig.html create mode 100644 pr/672/docs/noq_proto/congestion/bbr3/struct.Bbr3.html create mode 100644 pr/672/docs/noq_proto/congestion/bbr3/struct.Bbr3Config.html create mode 100644 pr/672/docs/noq_proto/congestion/cubic/struct.Cubic.html create mode 100644 pr/672/docs/noq_proto/congestion/cubic/struct.CubicConfig.html create mode 100644 pr/672/docs/noq_proto/congestion/index.html create mode 100644 pr/672/docs/noq_proto/congestion/new_reno/struct.NewReno.html create mode 100644 pr/672/docs/noq_proto/congestion/new_reno/struct.NewRenoConfig.html create mode 100644 pr/672/docs/noq_proto/congestion/sidebar-items.js create mode 100644 pr/672/docs/noq_proto/congestion/struct.Bbr3.html create mode 100644 pr/672/docs/noq_proto/congestion/struct.Bbr3Config.html create mode 100644 pr/672/docs/noq_proto/congestion/struct.ControllerMetrics.html create mode 100644 pr/672/docs/noq_proto/congestion/struct.Cubic.html create mode 100644 pr/672/docs/noq_proto/congestion/struct.CubicConfig.html create mode 100644 pr/672/docs/noq_proto/congestion/struct.NewReno.html create mode 100644 pr/672/docs/noq_proto/congestion/struct.NewRenoConfig.html create mode 100644 pr/672/docs/noq_proto/congestion/trait.Controller.html create mode 100644 pr/672/docs/noq_proto/congestion/trait.ControllerFactory.html create mode 100644 pr/672/docs/noq_proto/connection/assembler/struct.Chunk.html create mode 100644 pr/672/docs/noq_proto/connection/datagrams/enum.SendDatagramError.html create mode 100644 pr/672/docs/noq_proto/connection/datagrams/struct.Datagrams.html create mode 100644 pr/672/docs/noq_proto/connection/enum.ClosePathError.html create mode 100644 pr/672/docs/noq_proto/connection/enum.ConnectionError.html create mode 100644 pr/672/docs/noq_proto/connection/enum.Event.html create mode 100644 pr/672/docs/noq_proto/connection/enum.PathError.html create mode 100644 pr/672/docs/noq_proto/connection/paths/enum.PathAbandonReason.html create mode 100644 pr/672/docs/noq_proto/connection/paths/enum.PathEvent.html create mode 100644 pr/672/docs/noq_proto/connection/paths/enum.PathStatus.html create mode 100644 pr/672/docs/noq_proto/connection/paths/enum.SetPathStatusError.html create mode 100644 pr/672/docs/noq_proto/connection/paths/struct.ClosedPath.html create mode 100644 pr/672/docs/noq_proto/connection/paths/struct.PathId.html create mode 100644 pr/672/docs/noq_proto/connection/paths/struct.RttEstimator.html create mode 100644 pr/672/docs/noq_proto/connection/send_buffer/send_buffer_benches/fn.get_into_many_segments.html create mode 100644 pr/672/docs/noq_proto/connection/send_buffer/send_buffer_benches/fn.get_loop_many_segments.html create mode 100644 pr/672/docs/noq_proto/connection/send_buffer/send_buffer_benches/index.html create mode 100644 pr/672/docs/noq_proto/connection/stats/struct.ConnectionStats.html create mode 100644 pr/672/docs/noq_proto/connection/stats/struct.FrameStats.html create mode 100644 pr/672/docs/noq_proto/connection/stats/struct.PathStats.html create mode 100644 pr/672/docs/noq_proto/connection/stats/struct.UdpStats.html create mode 100644 pr/672/docs/noq_proto/connection/streams/enum.StreamEvent.html create mode 100644 pr/672/docs/noq_proto/connection/streams/recv/enum.ReadError.html create mode 100644 pr/672/docs/noq_proto/connection/streams/recv/enum.ReadableError.html create mode 100644 pr/672/docs/noq_proto/connection/streams/recv/struct.Chunks.html create mode 100644 pr/672/docs/noq_proto/connection/streams/send/enum.FinishError.html create mode 100644 pr/672/docs/noq_proto/connection/streams/send/enum.WriteError.html create mode 100644 pr/672/docs/noq_proto/connection/streams/struct.ClosedStream.html create mode 100644 pr/672/docs/noq_proto/connection/streams/struct.RecvStream.html create mode 100644 pr/672/docs/noq_proto/connection/streams/struct.SendStream.html create mode 100644 pr/672/docs/noq_proto/connection/streams/struct.ShouldTransmit.html create mode 100644 pr/672/docs/noq_proto/connection/streams/struct.Streams.html create mode 100644 pr/672/docs/noq_proto/connection/struct.Connection.html create mode 100644 pr/672/docs/noq_proto/connection/struct.MultipathNotNegotiated.html create mode 100644 pr/672/docs/noq_proto/connection/trait.NetworkChangeHint.html create mode 100644 pr/672/docs/noq_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html create mode 100644 pr/672/docs/noq_proto/crypto/index.html create mode 100644 pr/672/docs/noq_proto/crypto/rustls/enum.Error.html create mode 100644 pr/672/docs/noq_proto/crypto/rustls/index.html create mode 100644 pr/672/docs/noq_proto/crypto/rustls/sidebar-items.js create mode 100644 pr/672/docs/noq_proto/crypto/rustls/struct.HandshakeData.html create mode 100644 pr/672/docs/noq_proto/crypto/rustls/struct.NoInitialCipherSuite.html create mode 100644 pr/672/docs/noq_proto/crypto/rustls/struct.QuicClientConfig.html create mode 100644 pr/672/docs/noq_proto/crypto/rustls/struct.QuicServerConfig.html create mode 100644 pr/672/docs/noq_proto/crypto/rustls/struct.TlsSession.html create mode 100644 pr/672/docs/noq_proto/crypto/sidebar-items.js create mode 100644 pr/672/docs/noq_proto/crypto/struct.CryptoError.html create mode 100644 pr/672/docs/noq_proto/crypto/struct.ExportKeyingMaterialError.html create mode 100644 pr/672/docs/noq_proto/crypto/struct.KeyPair.html create mode 100644 pr/672/docs/noq_proto/crypto/struct.Keys.html create mode 100644 pr/672/docs/noq_proto/crypto/struct.UnsupportedVersion.html create mode 100644 pr/672/docs/noq_proto/crypto/trait.ClientConfig.html create mode 100644 pr/672/docs/noq_proto/crypto/trait.HandshakeTokenKey.html create mode 100644 pr/672/docs/noq_proto/crypto/trait.HeaderKey.html create mode 100644 pr/672/docs/noq_proto/crypto/trait.HmacKey.html create mode 100644 pr/672/docs/noq_proto/crypto/trait.PacketKey.html create mode 100644 pr/672/docs/noq_proto/crypto/trait.ServerConfig.html create mode 100644 pr/672/docs/noq_proto/crypto/trait.Session.html create mode 100644 pr/672/docs/noq_proto/endpoint/enum.ConnectError.html create mode 100644 pr/672/docs/noq_proto/endpoint/enum.DatagramEvent.html create mode 100644 pr/672/docs/noq_proto/endpoint/struct.AcceptError.html create mode 100644 pr/672/docs/noq_proto/endpoint/struct.ConnectionHandle.html create mode 100644 pr/672/docs/noq_proto/endpoint/struct.DecryptedInitial.html create mode 100644 pr/672/docs/noq_proto/endpoint/struct.Endpoint.html create mode 100644 pr/672/docs/noq_proto/endpoint/struct.Incoming.html create mode 100644 pr/672/docs/noq_proto/endpoint/struct.IncomingAlpns.html create mode 100644 pr/672/docs/noq_proto/endpoint/struct.RetryError.html create mode 100644 pr/672/docs/noq_proto/enum.ClosePathError.html create mode 100644 pr/672/docs/noq_proto/enum.ConfigError.html create mode 100644 pr/672/docs/noq_proto/enum.ConnectError.html create mode 100644 pr/672/docs/noq_proto/enum.ConnectionError.html create mode 100644 pr/672/docs/noq_proto/enum.DatagramEvent.html create mode 100644 pr/672/docs/noq_proto/enum.Dir.html create mode 100644 pr/672/docs/noq_proto/enum.EcnCodepoint.html create mode 100644 pr/672/docs/noq_proto/enum.Event.html create mode 100644 pr/672/docs/noq_proto/enum.FinishError.html create mode 100644 pr/672/docs/noq_proto/enum.FrameType.html create mode 100644 pr/672/docs/noq_proto/enum.LongType.html create mode 100644 pr/672/docs/noq_proto/enum.MaybeFrame.html create mode 100644 pr/672/docs/noq_proto/enum.PacketDecodeError.html create mode 100644 pr/672/docs/noq_proto/enum.PathAbandonReason.html create mode 100644 pr/672/docs/noq_proto/enum.PathError.html create mode 100644 pr/672/docs/noq_proto/enum.PathEvent.html create mode 100644 pr/672/docs/noq_proto/enum.PathStatus.html create mode 100644 pr/672/docs/noq_proto/enum.ProtectedHeader.html create mode 100644 pr/672/docs/noq_proto/enum.ReadError.html create mode 100644 pr/672/docs/noq_proto/enum.ReadableError.html create mode 100644 pr/672/docs/noq_proto/enum.SendDatagramError.html create mode 100644 pr/672/docs/noq_proto/enum.SetPathStatusError.html create mode 100644 pr/672/docs/noq_proto/enum.Side.html create mode 100644 pr/672/docs/noq_proto/enum.StreamEvent.html create mode 100644 pr/672/docs/noq_proto/enum.WriteError.html create mode 100644 pr/672/docs/noq_proto/frame/enum.FrameType.html create mode 100644 pr/672/docs/noq_proto/frame/enum.MaybeFrame.html create mode 100644 pr/672/docs/noq_proto/frame/struct.ApplicationClose.html create mode 100644 pr/672/docs/noq_proto/frame/struct.ConnectionClose.html create mode 100644 pr/672/docs/noq_proto/frame/struct.Datagram.html create mode 100644 pr/672/docs/noq_proto/frame/struct.DatagramInfo.html create mode 100644 pr/672/docs/noq_proto/frame/struct.InvalidFrameId.html create mode 100644 pr/672/docs/noq_proto/frame/struct.StreamInfo.html create mode 100644 pr/672/docs/noq_proto/index.html create mode 100644 pr/672/docs/noq_proto/n0_nat_traversal/enum.Error.html create mode 100644 pr/672/docs/noq_proto/n0_nat_traversal/enum.Event.html create mode 100644 pr/672/docs/noq_proto/n0_nat_traversal/index.html create mode 100644 pr/672/docs/noq_proto/n0_nat_traversal/sidebar-items.js create mode 100644 pr/672/docs/noq_proto/packet/enum.LongType.html create mode 100644 pr/672/docs/noq_proto/packet/enum.PacketDecodeError.html create mode 100644 pr/672/docs/noq_proto/packet/enum.ProtectedHeader.html create mode 100644 pr/672/docs/noq_proto/packet/struct.FixedLengthConnectionIdParser.html create mode 100644 pr/672/docs/noq_proto/packet/struct.PartialDecode.html create mode 100644 pr/672/docs/noq_proto/packet/struct.ProtectedInitialHeader.html create mode 100644 pr/672/docs/noq_proto/packet/trait.ConnectionIdParser.html create mode 100644 pr/672/docs/noq_proto/shared/enum.EcnCodepoint.html create mode 100644 pr/672/docs/noq_proto/shared/struct.ConnectionEvent.html create mode 100644 pr/672/docs/noq_proto/shared/struct.ConnectionId.html create mode 100644 pr/672/docs/noq_proto/shared/struct.EndpointEvent.html create mode 100644 pr/672/docs/noq_proto/sidebar-items.js create mode 100644 pr/672/docs/noq_proto/struct.AcceptError.html create mode 100644 pr/672/docs/noq_proto/struct.AckFrequencyConfig.html create mode 100644 pr/672/docs/noq_proto/struct.ApplicationClose.html create mode 100644 pr/672/docs/noq_proto/struct.BloomTokenLog.html create mode 100644 pr/672/docs/noq_proto/struct.Chunk.html create mode 100644 pr/672/docs/noq_proto/struct.Chunks.html create mode 100644 pr/672/docs/noq_proto/struct.ClientConfig.html create mode 100644 pr/672/docs/noq_proto/struct.ClosedPath.html create mode 100644 pr/672/docs/noq_proto/struct.ClosedStream.html create mode 100644 pr/672/docs/noq_proto/struct.Connection.html create mode 100644 pr/672/docs/noq_proto/struct.ConnectionClose.html create mode 100644 pr/672/docs/noq_proto/struct.ConnectionEvent.html create mode 100644 pr/672/docs/noq_proto/struct.ConnectionHandle.html create mode 100644 pr/672/docs/noq_proto/struct.ConnectionId.html create mode 100644 pr/672/docs/noq_proto/struct.ConnectionStats.html create mode 100644 pr/672/docs/noq_proto/struct.Datagram.html create mode 100644 pr/672/docs/noq_proto/struct.DatagramInfo.html create mode 100644 pr/672/docs/noq_proto/struct.Datagrams.html create mode 100644 pr/672/docs/noq_proto/struct.DecryptedInitial.html create mode 100644 pr/672/docs/noq_proto/struct.Endpoint.html create mode 100644 pr/672/docs/noq_proto/struct.EndpointConfig.html create mode 100644 pr/672/docs/noq_proto/struct.EndpointEvent.html create mode 100644 pr/672/docs/noq_proto/struct.FixedLengthConnectionIdParser.html create mode 100644 pr/672/docs/noq_proto/struct.FourTuple.html create mode 100644 pr/672/docs/noq_proto/struct.FrameStats.html create mode 100644 pr/672/docs/noq_proto/struct.HashedConnectionIdGenerator.html create mode 100644 pr/672/docs/noq_proto/struct.IdleTimeout.html create mode 100644 pr/672/docs/noq_proto/struct.Incoming.html create mode 100644 pr/672/docs/noq_proto/struct.IncomingAlpns.html create mode 100644 pr/672/docs/noq_proto/struct.InvalidCid.html create mode 100644 pr/672/docs/noq_proto/struct.InvalidFrameId.html create mode 100644 pr/672/docs/noq_proto/struct.MtuDiscoveryConfig.html create mode 100644 pr/672/docs/noq_proto/struct.MultipathNotNegotiated.html create mode 100644 pr/672/docs/noq_proto/struct.NoneTokenLog.html create mode 100644 pr/672/docs/noq_proto/struct.NoneTokenStore.html create mode 100644 pr/672/docs/noq_proto/struct.PartialDecode.html create mode 100644 pr/672/docs/noq_proto/struct.PathId.html create mode 100644 pr/672/docs/noq_proto/struct.PathStats.html create mode 100644 pr/672/docs/noq_proto/struct.ProtectedInitialHeader.html create mode 100644 pr/672/docs/noq_proto/struct.QlogConfig.html create mode 100644 pr/672/docs/noq_proto/struct.QlogFileFactory.html create mode 100644 pr/672/docs/noq_proto/struct.RandomConnectionIdGenerator.html create mode 100644 pr/672/docs/noq_proto/struct.RecvStream.html create mode 100644 pr/672/docs/noq_proto/struct.RetryError.html create mode 100644 pr/672/docs/noq_proto/struct.RttEstimator.html create mode 100644 pr/672/docs/noq_proto/struct.SendStream.html create mode 100644 pr/672/docs/noq_proto/struct.ServerConfig.html create mode 100644 pr/672/docs/noq_proto/struct.ShouldTransmit.html create mode 100644 pr/672/docs/noq_proto/struct.StdSystemTime.html create mode 100644 pr/672/docs/noq_proto/struct.StreamId.html create mode 100644 pr/672/docs/noq_proto/struct.StreamInfo.html create mode 100644 pr/672/docs/noq_proto/struct.Streams.html create mode 100644 pr/672/docs/noq_proto/struct.TokenMemoryCache.html create mode 100644 pr/672/docs/noq_proto/struct.TokenReuseError.html create mode 100644 pr/672/docs/noq_proto/struct.Transmit.html create mode 100644 pr/672/docs/noq_proto/struct.TransportConfig.html create mode 100644 pr/672/docs/noq_proto/struct.TransportError.html create mode 100644 pr/672/docs/noq_proto/struct.TransportErrorCode.html create mode 100644 pr/672/docs/noq_proto/struct.UdpStats.html create mode 100644 pr/672/docs/noq_proto/struct.ValidationTokenConfig.html create mode 100644 pr/672/docs/noq_proto/struct.VarInt.html create mode 100644 pr/672/docs/noq_proto/struct.VarIntBoundsExceeded.html create mode 100644 pr/672/docs/noq_proto/token/struct.NoneTokenLog.html create mode 100644 pr/672/docs/noq_proto/token/struct.NoneTokenStore.html create mode 100644 pr/672/docs/noq_proto/token/struct.TokenReuseError.html create mode 100644 pr/672/docs/noq_proto/token/trait.TokenLog.html create mode 100644 pr/672/docs/noq_proto/token/trait.TokenStore.html create mode 100644 pr/672/docs/noq_proto/token_memory_cache/struct.TokenMemoryCache.html create mode 100644 pr/672/docs/noq_proto/trait.ConnectionIdGenerator.html create mode 100644 pr/672/docs/noq_proto/trait.ConnectionIdParser.html create mode 100644 pr/672/docs/noq_proto/trait.NetworkChangeHint.html create mode 100644 pr/672/docs/noq_proto/trait.QlogFactory.html create mode 100644 pr/672/docs/noq_proto/trait.TimeSource.html create mode 100644 pr/672/docs/noq_proto/trait.TokenLog.html create mode 100644 pr/672/docs/noq_proto/trait.TokenStore.html create mode 100644 pr/672/docs/noq_proto/transport_error/struct.Code.html create mode 100644 pr/672/docs/noq_proto/transport_error/struct.Error.html create mode 100644 pr/672/docs/noq_proto/transport_parameters/enum.Error.html create mode 100644 pr/672/docs/noq_proto/transport_parameters/index.html create mode 100644 pr/672/docs/noq_proto/transport_parameters/sidebar-items.js create mode 100644 pr/672/docs/noq_proto/transport_parameters/struct.TransportParameters.html create mode 100644 pr/672/docs/noq_proto/varint/struct.VarInt.html create mode 100644 pr/672/docs/noq_proto/varint/struct.VarIntBoundsExceeded.html create mode 100644 pr/672/docs/noq_udp/all.html create mode 100644 pr/672/docs/noq_udp/constant.BATCH_SIZE.html create mode 100644 pr/672/docs/noq_udp/enum.EcnCodepoint.html create mode 100644 pr/672/docs/noq_udp/imp/struct.UdpSocketState.html create mode 100644 pr/672/docs/noq_udp/index.html create mode 100644 pr/672/docs/noq_udp/sidebar-items.js create mode 100644 pr/672/docs/noq_udp/struct.RecvMeta.html create mode 100644 pr/672/docs/noq_udp/struct.Transmit.html create mode 100644 pr/672/docs/noq_udp/struct.UdpSockRef.html create mode 100644 pr/672/docs/noq_udp/struct.UdpSocketState.html create mode 100644 pr/672/docs/perf/all.html create mode 100644 pr/672/docs/perf/client/fn.run.html create mode 100644 pr/672/docs/perf/client/index.html create mode 100644 pr/672/docs/perf/client/sidebar-items.js create mode 100644 pr/672/docs/perf/client/struct.Opt.html create mode 100644 pr/672/docs/perf/enum.CongestionAlgorithm.html create mode 100644 pr/672/docs/perf/fn.parse_byte_size.html create mode 100644 pr/672/docs/perf/index.html create mode 100644 pr/672/docs/perf/noprotection/index.html create mode 100644 pr/672/docs/perf/noprotection/sidebar-items.js create mode 100644 pr/672/docs/perf/noprotection/struct.NoProtectionClientConfig.html create mode 100644 pr/672/docs/perf/noprotection/struct.NoProtectionServerConfig.html create mode 100644 pr/672/docs/perf/server/fn.run.html create mode 100644 pr/672/docs/perf/server/index.html create mode 100644 pr/672/docs/perf/server/sidebar-items.js create mode 100644 pr/672/docs/perf/server/struct.Opt.html create mode 100644 pr/672/docs/perf/sidebar-items.js create mode 100644 pr/672/docs/perf/static.PERF_CIPHER_SUITES.html create mode 100644 pr/672/docs/perf/stats/index.html create mode 100644 pr/672/docs/perf/stats/sidebar-items.js create mode 100644 pr/672/docs/perf/stats/struct.OpenStreamStats.html create mode 100644 pr/672/docs/perf/stats/struct.Stats.html create mode 100644 pr/672/docs/perf/stats/struct.StreamStats.html create mode 100644 pr/672/docs/perf/struct.CommonOpt.html create mode 100644 pr/672/docs/search.index/000ba6d7fd92.js create mode 100644 pr/672/docs/search.index/00ee768feb28.js create mode 100644 pr/672/docs/search.index/0198ee31ee3b.js create mode 100644 pr/672/docs/search.index/023324239cca.js create mode 100644 pr/672/docs/search.index/02e07382c03b.js create mode 100644 pr/672/docs/search.index/035dbd7fa572.js create mode 100644 pr/672/docs/search.index/03a9c483da4d.js create mode 100644 pr/672/docs/search.index/0425426ec9ab.js create mode 100644 pr/672/docs/search.index/05aae8a6fc63.js create mode 100644 pr/672/docs/search.index/05b8ba3e24ea.js create mode 100644 pr/672/docs/search.index/05ba80c533eb.js create mode 100644 pr/672/docs/search.index/0613632b906a.js create mode 100644 pr/672/docs/search.index/06c693dd9ca3.js create mode 100644 pr/672/docs/search.index/06f31413a772.js create mode 100644 pr/672/docs/search.index/06fa28d2800a.js create mode 100644 pr/672/docs/search.index/072d10679fb0.js create mode 100644 pr/672/docs/search.index/0867d8c9f05d.js create mode 100644 pr/672/docs/search.index/09a203b4464b.js create mode 100644 pr/672/docs/search.index/0a2e52a6ccac.js create mode 100644 pr/672/docs/search.index/0a99e2bd71de.js create mode 100644 pr/672/docs/search.index/0b73e38f9a07.js create mode 100644 pr/672/docs/search.index/0d39a6ae8014.js create mode 100644 pr/672/docs/search.index/0d3f98434995.js create mode 100644 pr/672/docs/search.index/0d9568b14335.js create mode 100644 pr/672/docs/search.index/0e6582e80826.js create mode 100644 pr/672/docs/search.index/1089838db3bf.js create mode 100644 pr/672/docs/search.index/1105a51fe78c.js create mode 100644 pr/672/docs/search.index/140824e73115.js create mode 100644 pr/672/docs/search.index/148ca916a394.js create mode 100644 pr/672/docs/search.index/1573b4768191.js create mode 100644 pr/672/docs/search.index/161c195e5cd2.js create mode 100644 pr/672/docs/search.index/16a23e4d7f90.js create mode 100644 pr/672/docs/search.index/1727786f31fa.js create mode 100644 pr/672/docs/search.index/1736d97117b1.js create mode 100644 pr/672/docs/search.index/175082f63529.js create mode 100644 pr/672/docs/search.index/18d8732f90bc.js create mode 100644 pr/672/docs/search.index/191d6e7f041f.js create mode 100644 pr/672/docs/search.index/195d6fe9d0c7.js create mode 100644 pr/672/docs/search.index/19fb50c323e0.js create mode 100644 pr/672/docs/search.index/1aeec3eb1f17.js create mode 100644 pr/672/docs/search.index/1b0a08e9b744.js create mode 100644 pr/672/docs/search.index/1b5ac0a0d3eb.js create mode 100644 pr/672/docs/search.index/1c80fc1385ea.js create mode 100644 pr/672/docs/search.index/1d72d41a89e5.js create mode 100644 pr/672/docs/search.index/1d79643796e7.js create mode 100644 pr/672/docs/search.index/1dbb8572c4de.js create mode 100644 pr/672/docs/search.index/1ed8c6ed4cd9.js create mode 100644 pr/672/docs/search.index/1fae25a0d497.js create mode 100644 pr/672/docs/search.index/1fee3e0237a8.js create mode 100644 pr/672/docs/search.index/206a22bb12ba.js create mode 100644 pr/672/docs/search.index/2084af11f5a7.js create mode 100644 pr/672/docs/search.index/20b60bb6dc02.js create mode 100644 pr/672/docs/search.index/228447d4f222.js create mode 100644 pr/672/docs/search.index/22b744f970a4.js create mode 100644 pr/672/docs/search.index/22eb75e289be.js create mode 100644 pr/672/docs/search.index/233f5ec5c226.js create mode 100644 pr/672/docs/search.index/23a40eca4762.js create mode 100644 pr/672/docs/search.index/240f796d6ccd.js create mode 100644 pr/672/docs/search.index/2424bb274ca9.js create mode 100644 pr/672/docs/search.index/2618e545b18c.js create mode 100644 pr/672/docs/search.index/289f0a680251.js create mode 100644 pr/672/docs/search.index/28cab8635ca9.js create mode 100644 pr/672/docs/search.index/29212c997b44.js create mode 100644 pr/672/docs/search.index/2923606a2790.js create mode 100644 pr/672/docs/search.index/29667f8ae9a2.js create mode 100644 pr/672/docs/search.index/297772620241.js create mode 100644 pr/672/docs/search.index/297c2deef0ac.js create mode 100644 pr/672/docs/search.index/298fec6e5a30.js create mode 100644 pr/672/docs/search.index/29a80323f87b.js create mode 100644 pr/672/docs/search.index/29d2100cf07e.js create mode 100644 pr/672/docs/search.index/29f7585ca898.js create mode 100644 pr/672/docs/search.index/2b576bc01ad5.js create mode 100644 pr/672/docs/search.index/2b9e99969beb.js create mode 100644 pr/672/docs/search.index/2ce5f6d932f6.js create mode 100644 pr/672/docs/search.index/2d9a6dddfba0.js create mode 100644 pr/672/docs/search.index/2da0a77b4fbc.js create mode 100644 pr/672/docs/search.index/2dd3fa22e8d4.js create mode 100644 pr/672/docs/search.index/2eede6b213b7.js create mode 100644 pr/672/docs/search.index/3067b7ed1d8a.js create mode 100644 pr/672/docs/search.index/30dacb34bdb2.js create mode 100644 pr/672/docs/search.index/31541a51e8db.js create mode 100644 pr/672/docs/search.index/31eb7fecbe0b.js create mode 100644 pr/672/docs/search.index/32222f9e344c.js create mode 100644 pr/672/docs/search.index/336c95938b04.js create mode 100644 pr/672/docs/search.index/34d17abef16f.js create mode 100644 pr/672/docs/search.index/3545cf7f836f.js create mode 100644 pr/672/docs/search.index/357d49476acc.js create mode 100644 pr/672/docs/search.index/358b5f8021f2.js create mode 100644 pr/672/docs/search.index/358dcb9f0de5.js create mode 100644 pr/672/docs/search.index/35eb33e7a6e8.js create mode 100644 pr/672/docs/search.index/36ce02943eb1.js create mode 100644 pr/672/docs/search.index/36ce51aa446c.js create mode 100644 pr/672/docs/search.index/378b1aab08c9.js create mode 100644 pr/672/docs/search.index/37cb471d121c.js create mode 100644 pr/672/docs/search.index/37eaa116de94.js create mode 100644 pr/672/docs/search.index/380eb6ff2905.js create mode 100644 pr/672/docs/search.index/3840c747afc6.js create mode 100644 pr/672/docs/search.index/38b51321ef64.js create mode 100644 pr/672/docs/search.index/3a1ad02b1851.js create mode 100644 pr/672/docs/search.index/3a8f25986add.js create mode 100644 pr/672/docs/search.index/3b45dd23db88.js create mode 100644 pr/672/docs/search.index/3be8e84e40bd.js create mode 100644 pr/672/docs/search.index/3c4d6a050636.js create mode 100644 pr/672/docs/search.index/3fae53857d69.js create mode 100644 pr/672/docs/search.index/40fbc4e3aee2.js create mode 100644 pr/672/docs/search.index/4129fde217fe.js create mode 100644 pr/672/docs/search.index/4143e8d28ee3.js create mode 100644 pr/672/docs/search.index/4177e23c85bd.js create mode 100644 pr/672/docs/search.index/4239d228a482.js create mode 100644 pr/672/docs/search.index/426c45f94d9c.js create mode 100644 pr/672/docs/search.index/4372412479ef.js create mode 100644 pr/672/docs/search.index/43819ce322e0.js create mode 100644 pr/672/docs/search.index/4471911fea22.js create mode 100644 pr/672/docs/search.index/45ccc7ad5291.js create mode 100644 pr/672/docs/search.index/45d9dd3f98fe.js create mode 100644 pr/672/docs/search.index/46364023d7e3.js create mode 100644 pr/672/docs/search.index/482faea55872.js create mode 100644 pr/672/docs/search.index/487bb0c57643.js create mode 100644 pr/672/docs/search.index/48dd45a6efaf.js create mode 100644 pr/672/docs/search.index/48ff0a8ecdcf.js create mode 100644 pr/672/docs/search.index/4920da678e00.js create mode 100644 pr/672/docs/search.index/4986f6b50c9a.js create mode 100644 pr/672/docs/search.index/4a3a9fa5310b.js create mode 100644 pr/672/docs/search.index/4ba0a8bbc176.js create mode 100644 pr/672/docs/search.index/4c3658ed6e5f.js create mode 100644 pr/672/docs/search.index/4c4e5cf71a95.js create mode 100644 pr/672/docs/search.index/4c6ccd5ef6f9.js create mode 100644 pr/672/docs/search.index/4ce1093f624a.js create mode 100644 pr/672/docs/search.index/4d1a3a5d7a12.js create mode 100644 pr/672/docs/search.index/4db7cfff3d56.js create mode 100644 pr/672/docs/search.index/4dd22e56451b.js create mode 100644 pr/672/docs/search.index/4ede297cd13d.js create mode 100644 pr/672/docs/search.index/4ef056118e58.js create mode 100644 pr/672/docs/search.index/4f020f79d021.js create mode 100644 pr/672/docs/search.index/4f7d52082fe6.js create mode 100644 pr/672/docs/search.index/504ca52d3ae9.js create mode 100644 pr/672/docs/search.index/5106fdea4a6c.js create mode 100644 pr/672/docs/search.index/5168b1c9fd9a.js create mode 100644 pr/672/docs/search.index/51a383abcfdb.js create mode 100644 pr/672/docs/search.index/51d0879dcf6d.js create mode 100644 pr/672/docs/search.index/5219aed485bd.js create mode 100644 pr/672/docs/search.index/52ddf1446ab6.js create mode 100644 pr/672/docs/search.index/536af0dd1eb6.js create mode 100644 pr/672/docs/search.index/53a370bed26e.js create mode 100644 pr/672/docs/search.index/53a9362a0d4b.js create mode 100644 pr/672/docs/search.index/53ffd91bdfe9.js create mode 100644 pr/672/docs/search.index/5450ecaeec3d.js create mode 100644 pr/672/docs/search.index/5525682f4073.js create mode 100644 pr/672/docs/search.index/5540c5ab7cb3.js create mode 100644 pr/672/docs/search.index/5560d878572f.js create mode 100644 pr/672/docs/search.index/560bd128efec.js create mode 100644 pr/672/docs/search.index/5687f616548f.js create mode 100644 pr/672/docs/search.index/57030fb90cf6.js create mode 100644 pr/672/docs/search.index/5718d5014774.js create mode 100644 pr/672/docs/search.index/5853623e6846.js create mode 100644 pr/672/docs/search.index/58beb8724380.js create mode 100644 pr/672/docs/search.index/58cbe3793786.js create mode 100644 pr/672/docs/search.index/59718cd9c46f.js create mode 100644 pr/672/docs/search.index/59d3892a5c9c.js create mode 100644 pr/672/docs/search.index/5b3910277d15.js create mode 100644 pr/672/docs/search.index/5bbc672548ec.js create mode 100644 pr/672/docs/search.index/5c06b39c26ac.js create mode 100644 pr/672/docs/search.index/5c9fb1c311dd.js create mode 100644 pr/672/docs/search.index/5e777cfd64d1.js create mode 100644 pr/672/docs/search.index/5ed95ff36a5f.js create mode 100644 pr/672/docs/search.index/5fb5abc1fd26.js create mode 100644 pr/672/docs/search.index/6026531291d3.js create mode 100644 pr/672/docs/search.index/60287e11cc71.js create mode 100644 pr/672/docs/search.index/608a5d02477c.js create mode 100644 pr/672/docs/search.index/6094984aadd1.js create mode 100644 pr/672/docs/search.index/60e7eb5d335c.js create mode 100644 pr/672/docs/search.index/60e98a5178e4.js create mode 100644 pr/672/docs/search.index/60ea120b017f.js create mode 100644 pr/672/docs/search.index/60eabdb6a059.js create mode 100644 pr/672/docs/search.index/60f27d69272c.js create mode 100644 pr/672/docs/search.index/62158b71bbd3.js create mode 100644 pr/672/docs/search.index/624cc81e0578.js create mode 100644 pr/672/docs/search.index/62a274538f89.js create mode 100644 pr/672/docs/search.index/62ea63f61793.js create mode 100644 pr/672/docs/search.index/63002ba13ba6.js create mode 100644 pr/672/docs/search.index/644655b6a37b.js create mode 100644 pr/672/docs/search.index/647990943ac3.js create mode 100644 pr/672/docs/search.index/650b84df086a.js create mode 100644 pr/672/docs/search.index/651d241c3c65.js create mode 100644 pr/672/docs/search.index/651ea497c196.js create mode 100644 pr/672/docs/search.index/65427ca2c609.js create mode 100644 pr/672/docs/search.index/66c7e9777791.js create mode 100644 pr/672/docs/search.index/66d031770208.js create mode 100644 pr/672/docs/search.index/686d5bf1a575.js create mode 100644 pr/672/docs/search.index/6995c7586e21.js create mode 100644 pr/672/docs/search.index/6ae15219502a.js create mode 100644 pr/672/docs/search.index/6b95550a6642.js create mode 100644 pr/672/docs/search.index/6be7775146ac.js create mode 100644 pr/672/docs/search.index/6c223b5d689a.js create mode 100644 pr/672/docs/search.index/6c39ad1a7a87.js create mode 100644 pr/672/docs/search.index/6ca18fe2a088.js create mode 100644 pr/672/docs/search.index/6cec104bb6e3.js create mode 100644 pr/672/docs/search.index/6d172713b950.js create mode 100644 pr/672/docs/search.index/6d22a2adcab1.js create mode 100644 pr/672/docs/search.index/6dc7b167965d.js create mode 100644 pr/672/docs/search.index/6e88e59bfb82.js create mode 100644 pr/672/docs/search.index/6eb9e85c5b88.js create mode 100644 pr/672/docs/search.index/6f8ed6ef61e6.js create mode 100644 pr/672/docs/search.index/70531bfc34d4.js create mode 100644 pr/672/docs/search.index/7063c86102c3.js create mode 100644 pr/672/docs/search.index/7093873e356a.js create mode 100644 pr/672/docs/search.index/712b58a1cc15.js create mode 100644 pr/672/docs/search.index/715b0a6426a1.js create mode 100644 pr/672/docs/search.index/719b4a1ba67f.js create mode 100644 pr/672/docs/search.index/73a9ad245c25.js create mode 100644 pr/672/docs/search.index/74503773b7e7.js create mode 100644 pr/672/docs/search.index/747b64cfeb14.js create mode 100644 pr/672/docs/search.index/758d161430bc.js create mode 100644 pr/672/docs/search.index/75c77b7919db.js create mode 100644 pr/672/docs/search.index/763c0d5d4643.js create mode 100644 pr/672/docs/search.index/769cab48f482.js create mode 100644 pr/672/docs/search.index/76bc946ad95f.js create mode 100644 pr/672/docs/search.index/77dc9e44aee7.js create mode 100644 pr/672/docs/search.index/780b49f04404.js create mode 100644 pr/672/docs/search.index/7811abcc9a5f.js create mode 100644 pr/672/docs/search.index/7867cdb9af20.js create mode 100644 pr/672/docs/search.index/7b4e167c9f1f.js create mode 100644 pr/672/docs/search.index/7ca9a5d9b5a7.js create mode 100644 pr/672/docs/search.index/7ce496ab7c6d.js create mode 100644 pr/672/docs/search.index/7d1520a0ac6f.js create mode 100644 pr/672/docs/search.index/7f66cf4dc275.js create mode 100644 pr/672/docs/search.index/alias/a762150c532c.js create mode 100644 pr/672/docs/search.index/crateNames/2450c9cb4357.js create mode 100644 pr/672/docs/search.index/desc/5f4b62053dd2.js create mode 100644 pr/672/docs/search.index/entry/843e82bff23e.js create mode 100644 pr/672/docs/search.index/function/8915390c6a23.js create mode 100644 pr/672/docs/search.index/generic_inverted_index/11aba052bb82.js create mode 100644 pr/672/docs/search.index/name/7f055d9d7409.js create mode 100644 pr/672/docs/search.index/normalizedName/c413c0680ead.js create mode 100644 pr/672/docs/search.index/path/813227ed5be5.js create mode 100644 pr/672/docs/search.index/root.js create mode 100644 pr/672/docs/search.index/type/3625b511c301.js create mode 100644 pr/672/docs/set_up_connection/all.html create mode 100644 pr/672/docs/set_up_connection/constant.CLIENT_ADDR.html create mode 100644 pr/672/docs/set_up_connection/constant.LOCALHOST_V4.html create mode 100644 pr/672/docs/set_up_connection/constant.SERVER_ADDR.html create mode 100644 pr/672/docs/set_up_connection/constant.SERVER_NAME.html create mode 100644 pr/672/docs/set_up_connection/fn.client.html create mode 100644 pr/672/docs/set_up_connection/fn.main.html create mode 100644 pr/672/docs/set_up_connection/fn.server.html create mode 100644 pr/672/docs/set_up_connection/index.html create mode 100644 pr/672/docs/set_up_connection/sidebar-items.js create mode 100644 pr/672/docs/settings.html create mode 100644 pr/672/docs/src-files.js create mode 100644 pr/672/docs/src/bench/lib.rs.html create mode 100644 pr/672/docs/src/bench/stats.rs.html create mode 100644 pr/672/docs/src/bulk/bulk.rs.html create mode 100644 pr/672/docs/src/certificate/certificate.rs.html create mode 100644 pr/672/docs/src/data_transfer/data-transfer.rs.html create mode 100644 pr/672/docs/src/noq/connection.rs.html create mode 100644 pr/672/docs/src/noq/endpoint.rs.html create mode 100644 pr/672/docs/src/noq/event_stream.rs.html create mode 100644 pr/672/docs/src/noq/incoming.rs.html create mode 100644 pr/672/docs/src/noq/lib.rs.html create mode 100644 pr/672/docs/src/noq/mutex.rs.html create mode 100644 pr/672/docs/src/noq/path.rs.html create mode 100644 pr/672/docs/src/noq/recv_stream.rs.html create mode 100644 pr/672/docs/src/noq/runtime/mod.rs.html create mode 100644 pr/672/docs/src/noq/runtime/smol.rs.html create mode 100644 pr/672/docs/src/noq/runtime/tokio.rs.html create mode 100644 pr/672/docs/src/noq/send_stream.rs.html create mode 100644 pr/672/docs/src/noq/work_limiter.rs.html create mode 100644 pr/672/docs/src/noq_perf/perf.rs.html create mode 100644 pr/672/docs/src/noq_proto/address_discovery.rs.html create mode 100644 pr/672/docs/src/noq_proto/bloom_token_log.rs.html create mode 100644 pr/672/docs/src/noq_proto/cid_generator.rs.html create mode 100644 pr/672/docs/src/noq_proto/cid_queue.rs.html create mode 100644 pr/672/docs/src/noq_proto/coding.rs.html create mode 100644 pr/672/docs/src/noq_proto/config/mod.rs.html create mode 100644 pr/672/docs/src/noq_proto/config/qlog.rs.html create mode 100644 pr/672/docs/src/noq_proto/config/transport.rs.html create mode 100644 pr/672/docs/src/noq_proto/congestion.rs.html create mode 100644 pr/672/docs/src/noq_proto/congestion/bbr3/max_filter.rs.html create mode 100644 pr/672/docs/src/noq_proto/congestion/bbr3/mod.rs.html create mode 100644 pr/672/docs/src/noq_proto/congestion/cubic.rs.html create mode 100644 pr/672/docs/src/noq_proto/congestion/new_reno.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/ack_frequency.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/assembler.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/cid_state.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/datagrams.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/mod.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/mtud.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/pacing.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/packet_builder.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/packet_crypto.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/paths.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/qlog.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/send_buffer.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/spaces.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/state.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/stats.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/streams/mod.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/streams/recv.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/streams/send.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/streams/state.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/timer.rs.html create mode 100644 pr/672/docs/src/noq_proto/connection/transmit_buf.rs.html create mode 100644 pr/672/docs/src/noq_proto/constant_time.rs.html create mode 100644 pr/672/docs/src/noq_proto/crypto.rs.html create mode 100644 pr/672/docs/src/noq_proto/crypto/ring_like.rs.html create mode 100644 pr/672/docs/src/noq_proto/crypto/rustls.rs.html create mode 100644 pr/672/docs/src/noq_proto/endpoint.rs.html create mode 100644 pr/672/docs/src/noq_proto/frame.rs.html create mode 100644 pr/672/docs/src/noq_proto/lib.rs.html create mode 100644 pr/672/docs/src/noq_proto/n0_nat_traversal.rs.html create mode 100644 pr/672/docs/src/noq_proto/packet.rs.html create mode 100644 pr/672/docs/src/noq_proto/range_set/array_range_set.rs.html create mode 100644 pr/672/docs/src/noq_proto/range_set/mod.rs.html create mode 100644 pr/672/docs/src/noq_proto/shared.rs.html create mode 100644 pr/672/docs/src/noq_proto/token.rs.html create mode 100644 pr/672/docs/src/noq_proto/token_memory_cache.rs.html create mode 100644 pr/672/docs/src/noq_proto/transport_error.rs.html create mode 100644 pr/672/docs/src/noq_proto/transport_parameters.rs.html create mode 100644 pr/672/docs/src/noq_proto/varint.rs.html create mode 100644 pr/672/docs/src/noq_udp/cmsg/mod.rs.html create mode 100644 pr/672/docs/src/noq_udp/cmsg/unix.rs.html create mode 100644 pr/672/docs/src/noq_udp/lib.rs.html create mode 100644 pr/672/docs/src/noq_udp/unix.rs.html create mode 100644 pr/672/docs/src/perf/client.rs.html create mode 100644 pr/672/docs/src/perf/lib.rs.html create mode 100644 pr/672/docs/src/perf/noprotection.rs.html create mode 100644 pr/672/docs/src/perf/server.rs.html create mode 100644 pr/672/docs/src/perf/stats.rs.html create mode 100644 pr/672/docs/src/set_up_connection/set-up-connection.rs.html create mode 100644 pr/672/docs/static.files/COPYRIGHT-7fb11f4e.txt create mode 100644 pr/672/docs/static.files/FiraMono-Medium-86f75c8c.woff2 create mode 100644 pr/672/docs/static.files/FiraMono-Regular-87c26294.woff2 create mode 100644 pr/672/docs/static.files/FiraSans-Italic-81dc35de.woff2 create mode 100644 pr/672/docs/static.files/FiraSans-LICENSE-05ab6dbd.txt create mode 100644 pr/672/docs/static.files/FiraSans-Medium-e1aa3f0a.woff2 create mode 100644 pr/672/docs/static.files/FiraSans-MediumItalic-ccf7e434.woff2 create mode 100644 pr/672/docs/static.files/FiraSans-Regular-0fe48ade.woff2 create mode 100644 pr/672/docs/static.files/LICENSE-APACHE-a60eea81.txt create mode 100644 pr/672/docs/static.files/LICENSE-MIT-23f18e03.txt create mode 100644 pr/672/docs/static.files/NanumBarunGothic-13b3dcba.ttf.woff2 create mode 100644 pr/672/docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt create mode 100644 pr/672/docs/static.files/SourceCodePro-It-fc8b9304.ttf.woff2 create mode 100644 pr/672/docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt create mode 100644 pr/672/docs/static.files/SourceCodePro-Regular-8badfe75.ttf.woff2 create mode 100644 pr/672/docs/static.files/SourceCodePro-Semibold-aa29a496.ttf.woff2 create mode 100644 pr/672/docs/static.files/SourceSerif4-Bold-6d4fd4c0.ttf.woff2 create mode 100644 pr/672/docs/static.files/SourceSerif4-It-ca3b17ed.ttf.woff2 create mode 100644 pr/672/docs/static.files/SourceSerif4-LICENSE-a2cfd9d5.md create mode 100644 pr/672/docs/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 create mode 100644 pr/672/docs/static.files/SourceSerif4-Semibold-457a13ac.ttf.woff2 create mode 100644 pr/672/docs/static.files/favicon-044be391.svg create mode 100644 pr/672/docs/static.files/favicon-32x32-eab170b8.png create mode 100644 pr/672/docs/static.files/main-ce535bd0.js create mode 100644 pr/672/docs/static.files/normalize-9960930a.css create mode 100644 pr/672/docs/static.files/noscript-263c88ec.css create mode 100644 pr/672/docs/static.files/rust-logo-9a9549ea.svg create mode 100644 pr/672/docs/static.files/rustdoc-ca0dd0c4.css create mode 100644 pr/672/docs/static.files/scrape-examples-2bbcccac.js create mode 100644 pr/672/docs/static.files/search-8d3311b9.js create mode 100644 pr/672/docs/static.files/settings-c38705f0.js create mode 100644 pr/672/docs/static.files/src-script-813739b1.js create mode 100644 pr/672/docs/static.files/storage-e2aeef58.js create mode 100644 pr/672/docs/static.files/stringdex-828709d0.js create mode 100644 pr/672/docs/trait.impl/arbitrary/trait.Arbitrary.js create mode 100644 pr/672/docs/trait.impl/clap_builder/derive/trait.Args.js create mode 100644 pr/672/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js create mode 100644 pr/672/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js create mode 100644 pr/672/docs/trait.impl/clap_builder/derive/trait.Parser.js create mode 100644 pr/672/docs/trait.impl/clap_builder/derive/trait.Subcommand.js create mode 100644 pr/672/docs/trait.impl/clap_builder/derive/trait.ValueEnum.js create mode 100644 pr/672/docs/trait.impl/core/clone/trait.Clone.js create mode 100644 pr/672/docs/trait.impl/core/cmp/trait.Eq.js create mode 100644 pr/672/docs/trait.impl/core/cmp/trait.Ord.js create mode 100644 pr/672/docs/trait.impl/core/cmp/trait.PartialEq.js create mode 100644 pr/672/docs/trait.impl/core/cmp/trait.PartialOrd.js create mode 100644 pr/672/docs/trait.impl/core/convert/trait.From.js create mode 100644 pr/672/docs/trait.impl/core/convert/trait.TryFrom.js create mode 100644 pr/672/docs/trait.impl/core/default/trait.Default.js create mode 100644 pr/672/docs/trait.impl/core/error/trait.Error.js create mode 100644 pr/672/docs/trait.impl/core/fmt/trait.Debug.js create mode 100644 pr/672/docs/trait.impl/core/fmt/trait.Display.js create mode 100644 pr/672/docs/trait.impl/core/future/future/trait.Future.js create mode 100644 pr/672/docs/trait.impl/core/future/into_future/trait.IntoFuture.js create mode 100644 pr/672/docs/trait.impl/core/hash/trait.Hash.js create mode 100644 pr/672/docs/trait.impl/core/iter/traits/iterator/trait.Iterator.js create mode 100644 pr/672/docs/trait.impl/core/marker/trait.Copy.js create mode 100644 pr/672/docs/trait.impl/core/marker/trait.Freeze.js create mode 100644 pr/672/docs/trait.impl/core/marker/trait.Send.js create mode 100644 pr/672/docs/trait.impl/core/marker/trait.StructuralPartialEq.js create mode 100644 pr/672/docs/trait.impl/core/marker/trait.Sync.js create mode 100644 pr/672/docs/trait.impl/core/marker/trait.Unpin.js create mode 100644 pr/672/docs/trait.impl/core/marker/trait.UnsafeUnpin.js create mode 100644 pr/672/docs/trait.impl/core/ops/arith/trait.Add.js create mode 100644 pr/672/docs/trait.impl/core/ops/arith/trait.AddAssign.js create mode 100644 pr/672/docs/trait.impl/core/ops/bit/trait.Not.js create mode 100644 pr/672/docs/trait.impl/core/ops/deref/trait.Deref.js create mode 100644 pr/672/docs/trait.impl/core/ops/deref/trait.DerefMut.js create mode 100644 pr/672/docs/trait.impl/core/ops/drop/trait.Drop.js create mode 100644 pr/672/docs/trait.impl/core/ops/index/trait.Index.js create mode 100644 pr/672/docs/trait.impl/core/ops/index/trait.IndexMut.js create mode 100644 pr/672/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js create mode 100644 pr/672/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js create mode 100644 pr/672/docs/trait.impl/core/str/traits/trait.FromStr.js create mode 100644 pr/672/docs/trait.impl/futures_core/stream/trait.Stream.js create mode 100644 pr/672/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js create mode 100644 pr/672/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js create mode 100644 pr/672/docs/trait.impl/noq/runtime/trait.AsyncTimer.js create mode 100644 pr/672/docs/trait.impl/noq/runtime/trait.Runtime.js create mode 100644 pr/672/docs/trait.impl/noq_proto/cid_generator/trait.ConnectionIdGenerator.js create mode 100644 pr/672/docs/trait.impl/noq_proto/coding/trait.Decodable.js create mode 100644 pr/672/docs/trait.impl/noq_proto/coding/trait.Encodable.js create mode 100644 pr/672/docs/trait.impl/noq_proto/config/qlog/trait.QlogFactory.js create mode 100644 pr/672/docs/trait.impl/noq_proto/config/trait.TimeSource.js create mode 100644 pr/672/docs/trait.impl/noq_proto/congestion/trait.Controller.js create mode 100644 pr/672/docs/trait.impl/noq_proto/congestion/trait.ControllerFactory.js create mode 100644 pr/672/docs/trait.impl/noq_proto/crypto/trait.ClientConfig.js create mode 100644 pr/672/docs/trait.impl/noq_proto/crypto/trait.HeaderKey.js create mode 100644 pr/672/docs/trait.impl/noq_proto/crypto/trait.HmacKey.js create mode 100644 pr/672/docs/trait.impl/noq_proto/crypto/trait.PacketKey.js create mode 100644 pr/672/docs/trait.impl/noq_proto/crypto/trait.ServerConfig.js create mode 100644 pr/672/docs/trait.impl/noq_proto/crypto/trait.Session.js create mode 100644 pr/672/docs/trait.impl/noq_proto/packet/trait.ConnectionIdParser.js create mode 100644 pr/672/docs/trait.impl/noq_proto/token/trait.TokenLog.js create mode 100644 pr/672/docs/trait.impl/noq_proto/token/trait.TokenStore.js create mode 100644 pr/672/docs/trait.impl/rustls/verify/trait.ServerCertVerifier.js create mode 100644 pr/672/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js create mode 100644 pr/672/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js create mode 100644 pr/672/docs/type.impl/core/result/enum.Result.js diff --git a/pr/672/docs/.lock b/pr/672/docs/.lock new file mode 100644 index 000000000..e69de29bb diff --git a/pr/672/docs/bench/all.html b/pr/672/docs/bench/all.html new file mode 100644 index 000000000..7dda8f608 --- /dev/null +++ b/pr/672/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/672/docs/bench/enum.CipherSuite.html b/pr/672/docs/bench/enum.CipherSuite.html new file mode 100644 index 000000000..6904e5d0d --- /dev/null +++ b/pr/672/docs/bench/enum.CipherSuite.html @@ -0,0 +1,34 @@ +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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/bench/enum.RuntimeType.html b/pr/672/docs/bench/enum.RuntimeType.html new file mode 100644 index 000000000..e56c4d369 --- /dev/null +++ b/pr/672/docs/bench/enum.RuntimeType.html @@ -0,0 +1,28 @@ +RuntimeType in bench - Rust

RuntimeType

Enum RuntimeType 

Source
pub enum RuntimeType {
+    Tokio,
+    TokioCurrentThread,
+}

Variants§

§

Tokio

§

TokioCurrentThread

Trait Implementations§

Source§

impl Clone for RuntimeType

Source§

fn clone(&self) -> RuntimeType

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 RuntimeType

Source§

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

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

impl FromStr for RuntimeType

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 Copy for RuntimeType

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/bench/fn.configure_tracing_subscriber.html b/pr/672/docs/bench/fn.configure_tracing_subscriber.html new file mode 100644 index 000000000..b07f8c28a --- /dev/null +++ b/pr/672/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/672/docs/bench/fn.connect_client.html b/pr/672/docs/bench/fn.connect_client.html new file mode 100644 index 000000000..1f4edbfc9 --- /dev/null +++ b/pr/672/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/672/docs/bench/fn.drain_stream.html b/pr/672/docs/bench/fn.drain_stream.html new file mode 100644 index 000000000..1156135bd --- /dev/null +++ b/pr/672/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/672/docs/bench/fn.rt.html b/pr/672/docs/bench/fn.rt.html new file mode 100644 index 000000000..297a48899 --- /dev/null +++ b/pr/672/docs/bench/fn.rt.html @@ -0,0 +1 @@ +rt in bench - Rust

rt

Function rt 

Source
pub fn rt(runtime_type: RuntimeType) -> Runtime
\ No newline at end of file diff --git a/pr/672/docs/bench/fn.send_data_on_stream.html b/pr/672/docs/bench/fn.send_data_on_stream.html new file mode 100644 index 000000000..dbdfc4d28 --- /dev/null +++ b/pr/672/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/672/docs/bench/fn.server_endpoint.html b/pr/672/docs/bench/fn.server_endpoint.html new file mode 100644 index 000000000..2b7bac5c1 --- /dev/null +++ b/pr/672/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/672/docs/bench/fn.transport_config.html b/pr/672/docs/bench/fn.transport_config.html new file mode 100644 index 000000000..3bbf46783 --- /dev/null +++ b/pr/672/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/672/docs/bench/index.html b/pr/672/docs/bench/index.html new file mode 100644 index 000000000..3b324a290 --- /dev/null +++ b/pr/672/docs/bench/index.html @@ -0,0 +1 @@ +bench - Rust

Crate bench

Crate bench 

Source

Modules§

stats

Structs§

Opt

Enums§

CipherSuite
RuntimeType

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/672/docs/bench/sidebar-items.js b/pr/672/docs/bench/sidebar-items.js new file mode 100644 index 000000000..a0652e632 --- /dev/null +++ b/pr/672/docs/bench/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["CipherSuite","RuntimeType"],"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/672/docs/bench/stats/fn.throughput_bps.html b/pr/672/docs/bench/stats/fn.throughput_bps.html new file mode 100644 index 000000000..8f9cac152 --- /dev/null +++ b/pr/672/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/672/docs/bench/stats/index.html b/pr/672/docs/bench/stats/index.html new file mode 100644 index 000000000..61b71b975 --- /dev/null +++ b/pr/672/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/672/docs/bench/stats/sidebar-items.js b/pr/672/docs/bench/stats/sidebar-items.js new file mode 100644 index 000000000..6272a13ae --- /dev/null +++ b/pr/672/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/672/docs/bench/stats/struct.Stats.html b/pr/672/docs/bench/stats/struct.Stats.html new file mode 100644 index 000000000..0ed84cae8 --- /dev/null +++ b/pr/672/docs/bench/stats/struct.Stats.html @@ -0,0 +1,28 @@ +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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/bench/stats/struct.StreamStats.html b/pr/672/docs/bench/stats/struct.StreamStats.html new file mode 100644 index 000000000..249ceaf43 --- /dev/null +++ b/pr/672/docs/bench/stats/struct.StreamStats.html @@ -0,0 +1,26 @@ +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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/bench/stats/struct.TransferResult.html b/pr/672/docs/bench/stats/struct.TransferResult.html new file mode 100644 index 000000000..5822c38e4 --- /dev/null +++ b/pr/672/docs/bench/stats/struct.TransferResult.html @@ -0,0 +1,27 @@ +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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/bench/struct.Opt.html b/pr/672/docs/bench/struct.Opt.html new file mode 100644 index 000000000..2922f75e6 --- /dev/null +++ b/pr/672/docs/bench/struct.Opt.html @@ -0,0 +1,69 @@ +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,
+    pub runtime_type: RuntimeType,
+}

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

+
§runtime_type: RuntimeType

The runtime type to use

+

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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/bulk/all.html b/pr/672/docs/bulk/all.html new file mode 100644 index 000000000..ec127a04e --- /dev/null +++ b/pr/672/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/672/docs/bulk/fn.client.html b/pr/672/docs/bulk/fn.client.html new file mode 100644 index 000000000..11e16fbf0 --- /dev/null +++ b/pr/672/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/672/docs/bulk/fn.handle_client_stream.html b/pr/672/docs/bulk/fn.handle_client_stream.html new file mode 100644 index 000000000..057b0d3ce --- /dev/null +++ b/pr/672/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/672/docs/bulk/fn.main.html b/pr/672/docs/bulk/fn.main.html new file mode 100644 index 000000000..92a2fc225 --- /dev/null +++ b/pr/672/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/672/docs/bulk/fn.server.html b/pr/672/docs/bulk/fn.server.html new file mode 100644 index 000000000..c1722630f --- /dev/null +++ b/pr/672/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/672/docs/bulk/index.html b/pr/672/docs/bulk/index.html new file mode 100644 index 000000000..717e893f9 --- /dev/null +++ b/pr/672/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/672/docs/bulk/sidebar-items.js b/pr/672/docs/bulk/sidebar-items.js new file mode 100644 index 000000000..4b4dceb17 --- /dev/null +++ b/pr/672/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/672/docs/bulk/struct.ClientStats.html b/pr/672/docs/bulk/struct.ClientStats.html new file mode 100644 index 000000000..ffb57c000 --- /dev/null +++ b/pr/672/docs/bulk/struct.ClientStats.html @@ -0,0 +1,26 @@ +ClientStats in bulk - Rust

ClientStats

Struct ClientStats 

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

Fields§

§upload_stats: Stats§download_stats: Stats

Implementations§

Source§

impl ClientStats

Source

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

Trait Implementations§

Source§

impl Default for ClientStats

Source§

fn default() -> ClientStats

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/certificate/all.html b/pr/672/docs/certificate/all.html new file mode 100644 index 000000000..d6e1b79d5 --- /dev/null +++ b/pr/672/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/672/docs/certificate/fn.configure_client.html b/pr/672/docs/certificate/fn.configure_client.html new file mode 100644 index 000000000..e0c8b6fdb --- /dev/null +++ b/pr/672/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/672/docs/certificate/fn.generate_self_signed_cert.html b/pr/672/docs/certificate/fn.generate_self_signed_cert.html new file mode 100644 index 000000000..e91269156 --- /dev/null +++ b/pr/672/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/672/docs/certificate/fn.main.html b/pr/672/docs/certificate/fn.main.html new file mode 100644 index 000000000..461b95b57 --- /dev/null +++ b/pr/672/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/672/docs/certificate/fn.read_certs_from_file.html b/pr/672/docs/certificate/fn.read_certs_from_file.html new file mode 100644 index 000000000..bd63cf355 --- /dev/null +++ b/pr/672/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/672/docs/certificate/index.html b/pr/672/docs/certificate/index.html new file mode 100644 index 000000000..eb429342b --- /dev/null +++ b/pr/672/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/672/docs/certificate/sidebar-items.js b/pr/672/docs/certificate/sidebar-items.js new file mode 100644 index 000000000..0f8f69b2b --- /dev/null +++ b/pr/672/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/672/docs/certificate/struct.SkipServerVerification.html b/pr/672/docs/certificate/struct.SkipServerVerification.html new file mode 100644 index 000000000..11829118c --- /dev/null +++ b/pr/672/docs/certificate/struct.SkipServerVerification.html @@ -0,0 +1,43 @@ +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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/crates.js b/pr/672/docs/crates.js new file mode 100644 index 000000000..5bec0807a --- /dev/null +++ b/pr/672/docs/crates.js @@ -0,0 +1,2 @@ +window.ALL_CRATES = ["bench","bulk","certificate","data_transfer","noq","noq_perf","noq_proto","noq_udp","perf","set_up_connection"]; +//{"start":21,"fragment_lengths":[7,7,14,16,6,11,12,10,7,20]} \ No newline at end of file diff --git a/pr/672/docs/data_transfer/all.html b/pr/672/docs/data_transfer/all.html new file mode 100644 index 000000000..fd64bbb8f --- /dev/null +++ b/pr/672/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/672/docs/data_transfer/fn.main.html b/pr/672/docs/data_transfer/fn.main.html new file mode 100644 index 000000000..8bcfacdf5 --- /dev/null +++ b/pr/672/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/672/docs/data_transfer/fn.open_bidirectional_stream.html b/pr/672/docs/data_transfer/fn.open_bidirectional_stream.html new file mode 100644 index 000000000..2655892df --- /dev/null +++ b/pr/672/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/672/docs/data_transfer/fn.open_unidirectional_stream.html b/pr/672/docs/data_transfer/fn.open_unidirectional_stream.html new file mode 100644 index 000000000..dabbc95a8 --- /dev/null +++ b/pr/672/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/672/docs/data_transfer/fn.receive_bidirectional_stream.html b/pr/672/docs/data_transfer/fn.receive_bidirectional_stream.html new file mode 100644 index 000000000..7622451c8 --- /dev/null +++ b/pr/672/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/672/docs/data_transfer/fn.receive_datagram.html b/pr/672/docs/data_transfer/fn.receive_datagram.html new file mode 100644 index 000000000..53214940b --- /dev/null +++ b/pr/672/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/672/docs/data_transfer/fn.receive_unidirectional_stream.html b/pr/672/docs/data_transfer/fn.receive_unidirectional_stream.html new file mode 100644 index 000000000..3cb11052a --- /dev/null +++ b/pr/672/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/672/docs/data_transfer/fn.send_unreliable.html b/pr/672/docs/data_transfer/fn.send_unreliable.html new file mode 100644 index 000000000..eb16f5a71 --- /dev/null +++ b/pr/672/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/672/docs/data_transfer/index.html b/pr/672/docs/data_transfer/index.html new file mode 100644 index 000000000..bc41bd1c4 --- /dev/null +++ b/pr/672/docs/data_transfer/index.html @@ -0,0 +1 @@ +data_transfer - Rust

Crate data_transfer

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

All

Rustdoc help

Back
\ No newline at end of file diff --git a/pr/672/docs/noq/all.html b/pr/672/docs/noq/all.html new file mode 100644 index 000000000..b4551b51f --- /dev/null +++ b/pr/672/docs/noq/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/672/docs/noq/congestion/index.html b/pr/672/docs/noq/congestion/index.html new file mode 100644 index 000000000..7b4789267 --- /dev/null +++ b/pr/672/docs/noq/congestion/index.html @@ -0,0 +1,4 @@ +noq::congestion - Rust

Module congestion

Module congestion 

Source
Expand description

Logic for controlling the rate at which data is sent

+

Structs§

Bbr3
Experimental! Use at your own risk.
Bbr3Config
Configuration for the Bbr3 congestion controller
ControllerMetrics
Common congestion controller metrics used both for logging purposes +but also to alter the pacing of the connection with +pacing_rate and send_quantum
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/672/docs/noq/congestion/sidebar-items.js b/pr/672/docs/noq/congestion/sidebar-items.js new file mode 100644 index 000000000..07e9dc7b1 --- /dev/null +++ b/pr/672/docs/noq/congestion/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["Bbr3","Bbr3Config","ControllerMetrics","Cubic","CubicConfig","NewReno","NewRenoConfig"],"trait":["Controller","ControllerFactory"]}; \ No newline at end of file diff --git a/pr/672/docs/noq/congestion/struct.Bbr3.html b/pr/672/docs/noq/congestion/struct.Bbr3.html new file mode 100644 index 000000000..e20076bcb --- /dev/null +++ b/pr/672/docs/noq/congestion/struct.Bbr3.html @@ -0,0 +1,60 @@ +Bbr3 in noq::congestion - Rust

Bbr3

Struct Bbr3 

Source
pub struct Bbr3 { /* 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 https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html +equivalent to a combination of BBR and C states +https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.4 +https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.1

+

Trait Implementations§

Source§

impl Clone for Bbr3

Source§

fn clone(&self) -> Bbr3

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 Bbr3

Source§

fn on_spurious_congestion_event(&mut self)

Source§

fn on_packet_sent(&mut self, now: Instant, bytes: u16, pn: u64)

One packet was just sent
Source§

fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + pn: 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, + largest_lost_pn: u64, +)

Packets were deemed lost or marked congested Read more
Source§

fn on_packet_lost(&mut self, lost_bytes: u16, pn: u64, now: Instant)

One packet was just lost
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

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

fn on_ack_frequency_update( + &mut self, + ack_eliciting_threshold: u64, + requested_max_ack_delay: Duration, +)

The peer’s ACK-frequency parameters have changed Read more
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 +This is also used to alter the pacing of the connection with +pacing_rate and send_quantum
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<Bbr3>) -> Box<dyn Any>

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

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

One or more packets were just sent
Source§

impl Debug for Bbr3

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Bbr3

§

impl RefUnwindSafe for Bbr3

§

impl Send for Bbr3

§

impl Sync for Bbr3

§

impl Unpin for Bbr3

§

impl UnwindSafe for Bbr3

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/congestion/struct.Bbr3Config.html b/pr/672/docs/noq/congestion/struct.Bbr3Config.html new file mode 100644 index 000000000..70744366a --- /dev/null +++ b/pr/672/docs/noq/congestion/struct.Bbr3Config.html @@ -0,0 +1,39 @@ +Bbr3Config in noq::congestion - Rust

Bbr3Config

Struct Bbr3Config 

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

Configuration for the Bbr3 congestion controller

+

Different pacing_gains can be set to modify the multiplier used to +increase the sending rates. +Different cwnd_gains can be set to modify the multiplier used to increase +the congestion windows. +All of these parameters are specific to different states of the algorithm: see BbrState +pacing_margin_percent is used to set a margin when calculating the pacing_rate in order +to not send at 100% capacity when calculating pacing.

+

Implementations§

Source§

impl Bbr3Config

Source

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

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 Bbr3Config

Source§

fn clone(&self) -> Bbr3Config

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 Bbr3Config

Source§

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

Construct a fresh Controller
Source§

impl Debug for Bbr3Config

Source§

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

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

impl Default for Bbr3Config

Source§

fn default() -> Bbr3Config

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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/congestion/struct.ControllerMetrics.html b/pr/672/docs/noq/congestion/struct.ControllerMetrics.html new file mode 100644 index 000000000..e5c0772bb --- /dev/null +++ b/pr/672/docs/noq/congestion/struct.ControllerMetrics.html @@ -0,0 +1,35 @@ +ControllerMetrics in noq::congestion - Rust

ControllerMetrics

Struct ControllerMetrics 

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

Common congestion controller metrics used both for logging purposes +but also to alter the pacing of the connection with +pacing_rate and send_quantum

+

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 (bytes/s)

+
§send_quantum: Option<u64>

Send Quantum (bytes) used to control the size of packet bursts

+

Trait Implementations§

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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/congestion/struct.Cubic.html b/pr/672/docs/noq/congestion/struct.Cubic.html new file mode 100644 index 000000000..8fc498e57 --- /dev/null +++ b/pr/672/docs/noq/congestion/struct.Cubic.html @@ -0,0 +1,55 @@ +Cubic in noq::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, + _pn: 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, + _largest_lost_pn: 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 +This is also used to alter the pacing of the connection with +pacing_rate and send_quantum
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, largest_pn: u64)

One or more packets were just sent
Source§

fn on_packet_sent(&mut self, now: Instant, bytes: u16, pn: u64)

One packet was 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_packet_lost(&mut self, lost_bytes: u16, pn: u64, now: Instant)

One packet was just lost
Source§

fn on_ack_frequency_update( + &mut self, + ack_eliciting_threshold: u64, + requested_max_ack_delay: Duration, +)

The peer’s ACK-frequency parameters have changed Read more
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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/congestion/struct.CubicConfig.html b/pr/672/docs/noq/congestion/struct.CubicConfig.html new file mode 100644 index 000000000..a41c39a3b --- /dev/null +++ b/pr/672/docs/noq/congestion/struct.CubicConfig.html @@ -0,0 +1,32 @@ +CubicConfig in noq::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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/congestion/struct.NewReno.html b/pr/672/docs/noq/congestion/struct.NewReno.html new file mode 100644 index 000000000..56b75d807 --- /dev/null +++ b/pr/672/docs/noq/congestion/struct.NewReno.html @@ -0,0 +1,59 @@ +NewReno in noq::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, + _pn: 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, + _largest_lost_pn: 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 +This is also used to alter the pacing of the connection with +pacing_rate and send_quantum
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, largest_pn: u64)

One or more packets were just sent
Source§

fn on_packet_sent(&mut self, now: Instant, bytes: u16, pn: u64)

One packet was 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_packet_lost(&mut self, lost_bytes: u16, pn: u64, now: Instant)

One packet was just lost
Source§

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

fn on_ack_frequency_update( + &mut self, + ack_eliciting_threshold: u64, + requested_max_ack_delay: Duration, +)

The peer’s ACK-frequency parameters have changed 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/congestion/struct.NewRenoConfig.html b/pr/672/docs/noq/congestion/struct.NewRenoConfig.html new file mode 100644 index 000000000..4b226f8c0 --- /dev/null +++ b/pr/672/docs/noq/congestion/struct.NewRenoConfig.html @@ -0,0 +1,33 @@ +NewRenoConfig in noq::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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/congestion/trait.Controller.html b/pr/672/docs/noq/congestion/trait.Controller.html new file mode 100644 index 000000000..6c44b51fa --- /dev/null +++ b/pr/672/docs/noq/congestion/trait.Controller.html @@ -0,0 +1,107 @@ +Controller in noq::congestion - Rust

Controller

Trait Controller 

Source
pub trait Controller:
+    Send
+    + Sync
+    + Debug {
+
Show 14 methods // Required methods + fn on_congestion_event( + &mut self, + now: Instant, + sent: Instant, + is_persistent_congestion: bool, + is_ecn: bool, + lost_bytes: u64, + largest_lost_pn: 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, largest_pn: u64) { ... } + fn on_packet_sent(&mut self, now: Instant, bytes: u16, pn: u64) { ... } + fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + pn: 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_packet_lost(&mut self, lost_bytes: u16, pn: u64, now: Instant) { ... } + fn on_spurious_congestion_event(&mut self) { ... } + fn on_ack_frequency_update( + &mut self, + ack_eliciting_threshold: u64, + requested_max_ack_delay: Duration, + ) { ... } + 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, + largest_lost_pn: 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. +largest_lost_pn indicates the packet number of the packet with the highest packet number +in the congestion event.

+
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, largest_pn: u64)

One or more packets were just sent

+
Source

fn on_packet_sent(&mut self, now: Instant, bytes: u16, pn: u64)

One packet was just sent

+
Source

fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + pn: 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_packet_lost(&mut self, lost_bytes: u16, pn: u64, now: Instant)

One packet was just lost

+
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 on_ack_frequency_update( + &mut self, + ack_eliciting_threshold: u64, + requested_max_ack_delay: Duration, +)

The peer’s ACK-frequency parameters have changed

+

ack_eliciting_threshold is the number of ack-eliciting packets the peer may receive +before being required to send an immediate ACK (per the QUIC ACK frequency extension). +requested_max_ack_delay is the maximum delay we asked the peer to wait before sending +an ACK when the threshold hasn’t been reached.

+

Controllers can use this to refine estimates that depend on peer ACK behavior (e.g. +BBR’s offload budget).

+
Source

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled +This is also used to alter the pacing of the connection with +pacing_rate and send_quantum

+

Implementors§

\ No newline at end of file diff --git a/pr/672/docs/noq/congestion/trait.ControllerFactory.html b/pr/672/docs/noq/congestion/trait.ControllerFactory.html new file mode 100644 index 000000000..4c9343f49 --- /dev/null +++ b/pr/672/docs/noq/congestion/trait.ControllerFactory.html @@ -0,0 +1,10 @@ +ControllerFactory in noq::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/672/docs/noq/connection/enum.SendDatagramError.html b/pr/672/docs/noq/connection/enum.SendDatagramError.html new file mode 100644 index 000000000..c4cce6778 --- /dev/null +++ b/pr/672/docs/noq/connection/enum.SendDatagramError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/enum.SendDatagramError.html...

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

Redirecting to ../../noq/struct.AcceptBi.html...

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

Redirecting to ../../noq/struct.AcceptUni.html...

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

Redirecting to ../../noq/struct.Closed.html...

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

Redirecting to ../../noq/struct.Connecting.html...

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

Redirecting to ../../noq/struct.Connection.html...

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

Redirecting to ../../noq/struct.OnClosed.html...

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

Redirecting to ../../noq/struct.OpenBi.html...

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

Redirecting to ../../noq/struct.OpenUni.html...

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

Redirecting to ../../noq/struct.ReadDatagram.html...

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

Redirecting to ../../noq/struct.SendDatagram.html...

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

Redirecting to ../../noq/struct.WeakConnectionHandle.html...

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

Redirecting to ../../noq/struct.ZeroRttAccepted.html...

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

Module crypto

Module crypto 

Source
Expand description

Traits and implementations for the QUIC cryptography protocol

+

The protocol logic in noq 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§

ClientConfig
Client-side configuration for the crypto protocol
HandshakeTokenKey
The trait for encrypting tokens that power retry packets and NEW_TOKEN frames.
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/672/docs/noq/crypto/rustls/enum.Error.html b/pr/672/docs/noq/crypto/rustls/enum.Error.html new file mode 100644 index 000000000..836ac7220 --- /dev/null +++ b/pr/672/docs/noq/crypto/rustls/enum.Error.html @@ -0,0 +1,100 @@ +Error in noq::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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/crypto/rustls/index.html b/pr/672/docs/noq/crypto/rustls/index.html new file mode 100644 index 000000000..90d332226 --- /dev/null +++ b/pr/672/docs/noq/crypto/rustls/index.html @@ -0,0 +1,2 @@ +noq::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/672/docs/noq/crypto/rustls/sidebar-items.js b/pr/672/docs/noq/crypto/rustls/sidebar-items.js new file mode 100644 index 000000000..65f98d4d4 --- /dev/null +++ b/pr/672/docs/noq/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/672/docs/noq/crypto/rustls/struct.HandshakeData.html b/pr/672/docs/noq/crypto/rustls/struct.HandshakeData.html new file mode 100644 index 000000000..616b610ef --- /dev/null +++ b/pr/672/docs/noq/crypto/rustls/struct.HandshakeData.html @@ -0,0 +1,34 @@ +HandshakeData in noq::crypto::rustls - Rust

HandshakeData

Struct HandshakeData 

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

Authentication data for (rustls) TLS session

+

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.
§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: Option<NamedGroup>

The key exchange group negotiated with the peer. Useful for logging and metrics.

+

None until the handshake has completed.

+

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/crypto/rustls/struct.NoInitialCipherSuite.html b/pr/672/docs/noq/crypto/rustls/struct.NoInitialCipherSuite.html new file mode 100644 index 000000000..e019c918e --- /dev/null +++ b/pr/672/docs/noq/crypto/rustls/struct.NoInitialCipherSuite.html @@ -0,0 +1,30 @@ +NoInitialCipherSuite in noq::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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

QuicClientConfig

Struct QuicClientConfig 

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

A QUIC-compatible TLS client configuration

+

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

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

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

+

Implementations§

Source§

impl QuicClientConfig

Source

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

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

+

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

+
Source

pub fn set_alpn_protocols(&mut self, alpn_protocols: Vec<Vec<u8>>)

Updates the set of ALPN protocols configured in the client config.

+

Trait Implementations§

Source§

impl ClientConfig for QuicClientConfig

Source§

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

Start a client session with this configuration
Source§

impl Clone for QuicClientConfig

Source§

fn clone(&self) -> QuicClientConfig

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 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> 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/crypto/rustls/struct.QuicServerConfig.html b/pr/672/docs/noq/crypto/rustls/struct.QuicServerConfig.html new file mode 100644 index 000000000..c2a99906d --- /dev/null +++ b/pr/672/docs/noq/crypto/rustls/struct.QuicServerConfig.html @@ -0,0 +1,58 @@ +QuicServerConfig in noq::crypto::rustls - Rust

QuicServerConfig

Struct QuicServerConfig 

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

A QUIC-compatible TLS server configuration

+

noq 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.

+
Source

pub fn set_alpn_protocols(&mut self, alpn_protocols: Vec<Vec<u8>>)

Updates the set of ALPN protocols configured in the server config.

+

Trait Implementations§

Source§

impl Clone for QuicServerConfig

Source§

fn clone(&self) -> QuicServerConfig

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 ServerConfig for QuicServerConfig

Source§

fn start_session( + &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<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> 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/crypto/rustls/struct.TlsSession.html b/pr/672/docs/noq/crypto/rustls/struct.TlsSession.html new file mode 100644 index 000000000..9bd57d417 --- /dev/null +++ b/pr/672/docs/noq/crypto/rustls/struct.TlsSession.html @@ -0,0 +1,37 @@ +TlsSession in noq::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 Read more
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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/crypto/sidebar-items.js b/pr/672/docs/noq/crypto/sidebar-items.js new file mode 100644 index 000000000..710b27671 --- /dev/null +++ b/pr/672/docs/noq/crypto/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["rustls"],"struct":["CryptoError","ExportKeyingMaterialError","KeyPair","Keys","UnsupportedVersion"],"trait":["ClientConfig","HandshakeTokenKey","HeaderKey","HmacKey","PacketKey","ServerConfig","Session"]}; \ No newline at end of file diff --git a/pr/672/docs/noq/crypto/struct.CryptoError.html b/pr/672/docs/noq/crypto/struct.CryptoError.html new file mode 100644 index 000000000..569eb4367 --- /dev/null +++ b/pr/672/docs/noq/crypto/struct.CryptoError.html @@ -0,0 +1,24 @@ +CryptoError in noq::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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/crypto/struct.ExportKeyingMaterialError.html b/pr/672/docs/noq/crypto/struct.ExportKeyingMaterialError.html new file mode 100644 index 000000000..ba9c3d145 --- /dev/null +++ b/pr/672/docs/noq/crypto/struct.ExportKeyingMaterialError.html @@ -0,0 +1,30 @@ +ExportKeyingMaterialError in noq::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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/crypto/struct.KeyPair.html b/pr/672/docs/noq/crypto/struct.KeyPair.html new file mode 100644 index 000000000..0a703377b --- /dev/null +++ b/pr/672/docs/noq/crypto/struct.KeyPair.html @@ -0,0 +1,35 @@ +KeyPair in noq::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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/crypto/struct.Keys.html b/pr/672/docs/noq/crypto/struct.Keys.html new file mode 100644 index 000000000..620d9df84 --- /dev/null +++ b/pr/672/docs/noq/crypto/struct.Keys.html @@ -0,0 +1,29 @@ +Keys in noq::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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/crypto/struct.UnsupportedVersion.html b/pr/672/docs/noq/crypto/struct.UnsupportedVersion.html new file mode 100644 index 000000000..52ba3fac8 --- /dev/null +++ b/pr/672/docs/noq/crypto/struct.UnsupportedVersion.html @@ -0,0 +1,24 @@ +UnsupportedVersion in noq::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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/crypto/trait.ClientConfig.html b/pr/672/docs/noq/crypto/trait.ClientConfig.html new file mode 100644 index 000000000..2df73cbd7 --- /dev/null +++ b/pr/672/docs/noq/crypto/trait.ClientConfig.html @@ -0,0 +1,16 @@ +ClientConfig in noq::crypto - Rust

ClientConfig

Trait ClientConfig 

Source
pub trait ClientConfig: Send + Sync {
+    // Required method
+    fn start_session(
+        &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, + 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/672/docs/noq/crypto/trait.HandshakeTokenKey.html b/pr/672/docs/noq/crypto/trait.HandshakeTokenKey.html new file mode 100644 index 000000000..cf51ee2ce --- /dev/null +++ b/pr/672/docs/noq/crypto/trait.HandshakeTokenKey.html @@ -0,0 +1,23 @@ +HandshakeTokenKey in noq::crypto - Rust

HandshakeTokenKey

Trait HandshakeTokenKey 

Source
pub trait HandshakeTokenKey: Send + Sync {
+    // Required methods
+    fn seal(
+        &self,
+        token_nonce: u128,
+        data: &mut Vec<u8>,
+    ) -> Result<(), CryptoError>;
+    fn open<'a>(
+        &self,
+        token_nonce: u128,
+        data: &'a mut [u8],
+    ) -> Result<&'a [u8], CryptoError>;
+}
Expand description

The trait for encrypting tokens that power retry packets and NEW_TOKEN frames.

+

Required Methods§

Source

fn seal(&self, token_nonce: u128, data: &mut Vec<u8>) -> Result<(), CryptoError>

Method for sealing a token in-place.

+

The nonce doesn’t need to be attached to the ciphertext, +but the authentication tag is expected to be appended to data.

+
Source

fn open<'a>( + &self, + token_nonce: u128, + data: &'a mut [u8], +) -> Result<&'a [u8], CryptoError>

Method for opening a sealed message data in-place.

+

Returns the portion of data that contains the decrypted plaintext.

+

Implementors§

\ No newline at end of file diff --git a/pr/672/docs/noq/crypto/trait.HeaderKey.html b/pr/672/docs/noq/crypto/trait.HeaderKey.html new file mode 100644 index 000000000..0270f5240 --- /dev/null +++ b/pr/672/docs/noq/crypto/trait.HeaderKey.html @@ -0,0 +1,10 @@ +HeaderKey in noq::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/672/docs/noq/crypto/trait.HmacKey.html b/pr/672/docs/noq/crypto/trait.HmacKey.html new file mode 100644 index 000000000..f57593ce2 --- /dev/null +++ b/pr/672/docs/noq/crypto/trait.HmacKey.html @@ -0,0 +1,10 @@ +HmacKey in noq::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/672/docs/noq/crypto/trait.PacketKey.html b/pr/672/docs/noq/crypto/trait.PacketKey.html new file mode 100644 index 000000000..a3baef92e --- /dev/null +++ b/pr/672/docs/noq/crypto/trait.PacketKey.html @@ -0,0 +1,51 @@ +PacketKey in noq::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/672/docs/noq/crypto/trait.ServerConfig.html b/pr/672/docs/noq/crypto/trait.ServerConfig.html new file mode 100644 index 000000000..b535a589c --- /dev/null +++ b/pr/672/docs/noq/crypto/trait.ServerConfig.html @@ -0,0 +1,39 @@ +ServerConfig in noq::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,
+        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.

+

See the specification: https://www.rfc-editor.org/rfc/rfc9001#name-retry-packet-integrity

+
Source

fn start_session( + &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/672/docs/noq/crypto/trait.Session.html b/pr/672/docs/noq/crypto/trait.Session.html new file mode 100644 index 000000000..813e78b20 --- /dev/null +++ b/pr/672/docs/noq/crypto/trait.Session.html @@ -0,0 +1,69 @@ +Session in noq::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

+

See the specification: https://www.rfc-editor.org/rfc/rfc9001#name-retry-packet-integrity

+
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/672/docs/noq/endpoint/struct.Accept.html b/pr/672/docs/noq/endpoint/struct.Accept.html new file mode 100644 index 000000000..9cdddf024 --- /dev/null +++ b/pr/672/docs/noq/endpoint/struct.Accept.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.Accept.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/endpoint/struct.Endpoint.html b/pr/672/docs/noq/endpoint/struct.Endpoint.html new file mode 100644 index 000000000..7dcda303e --- /dev/null +++ b/pr/672/docs/noq/endpoint/struct.Endpoint.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.Endpoint.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/endpoint/struct.EndpointStats.html b/pr/672/docs/noq/endpoint/struct.EndpointStats.html new file mode 100644 index 000000000..83378cfb3 --- /dev/null +++ b/pr/672/docs/noq/endpoint/struct.EndpointStats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.EndpointStats.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/enum.ClosePathError.html b/pr/672/docs/noq/enum.ClosePathError.html new file mode 100644 index 000000000..3ae89a692 --- /dev/null +++ b/pr/672/docs/noq/enum.ClosePathError.html @@ -0,0 +1,40 @@ +ClosePathError in noq - Rust

ClosePathError

Enum ClosePathError 

Source
pub enum ClosePathError {
+    MultipathNotNegotiated,
+    ClosedPath,
+    LastOpenPath,
+}
Expand description

Errors triggered when abandoning a path

+

Variants§

§

MultipathNotNegotiated

Multipath is not negotiated

+
§

ClosedPath

The path is already closed or was never opened

+
§

LastOpenPath

Cannot close the last remaining open path via the local API.

+

Use Connection::close to end the connection instead.

+

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<(), Error>

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

impl Display for ClosePathError

Source§

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

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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/enum.ConfigError.html b/pr/672/docs/noq/enum.ConfigError.html new file mode 100644 index 000000000..6813b219d --- /dev/null +++ b/pr/672/docs/noq/enum.ConfigError.html @@ -0,0 +1,35 @@ +ConfigError in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/enum.ConnectError.html b/pr/672/docs/noq/enum.ConnectError.html new file mode 100644 index 000000000..15f029706 --- /dev/null +++ b/pr/672/docs/noq/enum.ConnectError.html @@ -0,0 +1,50 @@ +ConnectError in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/enum.ConnectionError.html b/pr/672/docs/noq/enum.ConnectionError.html new file mode 100644 index 000000000..f98fd5256 --- /dev/null +++ b/pr/672/docs/noq/enum.ConnectionError.html @@ -0,0 +1,53 @@ +ConnectionError in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/enum.Dir.html b/pr/672/docs/noq/enum.Dir.html new file mode 100644 index 000000000..183c2bdbf --- /dev/null +++ b/pr/672/docs/noq/enum.Dir.html @@ -0,0 +1,53 @@ +Dir in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/enum.EcnCodepoint.html b/pr/672/docs/noq/enum.EcnCodepoint.html new file mode 100644 index 000000000..cca291c39 --- /dev/null +++ b/pr/672/docs/noq/enum.EcnCodepoint.html @@ -0,0 +1,40 @@ +EcnCodepoint in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/enum.FrameType.html b/pr/672/docs/noq/enum.FrameType.html new file mode 100644 index 000000000..ded15b19f --- /dev/null +++ b/pr/672/docs/noq/enum.FrameType.html @@ -0,0 +1,78 @@ +FrameType in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/enum.PathError.html b/pr/672/docs/noq/enum.PathError.html new file mode 100644 index 000000000..5ec5dab49 --- /dev/null +++ b/pr/672/docs/noq/enum.PathError.html @@ -0,0 +1,45 @@ +PathError in noq - 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<(), Error>

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

impl Display for PathError

Source§

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

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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/enum.PathEvent.html b/pr/672/docs/noq/enum.PathEvent.html new file mode 100644 index 000000000..f83c0c8f8 --- /dev/null +++ b/pr/672/docs/noq/enum.PathEvent.html @@ -0,0 +1,71 @@ +PathEvent in noq - Rust

PathEvent

Enum PathEvent 

Source
#[non_exhaustive]
pub enum PathEvent { +
#[non_exhaustive]
Established { + id: PathId, + }, +
#[non_exhaustive]
Abandoned { + id: PathId, + reason: PathAbandonReason, + }, +
#[non_exhaustive]
Discarded { + id: PathId, + path_stats: Box<PathStats>, + }, +
#[non_exhaustive]
RemoteStatus { + id: PathId, + status: PathStatus, + }, +
#[non_exhaustive]
ObservedAddr { + id: PathId, + addr: SocketAddr, + }, +}
Expand description

Application events about paths

+

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

#[non_exhaustive]
Established

A new path has established connection with the peer.

+

Fields

This variant is marked as non-exhaustive
Non-exhaustive enum variants could have additional fields added in future. Therefore, non-exhaustive enum variants cannot be constructed in external crates and cannot be matched against.
§id: PathId

The path which can now be used for application data.

+
§

#[non_exhaustive]
Abandoned

A path was abandoned and is no longer usable.

+

This event will always be followed by Self::Discarded after some time.

+

Fields

This variant is marked as non-exhaustive
Non-exhaustive enum variants could have additional fields added in future. Therefore, non-exhaustive enum variants cannot be constructed in external crates and cannot be matched against.
§id: PathId

With path was abandoned.

+
§reason: PathAbandonReason

Reason why this path was abandoned.

+
§

#[non_exhaustive]
Discarded

A path was discarded and all remaining state for it has been removed.

+

This event is the last event for a path, and is always emitted after Self::Abandoned.

+

Fields

This variant is marked as non-exhaustive
Non-exhaustive enum variants could have additional fields added in future. Therefore, non-exhaustive enum variants cannot be constructed in external crates and cannot be matched against.
§id: PathId

Which path had its state dropped

+
§path_stats: Box<PathStats>

The final path stats, they are no longer available via Connection::stats

+
§

#[non_exhaustive]
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

This variant is marked as non-exhaustive
Non-exhaustive enum variants could have additional fields added in future. Therefore, non-exhaustive enum variants cannot be constructed in external crates and cannot be matched against.
§id: PathId

Path which has changed status

+
§status: PathStatus

The new status set by the remote

+
§

#[non_exhaustive]
ObservedAddr

Received an observation of our external address from the peer.

+

Fields

This variant is marked as non-exhaustive
Non-exhaustive enum variants could have additional fields added in future. Therefore, non-exhaustive enum variants cannot be constructed in external crates and cannot be matched against.
§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<(), Error>

Formats the value using the given formatter. Read more
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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/enum.PathStatus.html b/pr/672/docs/noq/enum.PathStatus.html new file mode 100644 index 000000000..52ae39f11 --- /dev/null +++ b/pr/672/docs/noq/enum.PathStatus.html @@ -0,0 +1,42 @@ +PathStatus in noq - 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<(), Error>

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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/enum.ReadError.html b/pr/672/docs/noq/enum.ReadError.html new file mode 100644 index 000000000..ee88e31be --- /dev/null +++ b/pr/672/docs/noq/enum.ReadError.html @@ -0,0 +1,44 @@ +ReadError in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/enum.ReadExactError.html b/pr/672/docs/noq/enum.ReadExactError.html new file mode 100644 index 000000000..0b972c610 --- /dev/null +++ b/pr/672/docs/noq/enum.ReadExactError.html @@ -0,0 +1,37 @@ +ReadExactError in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/enum.ReadToEndError.html b/pr/672/docs/noq/enum.ReadToEndError.html new file mode 100644 index 000000000..7c91e932c --- /dev/null +++ b/pr/672/docs/noq/enum.ReadToEndError.html @@ -0,0 +1,37 @@ +ReadToEndError in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/enum.ResetError.html b/pr/672/docs/noq/enum.ResetError.html new file mode 100644 index 000000000..bc3349d44 --- /dev/null +++ b/pr/672/docs/noq/enum.ResetError.html @@ -0,0 +1,39 @@ +ResetError in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/enum.SendDatagramError.html b/pr/672/docs/noq/enum.SendDatagramError.html new file mode 100644 index 000000000..98c46ccbd --- /dev/null +++ b/pr/672/docs/noq/enum.SendDatagramError.html @@ -0,0 +1,43 @@ +SendDatagramError in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/enum.SetPathStatusError.html b/pr/672/docs/noq/enum.SetPathStatusError.html new file mode 100644 index 000000000..a78045aac --- /dev/null +++ b/pr/672/docs/noq/enum.SetPathStatusError.html @@ -0,0 +1,37 @@ +SetPathStatusError in noq - 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<(), Error>

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

impl Display for SetPathStatusError

Source§

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

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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/enum.Side.html b/pr/672/docs/noq/enum.Side.html new file mode 100644 index 000000000..cd20a1d92 --- /dev/null +++ b/pr/672/docs/noq/enum.Side.html @@ -0,0 +1,54 @@ +Side in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/enum.StoppedError.html b/pr/672/docs/noq/enum.StoppedError.html new file mode 100644 index 000000000..2401e7bfe --- /dev/null +++ b/pr/672/docs/noq/enum.StoppedError.html @@ -0,0 +1,39 @@ +StoppedError in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/enum.WriteError.html b/pr/672/docs/noq/enum.WriteError.html new file mode 100644 index 000000000..81128fb54 --- /dev/null +++ b/pr/672/docs/noq/enum.WriteError.html @@ -0,0 +1,44 @@ +WriteError in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/event_stream/struct.Lagged.html b/pr/672/docs/noq/event_stream/struct.Lagged.html new file mode 100644 index 000000000..d36b86d2e --- /dev/null +++ b/pr/672/docs/noq/event_stream/struct.Lagged.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.Lagged.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/event_stream/struct.NatTraversalUpdates.html b/pr/672/docs/noq/event_stream/struct.NatTraversalUpdates.html new file mode 100644 index 000000000..edcccd603 --- /dev/null +++ b/pr/672/docs/noq/event_stream/struct.NatTraversalUpdates.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.NatTraversalUpdates.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/event_stream/struct.ObservedExternalAddr.html b/pr/672/docs/noq/event_stream/struct.ObservedExternalAddr.html new file mode 100644 index 000000000..22ca8bf7d --- /dev/null +++ b/pr/672/docs/noq/event_stream/struct.ObservedExternalAddr.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.ObservedExternalAddr.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/event_stream/struct.PathEvents.html b/pr/672/docs/noq/event_stream/struct.PathEvents.html new file mode 100644 index 000000000..584df6b1e --- /dev/null +++ b/pr/672/docs/noq/event_stream/struct.PathEvents.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.PathEvents.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/fn.default_runtime.html b/pr/672/docs/noq/fn.default_runtime.html new file mode 100644 index 000000000..b7d92b411 --- /dev/null +++ b/pr/672/docs/noq/fn.default_runtime.html @@ -0,0 +1,5 @@ +default_runtime in noq - 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/672/docs/noq/incoming/struct.Incoming.html b/pr/672/docs/noq/incoming/struct.Incoming.html new file mode 100644 index 000000000..50fb0ce69 --- /dev/null +++ b/pr/672/docs/noq/incoming/struct.Incoming.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.Incoming.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/incoming/struct.IncomingFuture.html b/pr/672/docs/noq/incoming/struct.IncomingFuture.html new file mode 100644 index 000000000..2f7f4e245 --- /dev/null +++ b/pr/672/docs/noq/incoming/struct.IncomingFuture.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.IncomingFuture.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/incoming/struct.RetryError.html b/pr/672/docs/noq/incoming/struct.RetryError.html new file mode 100644 index 000000000..b813e4f34 --- /dev/null +++ b/pr/672/docs/noq/incoming/struct.RetryError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.RetryError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/index.html b/pr/672/docs/noq/index.html new file mode 100644 index 000000000..b3adb8319 --- /dev/null +++ b/pr/672/docs/noq/index.html @@ -0,0 +1,34 @@ +noq - Rust

Crate noq

Crate noq 

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 noq-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.

+

noq also exposes unreliable datagrams, which are a low-level primitive preferred when +automatic fragmentation and retransmission of certain data is not desired.

+

QUIC uses encryption and identity verification built directly on TLS 1.3. Just as with a TLS +server, it is useful for a QUIC server to be identified by a certificate signed by a trusted +authority. If this is infeasible–for example, if servers are short-lived or not associated +with a domain name–then as with TLS, self-signed certificates can be used to provide +encryption alone.

+

Re-exports§

pub use rustls;
pub use udp;

Modules§

congestion
Logic for controlling the rate at which data is sent
crypto
Traits and implementations for the QUIC cryptography protocol

Structs§

Accept
Future produced by Endpoint::accept
AcceptBi
Future produced by Connection::accept_bi
AcceptUni
Future produced by Connection::accept_uni
AckFrequencyConfig
Parameters for controlling the peer’s acknowledgement frequency
AddressDiscovery
Stream produced by Path::observed_external_addr
ApplicationClose
Reason given by an application for closing the connection
BloomTokenLog
Bloom filter-based TokenLog
Chunk
A chunk of data from the receive stream
ClientConfig
Configuration for outgoing connections
Closed
State of a Connection at the moment it was closed.
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
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.
DecryptedInitial
Decrypted payload of a QUIC Initial packet
Endpoint
A QUIC endpoint.
EndpointConfig
Global configuration for the endpoint, affecting all connections
EndpointStats
Statistics on Endpoint activity
FourTuple
Identifies a network path by the combination of remote and local addresses
FrameStats
Number of frames transmitted or received of each frame type.
IdleTimeout
Maximum duration of inactivity to accept before timing out the connection
Incoming
An incoming connection for which the server has not yet begun its part of the handshake
IncomingFuture
Basic adapter to let Incoming be await-ed like a Connecting
InvalidCid
The connection ID was not recognized by the ConnectionIdGenerator
Lagged
The receiver lagged too far behind.
MtuDiscoveryConfig
Parameters governing MTU discovery.
NatTraversalUpdates
A stream of NAT traversal updates for a connection.
NoneTokenLog
Null implementation of TokenLog, which never accepts tokens
NoneTokenStore
Null implementation of TokenStore, which does not store any tokens
ObservedExternalAddr
Watches the external address reported by the peer for this connection.
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 network transmission within a multipath-enabled connection.
PathEvents
A stream of PathEvents for all paths in a connection.
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 noq runtime for smol
StdSystemTime
Default implementation of TimeSource
Stopped
Future returned from SendStream::stopped.
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 noq 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.
WeakPathHandle
Weak handle for a Path that does not keep the connection alive.
ZeroRttAccepted
Future that completes when a connection is fully established

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
Dir
Whether a stream communicates data in both directions or only from the initiator
EcnCodepoint
Explicit congestion notification codepoint
FrameType
PathError
Errors that might trigger a path being closed
PathEvent
Application events about paths
PathStatus
The QUIC-MULTIPATH path status
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
SetPathStatusError
Error from setting path status
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
NetworkChangeHint
Hints when the caller identifies a network change.
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/672/docs/noq/path/struct.AddressDiscovery.html b/pr/672/docs/noq/path/struct.AddressDiscovery.html new file mode 100644 index 000000000..34e9370e2 --- /dev/null +++ b/pr/672/docs/noq/path/struct.AddressDiscovery.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.AddressDiscovery.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/path/struct.OpenPath.html b/pr/672/docs/noq/path/struct.OpenPath.html new file mode 100644 index 000000000..e67f2e217 --- /dev/null +++ b/pr/672/docs/noq/path/struct.OpenPath.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.OpenPath.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/path/struct.Path.html b/pr/672/docs/noq/path/struct.Path.html new file mode 100644 index 000000000..43ba55501 --- /dev/null +++ b/pr/672/docs/noq/path/struct.Path.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.Path.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/path/struct.WeakPathHandle.html b/pr/672/docs/noq/path/struct.WeakPathHandle.html new file mode 100644 index 000000000..098ef640e --- /dev/null +++ b/pr/672/docs/noq/path/struct.WeakPathHandle.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.WeakPathHandle.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/recv_stream/enum.ReadError.html b/pr/672/docs/noq/recv_stream/enum.ReadError.html new file mode 100644 index 000000000..2cd0f4dc9 --- /dev/null +++ b/pr/672/docs/noq/recv_stream/enum.ReadError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/enum.ReadError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/recv_stream/enum.ReadExactError.html b/pr/672/docs/noq/recv_stream/enum.ReadExactError.html new file mode 100644 index 000000000..6e058f920 --- /dev/null +++ b/pr/672/docs/noq/recv_stream/enum.ReadExactError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/enum.ReadExactError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/recv_stream/enum.ReadToEndError.html b/pr/672/docs/noq/recv_stream/enum.ReadToEndError.html new file mode 100644 index 000000000..1844a14af --- /dev/null +++ b/pr/672/docs/noq/recv_stream/enum.ReadToEndError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/enum.ReadToEndError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/recv_stream/enum.ResetError.html b/pr/672/docs/noq/recv_stream/enum.ResetError.html new file mode 100644 index 000000000..9f6e90992 --- /dev/null +++ b/pr/672/docs/noq/recv_stream/enum.ResetError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/enum.ResetError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/recv_stream/struct.RecvStream.html b/pr/672/docs/noq/recv_stream/struct.RecvStream.html new file mode 100644 index 000000000..a27b4685e --- /dev/null +++ b/pr/672/docs/noq/recv_stream/struct.RecvStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.RecvStream.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/recv_stream/struct.UnorderedRecvStream.html b/pr/672/docs/noq/recv_stream/struct.UnorderedRecvStream.html new file mode 100644 index 000000000..d6f27060a --- /dev/null +++ b/pr/672/docs/noq/recv_stream/struct.UnorderedRecvStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.UnorderedRecvStream.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/runtime/fn.default_runtime.html b/pr/672/docs/noq/runtime/fn.default_runtime.html new file mode 100644 index 000000000..67f4c870b --- /dev/null +++ b/pr/672/docs/noq/runtime/fn.default_runtime.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/fn.default_runtime.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/runtime/smol/struct.SmolRuntime.html b/pr/672/docs/noq/runtime/smol/struct.SmolRuntime.html new file mode 100644 index 000000000..e64385bd3 --- /dev/null +++ b/pr/672/docs/noq/runtime/smol/struct.SmolRuntime.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq/struct.SmolRuntime.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/runtime/tokio/struct.TokioRuntime.html b/pr/672/docs/noq/runtime/tokio/struct.TokioRuntime.html new file mode 100644 index 000000000..a2146491d --- /dev/null +++ b/pr/672/docs/noq/runtime/tokio/struct.TokioRuntime.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq/struct.TokioRuntime.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/runtime/trait.AsyncTimer.html b/pr/672/docs/noq/runtime/trait.AsyncTimer.html new file mode 100644 index 000000000..9c7431b09 --- /dev/null +++ b/pr/672/docs/noq/runtime/trait.AsyncTimer.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/trait.AsyncTimer.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/runtime/trait.AsyncUdpSocket.html b/pr/672/docs/noq/runtime/trait.AsyncUdpSocket.html new file mode 100644 index 000000000..5f29e23be --- /dev/null +++ b/pr/672/docs/noq/runtime/trait.AsyncUdpSocket.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/trait.AsyncUdpSocket.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/runtime/trait.Runtime.html b/pr/672/docs/noq/runtime/trait.Runtime.html new file mode 100644 index 000000000..65709d454 --- /dev/null +++ b/pr/672/docs/noq/runtime/trait.Runtime.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/trait.Runtime.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/runtime/trait.UdpSender.html b/pr/672/docs/noq/runtime/trait.UdpSender.html new file mode 100644 index 000000000..c332897c7 --- /dev/null +++ b/pr/672/docs/noq/runtime/trait.UdpSender.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/trait.UdpSender.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/send_stream/enum.StoppedError.html b/pr/672/docs/noq/send_stream/enum.StoppedError.html new file mode 100644 index 000000000..35e9cb2cf --- /dev/null +++ b/pr/672/docs/noq/send_stream/enum.StoppedError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/enum.StoppedError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/send_stream/enum.WriteError.html b/pr/672/docs/noq/send_stream/enum.WriteError.html new file mode 100644 index 000000000..8a8d44bef --- /dev/null +++ b/pr/672/docs/noq/send_stream/enum.WriteError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/enum.WriteError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/send_stream/struct.SendStream.html b/pr/672/docs/noq/send_stream/struct.SendStream.html new file mode 100644 index 000000000..5e6ba28e0 --- /dev/null +++ b/pr/672/docs/noq/send_stream/struct.SendStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.SendStream.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/send_stream/struct.Stopped.html b/pr/672/docs/noq/send_stream/struct.Stopped.html new file mode 100644 index 000000000..c32498254 --- /dev/null +++ b/pr/672/docs/noq/send_stream/struct.Stopped.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq/struct.Stopped.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq/sidebar-items.js b/pr/672/docs/noq/sidebar-items.js new file mode 100644 index 000000000..f816c2154 --- /dev/null +++ b/pr/672/docs/noq/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["ClosePathError","ConfigError","ConnectError","ConnectionError","Dir","EcnCodepoint","FrameType","PathError","PathEvent","PathStatus","ReadError","ReadExactError","ReadToEndError","ResetError","SendDatagramError","SetPathStatusError","Side","StoppedError","WriteError"],"fn":["default_runtime"],"mod":["congestion","crypto"],"struct":["Accept","AcceptBi","AcceptUni","AckFrequencyConfig","AddressDiscovery","ApplicationClose","BloomTokenLog","Chunk","ClientConfig","Closed","ClosedPath","ClosedStream","Connecting","Connection","ConnectionClose","ConnectionId","ConnectionStats","DecryptedInitial","Endpoint","EndpointConfig","EndpointStats","FourTuple","FrameStats","IdleTimeout","Incoming","IncomingFuture","InvalidCid","Lagged","MtuDiscoveryConfig","NatTraversalUpdates","NoneTokenLog","NoneTokenStore","ObservedExternalAddr","OnClosed","OpenBi","OpenPath","OpenUni","Path","PathEvents","PathId","PathStats","QlogConfig","QlogFileFactory","ReadDatagram","RecvStream","RetryError","SendDatagram","SendStream","ServerConfig","SmolRuntime","StdSystemTime","Stopped","StreamId","TokenMemoryCache","TokenReuseError","TokioRuntime","Transmit","TransportConfig","TransportErrorCode","UdpStats","UnorderedRecvStream","ValidationTokenConfig","VarInt","VarIntBoundsExceeded","WeakConnectionHandle","WeakPathHandle","ZeroRttAccepted"],"trait":["AsyncTimer","AsyncUdpSocket","ConnectionIdGenerator","NetworkChangeHint","QlogFactory","Runtime","TimeSource","TokenLog","TokenStore","UdpSender"]}; \ No newline at end of file diff --git a/pr/672/docs/noq/struct.Accept.html b/pr/672/docs/noq/struct.Accept.html new file mode 100644 index 000000000..7d8f60aff --- /dev/null +++ b/pr/672/docs/noq/struct.Accept.html @@ -0,0 +1,49 @@ +Accept in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.AcceptBi.html b/pr/672/docs/noq/struct.AcceptBi.html new file mode 100644 index 000000000..c39e70904 --- /dev/null +++ b/pr/672/docs/noq/struct.AcceptBi.html @@ -0,0 +1,53 @@ +AcceptBi in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

The type 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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.AcceptUni.html b/pr/672/docs/noq/struct.AcceptUni.html new file mode 100644 index 000000000..3c264af0c --- /dev/null +++ b/pr/672/docs/noq/struct.AcceptUni.html @@ -0,0 +1,53 @@ +AcceptUni in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

The type 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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.AckFrequencyConfig.html b/pr/672/docs/noq/struct.AckFrequencyConfig.html new file mode 100644 index 000000000..24267c9b1 --- /dev/null +++ b/pr/672/docs/noq/struct.AckFrequencyConfig.html @@ -0,0 +1,64 @@ +AckFrequencyConfig in noq - 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).

+

noq’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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.AddressDiscovery.html b/pr/672/docs/noq/struct.AddressDiscovery.html new file mode 100644 index 000000000..50718f587 --- /dev/null +++ b/pr/672/docs/noq/struct.AddressDiscovery.html @@ -0,0 +1,172 @@ +AddressDiscovery in noq - Rust

AddressDiscovery

Struct AddressDiscovery 

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

Stream produced by Path::observed_external_addr

+

This will always return the external address most recently reported by the remote over this +path. If the extension is not negotiated, this stream will never return.

+

Trait Implementations§

Source§

impl Stream for AddressDiscovery

Source§

type Item = SocketAddr

Values yielded by the stream.
Source§

fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, +) -> Poll<Option<Self::Item>>

Attempt to pull out the next value of this stream, registering the +current task for wakeup if the value is not yet available, and returning +None if the stream is exhausted. Read more
§

fn size_hint(&self) -> (usize, Option<usize>)

Returns the bounds on the remaining length of the stream. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<S> StreamExt for S
where + S: Stream + ?Sized,

§

fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>
where + Self: Unpin,

A convenience for calling [Stream::poll_next()] on !Unpin types.
§

fn next(&mut self) -> NextFuture<'_, Self>
where + Self: Unpin,

Retrieves the next item in the stream. Read more
§

fn try_next<T, E>(&mut self) -> TryNextFuture<'_, Self>
where + Self: Stream<Item = Result<T, E>> + Unpin,

Retrieves the next item in the stream. Read more
§

fn count(self) -> CountFuture<Self>
where + Self: Sized,

Counts the number of items in the stream. Read more
§

fn map<T, F>(self, f: F) -> Map<Self, F>
where + Self: Sized, + F: FnMut(Self::Item) -> T,

Maps items of the stream to new values using a closure. Read more
§

fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>
where + Self: Sized, + U: Stream, + F: FnMut(Self::Item) -> U,

Maps items to streams and then concatenates them. Read more
§

fn flatten(self) -> Flatten<Self>
where + Self: Sized, + Self::Item: Stream,

Concatenates inner streams. Read more
§

fn then<F, Fut>(self, f: F) -> Then<Self, F, Fut>
where + Self: Sized, + F: FnMut(Self::Item) -> Fut, + Fut: Future,

Maps items of the stream to new values using an async closure. Read more
§

fn filter<P>(self, predicate: P) -> Filter<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Keeps items of the stream for which predicate returns true. Read more
§

fn filter_map<T, F>(self, f: F) -> FilterMap<Self, F>
where + Self: Sized, + F: FnMut(Self::Item) -> Option<T>,

Filters and maps items of the stream using a closure. Read more
§

fn take(self, n: usize) -> Take<Self>
where + Self: Sized,

Takes only the first n items of the stream. Read more
§

fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Takes items while predicate returns true. Read more
§

fn map_while<B, P>(self, predicate: P) -> MapWhile<Self, P>
where + Self: Sized, + P: FnMut(Self::Item) -> Option<B>,

Maps items while predicate returns Some. Read more
§

fn skip(self, n: usize) -> Skip<Self>
where + Self: Sized,

Skips the first n items of the stream. Read more
§

fn skip_while<P>(self, predicate: P) -> SkipWhile<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Skips items while predicate returns true. Read more
§

fn step_by(self, step: usize) -> StepBy<Self>
where + Self: Sized,

Yields every stepth item. Read more
§

fn chain<U>(self, other: U) -> Chain<Self, U>
where + Self: Sized, + U: Stream<Item = Self::Item>,

Appends another stream to the end of this one. Read more
§

fn cloned<'a, T>(self) -> Cloned<Self>
where + Self: Sized + Stream<Item = &'a T>, + T: Clone + 'a,

Clones all items. Read more
§

fn copied<'a, T>(self) -> Copied<Self>
where + Self: Sized + Stream<Item = &'a T>, + T: Copy + 'a,

Copies all items. Read more
§

fn collect<C>(self) -> CollectFuture<Self, C>
where + Self: Sized, + C: Default + Extend<Self::Item>,

Collects all items in the stream into a collection. Read more
§

fn try_collect<T, E, C>(self) -> TryCollectFuture<Self, C>
where + Self: Sized + Stream<Item = Result<T, E>>, + C: Default + Extend<T>,

Collects all items in the fallible stream into a collection. Read more
§

fn partition<B, P>(self, predicate: P) -> PartitionFuture<Self, P, B>
where + Self: Sized, + B: Default + Extend<Self::Item>, + P: FnMut(&Self::Item) -> bool,

Partitions items into those for which predicate is true and those for which it is +false, and then collects them into two collections. Read more
§

fn fold<T, F>(self, init: T, f: F) -> FoldFuture<Self, F, T>
where + Self: Sized, + F: FnMut(T, Self::Item) -> T,

Accumulates a computation over the stream. Read more
§

fn try_fold<T, E, F, B>( + &mut self, + init: B, + f: F, +) -> TryFoldFuture<'_, Self, F, B>
where + Self: Sized + Stream<Item = Result<T, E>> + Unpin, + F: FnMut(B, T) -> Result<B, E>,

Accumulates a fallible computation over the stream. Read more
§

fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>
where + Self: Sized, + F: FnMut(&mut St, Self::Item) -> Option<B>,

Maps items of the stream to new values using a state value and a closure. Read more
§

fn fuse(self) -> Fuse<Self>
where + Self: Sized,

Fuses the stream so that it stops yielding items after the first None. Read more
§

fn cycle(self) -> Cycle<Self>
where + Self: Sized + Clone,

Repeats the stream from beginning to end, forever. Read more
§

fn enumerate(self) -> Enumerate<Self>
where + Self: Sized,

Enumerates items, mapping them to (index, item). Read more
§

fn inspect<F>(self, f: F) -> Inspect<Self, F>
where + Self: Sized, + F: FnMut(&Self::Item),

Calls a closure on each item and passes it on. Read more
§

fn nth(&mut self, n: usize) -> NthFuture<'_, Self>
where + Self: Unpin,

Gets the nth item of the stream. Read more
§

fn last(self) -> LastFuture<Self>
where + Self: Sized,

Returns the last item in the stream. Read more
§

fn find<P>(&mut self, predicate: P) -> FindFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(&Self::Item) -> bool,

Finds the first item of the stream for which predicate returns true. Read more
§

fn find_map<F, B>(&mut self, f: F) -> FindMapFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> Option<B>,

Applies a closure to items in the stream and returns the first Some result. Read more
§

fn position<P>(&mut self, predicate: P) -> PositionFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(Self::Item) -> bool,

Finds the index of the first item of the stream for which predicate returns true. Read more
§

fn all<P>(&mut self, predicate: P) -> AllFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(Self::Item) -> bool,

Tests if predicate returns true for all items in the stream. Read more
§

fn any<P>(&mut self, predicate: P) -> AnyFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(Self::Item) -> bool,

Tests if predicate returns true for any item in the stream. Read more
§

fn for_each<F>(self, f: F) -> ForEachFuture<Self, F>
where + Self: Sized, + F: FnMut(Self::Item),

Calls a closure on each item of the stream. Read more
§

fn try_for_each<F, E>(&mut self, f: F) -> TryForEachFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> Result<(), E>,

Calls a fallible closure on each item of the stream, stopping on first error. Read more
§

fn zip<U>(self, other: U) -> Zip<Self, U>
where + Self: Sized, + U: Stream,

Zips up two streams into a single stream of pairs. Read more
§

fn unzip<A, B, FromA, FromB>(self) -> UnzipFuture<Self, FromA, FromB>
where + FromA: Default + Extend<A>, + FromB: Default + Extend<B>, + Self: Sized + Stream<Item = (A, B)>,

Collects a stream of pairs into a pair of collections. Read more
§

fn or<S>(self, other: S) -> Or<Self, S>
where + Self: Sized, + S: Stream<Item = Self::Item>,

Merges with other stream, preferring items from self whenever both streams are ready. Read more
§

fn race<S>(self, other: S) -> Race<Self, S>
where + Self: Sized, + S: Stream<Item = Self::Item>,

Merges with other stream, with no preference for either stream when both are ready. Read more
§

fn drain(&mut self) -> Drain<'_, Self>

Yields all immediately available values from a stream. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Stream<Item = Self::Item> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the stream and changes its type to dyn Stream + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Stream<Item = Self::Item> + 'a>>
where + Self: Sized + 'a,

Boxes the stream and changes its type to dyn Stream + 'a. Read more
§

impl<St> StreamExt for St
where + St: Stream + ?Sized,

§

fn next(&mut self) -> Next<'_, Self>
where + Self: Unpin,

Consumes and returns the next value in the stream or None if the +stream is finished. Read more
§

fn try_next<T, E>(&mut self) -> TryNext<'_, Self>
where + Self: Stream<Item = Result<T, E>> + Unpin,

Consumes and returns the next item in the stream. If an error is +encountered before the next item, the error is returned instead. Read more
§

fn map<T, F>(self, f: F) -> Map<Self, F>
where + F: FnMut(Self::Item) -> T, + Self: Sized,

Maps this stream’s items to a different type, returning a new stream of +the resulting type. Read more
§

fn map_while<T, F>(self, f: F) -> MapWhile<Self, F>
where + F: FnMut(Self::Item) -> Option<T>, + Self: Sized,

Map this stream’s items to a different type for as long as determined by +the provided closure. A stream of the target type will be returned, +which will yield elements until the closure returns None. Read more
§

fn then<F, Fut>(self, f: F) -> Then<Self, Fut, F>
where + F: FnMut(Self::Item) -> Fut, + Fut: Future, + Self: Sized,

Maps this stream’s items asynchronously to a different type, returning a +new stream of the resulting type. Read more
§

fn merge<U>(self, other: U) -> Merge<Self, U>
where + U: Stream<Item = Self::Item>, + Self: Sized,

Combine two streams into one by interleaving the output of both as it +is produced. Read more
§

fn filter<F>(self, f: F) -> Filter<Self, F>
where + F: FnMut(&Self::Item) -> bool, + Self: Sized,

Filters the values produced by this stream according to the provided +predicate. Read more
§

fn filter_map<T, F>(self, f: F) -> FilterMap<Self, F>
where + F: FnMut(Self::Item) -> Option<T>, + Self: Sized,

Filters the values produced by this stream while simultaneously mapping +them to a different type according to the provided closure. Read more
§

fn fuse(self) -> Fuse<Self>
where + Self: Sized,

Creates a stream which ends after the first None. Read more
§

fn take(self, n: usize) -> Take<Self>
where + Self: Sized,

Creates a new stream of at most n items of the underlying stream. Read more
§

fn take_while<F>(self, f: F) -> TakeWhile<Self, F>
where + F: FnMut(&Self::Item) -> bool, + Self: Sized,

Take elements from this stream while the provided predicate +resolves to true. Read more
§

fn skip(self, n: usize) -> Skip<Self>
where + Self: Sized,

Creates a new stream that will skip the n first items of the +underlying stream. Read more
§

fn skip_while<F>(self, f: F) -> SkipWhile<Self, F>
where + F: FnMut(&Self::Item) -> bool, + Self: Sized,

Skip elements from the underlying stream while the provided predicate +resolves to true. Read more
§

fn all<F>(&mut self, f: F) -> AllFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> bool,

Tests if every element of the stream matches a predicate. Read more
§

fn any<F>(&mut self, f: F) -> AnyFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> bool,

Tests if any element of the stream matches a predicate. Read more
§

fn chain<U>(self, other: U) -> Chain<Self, U>
where + U: Stream<Item = Self::Item>, + Self: Sized,

Combine two streams into one by first returning all values from the +first stream then all values from the second stream. Read more
§

fn fold<B, F>(self, init: B, f: F) -> FoldFuture<Self, B, F>
where + Self: Sized, + F: FnMut(B, Self::Item) -> B,

A combinator that applies a function to every element in a stream +producing a single, final value. Read more
§

fn collect<T>(self) -> Collect<Self, T>
where + T: FromStream<Self::Item>, + Self: Sized,

Drain stream pushing all emitted values into a collection. Read more
§

fn timeout(self, duration: Duration) -> Timeout<Self>
where + Self: Sized,

Applies a per-item timeout to the passed stream. Read more
§

fn timeout_repeating(self, interval: Interval) -> TimeoutRepeating<Self>
where + Self: Sized,

Applies a per-item timeout to the passed stream. Read more
§

fn throttle(self, duration: Duration) -> Throttle<Self>
where + Self: Sized,

Slows down a stream by enforcing a delay between items. Read more
§

fn chunks_timeout( + self, + max_size: usize, + duration: Duration, +) -> ChunksTimeout<Self>
where + Self: Sized,

Batches the items in the given stream using a maximum duration and size for each batch. Read more
§

fn peekable(self) -> Peekable<Self>
where + Self: Sized,

Turns the stream into a peekable stream, whose next element can be peeked at without being +consumed. Read more
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.ApplicationClose.html b/pr/672/docs/noq/struct.ApplicationClose.html new file mode 100644 index 000000000..e13b4bff3 --- /dev/null +++ b/pr/672/docs/noq/struct.ApplicationClose.html @@ -0,0 +1,37 @@ +ApplicationClose in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.BloomTokenLog.html b/pr/672/docs/noq/struct.BloomTokenLog.html new file mode 100644 index 000000000..c7937e4fa --- /dev/null +++ b/pr/672/docs/noq/struct.BloomTokenLog.html @@ -0,0 +1,47 @@ +BloomTokenLog in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.Chunk.html b/pr/672/docs/noq/struct.Chunk.html new file mode 100644 index 000000000..036e1b9f0 --- /dev/null +++ b/pr/672/docs/noq/struct.Chunk.html @@ -0,0 +1,34 @@ +Chunk in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.ClientConfig.html b/pr/672/docs/noq/struct.ClientConfig.html new file mode 100644 index 000000000..21299ee51 --- /dev/null +++ b/pr/672/docs/noq/struct.ClientConfig.html @@ -0,0 +1,48 @@ +ClientConfig in noq - Rust

ClientConfig

Struct ClientConfig 

Source
#[non_exhaustive]
pub struct ClientConfig { /* private fields */ }
Expand description

Configuration for outgoing connections

+

Default values should be suitable for most internet applications.

+

Implementations§

Source§

impl ClientConfig

Source

pub fn new(crypto: Arc<dyn ClientConfig>) -> ClientConfig

Create a default config with a particular cryptographic config

+
Source

pub fn initial_dst_cid_provider( + &mut self, + initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>, +) -> &mut ClientConfig

Configure how to populate the destination CID of the initial packet when attempting to +establish a new connection

+

By default, it’s populated with random bytes with reasonable length, so unless you have +a good reason, you do not need to change it.

+

When prefer to override the default, please note that the generated connection ID MUST be +at least 8 bytes long and unpredictable, as per section 7.2 of RFC 9000.

+
Source

pub fn transport_config( + &mut self, + transport: Arc<TransportConfig>, +) -> &mut ClientConfig

Set a custom TransportConfig

+
Source

pub fn token_store(&mut self, store: Arc<dyn TokenStore>) -> &mut ClientConfig

Set a custom TokenStore

+

Defaults to TokenMemoryCache, which is suitable for most internet applications.

+
Source

pub fn version(&mut self, version: u32) -> &mut ClientConfig

Set the QUIC version to use

+
Source§

impl ClientConfig

Source

pub fn try_with_platform_verifier() -> Result<ClientConfig, Error>

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

+
Source

pub fn with_root_certificates( + roots: Arc<RootCertStore>, +) -> Result<ClientConfig, VerifierBuilderError>

Create a client configuration that trusts specified trust anchors

+

Trait Implementations§

Source§

impl Clone for ClientConfig

Source§

fn clone(&self) -> ClientConfig

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for ClientConfig

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.Closed.html b/pr/672/docs/noq/struct.Closed.html new file mode 100644 index 000000000..e8c24dfa7 --- /dev/null +++ b/pr/672/docs/noq/struct.Closed.html @@ -0,0 +1,37 @@ +Closed in noq - Rust

Closed

Struct Closed 

Source
#[non_exhaustive]
pub struct Closed { + pub reason: ConnectionError, + pub stats: ConnectionStats, + pub path_stats: Vec<(PathId, PathStats)>, +}
Expand description

State of a Connection at the moment it was closed.

+

Returned by the OnClosed future from Connection::on_closed.

+

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.
§reason: ConnectionError

The reason the connection was closed.

+
§stats: ConnectionStats

Aggregate connection statistics at the moment of close.

+
§path_stats: Vec<(PathId, PathStats)>

Per-path statistics for every path the connection knew about at close time.

+

This includes paths that haven’t been discarded at close time, plus any +already-discarded paths whose final stats had been retained because a Path +or WeakPathHandle handle was kept alive.

+

Trait Implementations§

Source§

impl Clone for Closed

Source§

fn clone(&self) -> Closed

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 Closed

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl !Freeze for Closed

§

impl !RefUnwindSafe for Closed

§

impl Send for Closed

§

impl Sync for Closed

§

impl Unpin for Closed

§

impl !UnwindSafe for Closed

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.ClosedPath.html b/pr/672/docs/noq/struct.ClosedPath.html new file mode 100644 index 000000000..4a73a79f0 --- /dev/null +++ b/pr/672/docs/noq/struct.ClosedPath.html @@ -0,0 +1,32 @@ +ClosedPath in noq - 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<(), Error>

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<(), Error>

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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.ClosedStream.html b/pr/672/docs/noq/struct.ClosedStream.html new file mode 100644 index 000000000..23ec58b77 --- /dev/null +++ b/pr/672/docs/noq/struct.ClosedStream.html @@ -0,0 +1,32 @@ +ClosedStream in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.Connecting.html b/pr/672/docs/noq/struct.Connecting.html new file mode 100644 index 000000000..db197a0ae --- /dev/null +++ b/pr/672/docs/noq/struct.Connecting.html @@ -0,0 +1,100 @@ +Connecting in noq - 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 noq_udp::RecvMeta::dst_ip for a list of +supported platforms when using noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

The type 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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.Connection.html b/pr/672/docs/noq/struct.Connection.html new file mode 100644 index 000000000..446185fd5 --- /dev/null +++ b/pr/672/docs/noq/struct.Connection.html @@ -0,0 +1,222 @@ +Connection in noq - 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, + network_path: impl Into<FourTuple>, + initial_status: PathStatus, +) -> OpenPath

Opens a new path if no path exists yet for network_path.

+

If network_path has no local IP set, then this will open a new path +if no path exists for this remote address, independent of the existing +path’s local IP. If a local IP is set, it will match against the full +four-tuple of existing paths.

+

Otherwise behaves exactly as open_path.

+
Source

pub fn open_path( + &self, + network_path: impl Into<FourTuple>, + initial_status: PathStatus, +) -> OpenPath

Opens an additional path if the multipath extension is negotiated.

+

This function takes a FourTuple, which contains the remote address and an optional +local IP. If the local IP is set, the path will be opened with this source address, +and the endpoint must support sending from that IP address. You can also pass a +SocketAddr to only set the remote address and leave the local IP interface unspecified.

+

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::Established 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) -> PathEvents

A stream of PathEvents for all paths in this connection.

+

The stream will yield a PathEvent whenever there is a change in the state of any path in this connection. +The events need to be processed immediately, since there isn’t an unbounded buffer for them.

+

If processing of events lags behind too much, you will get an error of type crate::Lagged indicating +how many events were lost. The stream continues after a lag, delivering the oldest retained message next.

+
Source

pub fn nat_traversal_updates(&self) -> NatTraversalUpdates

A stream of NAT traversal updates for this connection.

+

The events need to be processed immediately, since there isn’t an unbounded buffer for them.

+

If processing of events lags behind too much, you will get an error of type crate::Lagged indicating +how many events were lost. The stream continues after a lag, delivering the oldest retained message next.

+
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 Closed struct +describing the close reason and final connection and per-path statistics.

+

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>

Whether the connection is closed, and why.

+

The close_reason is always set to Some(ConnectionError) when a socket is +closed; whether it was closed manually by calling Connection::close() or due to +an internal error (such as an idle timeout or the peer closing the +connection).

+

Note: when the connection is closed, connection.close_reason().is_some() will always be true.

+
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 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) -> ObservedExternalAddr

Track changes 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.ConnectionClose.html b/pr/672/docs/noq/struct.ConnectionClose.html new file mode 100644 index 000000000..f99d64e9e --- /dev/null +++ b/pr/672/docs/noq/struct.ConnectionClose.html @@ -0,0 +1,39 @@ +ConnectionClose in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.ConnectionId.html b/pr/672/docs/noq/struct.ConnectionId.html new file mode 100644 index 000000000..9a15e5d92 --- /dev/null +++ b/pr/672/docs/noq/struct.ConnectionId.html @@ -0,0 +1,2468 @@ +ConnectionId in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.ConnectionStats.html b/pr/672/docs/noq/struct.ConnectionStats.html new file mode 100644 index 000000000..31792da81 --- /dev/null +++ b/pr/672/docs/noq/struct.ConnectionStats.html @@ -0,0 +1,41 @@ +ConnectionStats in noq - 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, + pub lost_packets: u64, + pub lost_bytes: u64, +}
Expand description

Connection statistics.

+

The fields here are a sum of the respective fields in the PathStats for all the +paths that exist as well as all paths that previously existed.

+

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 the connection.

+
§udp_rx: UdpStats

Statistics about UDP datagrams received on the connection.

+
§frame_tx: FrameStats

Statistics about frames transmitted on the connection.

+
§frame_rx: FrameStats

Statistics about frames received on the connection.

+
§lost_packets: u64

The number of packets lost on the connection.

+
§lost_bytes: u64

The number of bytes lost on the connection.

+

Trait Implementations§

Source§

impl Add<PathStats> for ConnectionStats

Source§

type Output = ConnectionStats

The resulting type after applying the + operator.
Source§

fn add(self, rhs: PathStats) -> <ConnectionStats as Add<PathStats>>::Output

Performs the + operation. Read more
Source§

impl AddAssign<PathStats> for ConnectionStats

Source§

fn add_assign(&mut self, rhs: PathStats)

Performs the += operation. Read more
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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.DecryptedInitial.html b/pr/672/docs/noq/struct.DecryptedInitial.html new file mode 100644 index 000000000..fa9b5f3ee --- /dev/null +++ b/pr/672/docs/noq/struct.DecryptedInitial.html @@ -0,0 +1,30 @@ +DecryptedInitial in noq - Rust

DecryptedInitial

Struct DecryptedInitial 

Source
pub struct DecryptedInitial(/* private fields */);
Expand description

Decrypted payload of a QUIC Initial packet

+

Obtained via Incoming::decrypt. Can be used to extract information from +the TLS ClientHello without completing the handshake.

+

Implementations§

Source§

impl DecryptedInitial

Source

pub fn alpns(&self) -> Option<IncomingAlpns>

Best-effort extraction of the ALPN protocols from the TLS ClientHello

+

Parses the CRYPTO frames to extract the ALPN extension. This is intended +for routing and filtering; it is not guaranteed to succeed if the +ClientHello spans multiple packets. Returns None if parsing fails.

+

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.Endpoint.html b/pr/672/docs/noq/struct.Endpoint.html new file mode 100644 index 000000000..9ce939137 --- /dev/null +++ b/pr/672/docs/noq/struct.Endpoint.html @@ -0,0 +1,130 @@ +Endpoint in noq - 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:

+ +
noq::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

+

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 clients. As such, calling Endpoint::server with +the address [::]:0 is a reasonable default to maximize the ability to accept connections +from any address.

+

Some environments may not allow creation of dual-stack sockets, in which case an IPv6 +server will only be able to accept connections from IPv6 clients. An IPv4 server is never +dual-stack.

+
Source

pub fn new( + config: EndpointConfig, + server_config: Option<ServerConfig>, + socket: UdpSocket, + runtime: Arc<dyn Runtime>, +) -> Result<Self>

Construct an endpoint with arbitrary configuration and socket

+
Source

pub fn new_with_abstract_socket( + config: EndpointConfig, + server_config: Option<ServerConfig>, + socket: Box<dyn AsyncUdpSocket>, + runtime: Arc<dyn Runtime>, +) -> Result<Self>

Construct an endpoint with arbitrary configuration and pre-constructed abstract socket

+

Useful when socket has additional state (e.g. sidechannels) attached for which shared +ownership is needed.

+
Source

pub fn accept(&self) -> Accept<'_>

Get the next incoming connection attempt from a client

+

Yields Incomings, or None if the endpoint is closed. Incoming +can be awaited to obtain the final Connection, or used to e.g. +filter connection attempts or force address validation, or converted into an intermediate +Connecting future which can be used to e.g. send 0.5-RTT data.

+
Source

pub fn set_default_client_config(&self, config: ClientConfig)

Set the client configuration used by connect

+
Source

pub fn connect( + &self, + addr: SocketAddr, + server_name: &str, +) -> Result<Connecting, ConnectError>

Connect to a remote endpoint

+

server_name must be covered by the certificate presented by the server. This prevents a +connection from being intercepted by an attacker with a valid certificate for some other +server.

+

May fail immediately due to configuration errors, or in the future if the connection could +not be established.

+
Source

pub fn connect_with( + &self, + config: ClientConfig, + addr: SocketAddr, + server_name: &str, +) -> Result<Connecting, ConnectError>

Connect to a remote endpoint using a custom configuration.

+

See connect() for details.

+
Source

pub fn rebind(&self, socket: UdpSocket) -> Result<()>

Switch to a new UDP socket

+

See Endpoint::rebind_abstract() for details.

+
Source

pub fn rebind_abstract(&self, socket: Box<dyn AsyncUdpSocket>) -> Result<()>

Switch to a new UDP socket

+

Allows the endpoint’s address to be updated live, affecting all active connections. Incoming +connections and connections to servers unreachable from the new address will be lost.

+

On error, the old UDP socket is retained.

+
Source

pub fn handle_network_change( + &self, + hint: Option<Arc<dyn NetworkChangeHint + Sync + Send>>, +)

Notify connections that the local network address has changed.

+

This informs all active connections that the local address may have changed (e.g., due to a +network interface change), triggering liveness checks and recovery procedures on each +connection without requiring a socket rebind.

+

Unlike Self::rebind, this does not change the underlying socket. Use this when the +network topology changes but the socket remains valid (e.g., if bound to the unspecified +address and switching from WiFi to cellular, or when the local IP address changes).

+

The optional hint allows callers to indicate which paths may still be recoverable after +the network change. If None, all paths are assumed to be non-recoverable. For client-side +multipath connections, unrecoverable paths will be closed and replaced with new paths to +the same remote addresses.

+
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)

Waits for all connections on the endpoint to be cleanly shut down and drained.

+

This is equivalent to wait_all_draining() with additionally waiting for the connections to be +drained. Please see its documentation for more information.

+

Use wait_idle() in favor of wait_all_draining() if you care about waiting for the +Connection structs to be dropped.

+
Source

pub async fn wait_all_draining(&self)

Waits for all connections on the endpoint to be ready for shutting 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.

+

Unlike wait_idle(), this doesn’t wait for the full draining period, so it can’t be +used to wait for all now-idle Connections to be dropped.

+

See also this section in the QUIC RFC: https://datatracker.ietf.org/doc/html/rfc9000#section-10.2-6

+

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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.EndpointConfig.html b/pr/672/docs/noq/struct.EndpointConfig.html new file mode 100644 index 000000000..525a6f015 --- /dev/null +++ b/pr/672/docs/noq/struct.EndpointConfig.html @@ -0,0 +1,68 @@ +EndpointConfig in noq - 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( + &mut self, + factory: Arc<dyn Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync>, +) -> &mut EndpointConfig

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, noq 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 noq +deterministically or if you are using noq 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.EndpointStats.html b/pr/672/docs/noq/struct.EndpointStats.html new file mode 100644 index 000000000..5724c0abb --- /dev/null +++ b/pr/672/docs/noq/struct.EndpointStats.html @@ -0,0 +1,35 @@ +EndpointStats in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.FourTuple.html b/pr/672/docs/noq/struct.FourTuple.html new file mode 100644 index 000000000..8415aa746 --- /dev/null +++ b/pr/672/docs/noq/struct.FourTuple.html @@ -0,0 +1,44 @@ +FourTuple in noq - Rust

FourTuple

Struct FourTuple 

Source
pub struct FourTuple { /* private fields */ }
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.

+

FourTuple implements From<SocketAddr>, which expands to Self::from_remote.

+

Implementations§

Source§

impl FourTuple

Source

pub fn new(remote: SocketAddr, local_ip: Option<IpAddr>) -> FourTuple

Creates a new FourTuple.

+
Source

pub fn from_remote(remote: SocketAddr) -> FourTuple

Creates a new FourTuple without a known local address.

+
Source

pub fn remote(&self) -> SocketAddr

Returns the remote address of the network path.

+
Source

pub fn local_ip(&self) -> Option<IpAddr>

Returns the local address of the network path.

+

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<(), Error>

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

impl Display for FourTuple

Source§

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

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

impl From<SocketAddr> for FourTuple

Source§

fn from(value: SocketAddr) -> FourTuple

Converts to this type from the input type.
Source§

impl Hash for FourTuple

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 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.FrameStats.html b/pr/672/docs/noq/struct.FrameStats.html new file mode 100644 index 000000000..d9fe495e1 --- /dev/null +++ b/pr/672/docs/noq/struct.FrameStats.html @@ -0,0 +1,69 @@ +FrameStats in noq - 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 Add for FrameStats

Source§

type Output = FrameStats

The resulting type after applying the + operator.
Source§

fn add(self, __rhs: FrameStats) -> <FrameStats as Add>::Output

Performs the + operation. Read more
Source§

impl AddAssign for FrameStats

Source§

fn add_assign(&mut self, __rhs: FrameStats)

Performs the += operation. Read more
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 PartialEq for FrameStats

Source§

fn eq(&self, other: &FrameStats) -> 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 FrameStats

Source§

impl Eq for FrameStats

Source§

impl StructuralPartialEq 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
§

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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.IdleTimeout.html b/pr/672/docs/noq/struct.IdleTimeout.html new file mode 100644 index 000000000..873dea7d8 --- /dev/null +++ b/pr/672/docs/noq/struct.IdleTimeout.html @@ -0,0 +1,51 @@ +IdleTimeout in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.Incoming.html b/pr/672/docs/noq/struct.Incoming.html new file mode 100644 index 000000000..3f96ee9ac --- /dev/null +++ b/pr/672/docs/noq/struct.Incoming.html @@ -0,0 +1,48 @@ +Incoming in noq - 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

+
Source

pub fn decrypt(&self) -> Option<DecryptedInitial>

Decrypt the Initial packet payload

+

This clones and decrypts the packet payload (~1200 bytes). +Can be used to extract information from the TLS ClientHello without completing the handshake.

+

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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.IncomingFuture.html b/pr/672/docs/noq/struct.IncomingFuture.html new file mode 100644 index 000000000..b7adf40a4 --- /dev/null +++ b/pr/672/docs/noq/struct.IncomingFuture.html @@ -0,0 +1,52 @@ +IncomingFuture in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

The type 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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

InvalidCid

Struct InvalidCid 

Source
pub struct InvalidCid;
Expand description

The connection ID was not recognized by the ConnectionIdGenerator

+

Trait Implementations§

Source§

impl Clone for InvalidCid

Source§

fn clone(&self) -> InvalidCid

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for InvalidCid

Source§

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

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

impl Copy for InvalidCid

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.Lagged.html b/pr/672/docs/noq/struct.Lagged.html new file mode 100644 index 000000000..645120f7f --- /dev/null +++ b/pr/672/docs/noq/struct.Lagged.html @@ -0,0 +1,34 @@ +Lagged in noq - Rust

Lagged

Struct Lagged 

Source
pub struct Lagged(pub u64);
Expand description

The receiver lagged too far behind.

+

Attempting to receive again will return the oldest message still retained +by the channel.

+

Tuple Fields§

§0: u64

Trait Implementations§

Source§

impl Clone for Lagged

Source§

fn clone(&self) -> Lagged

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 Lagged

Source§

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

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

impl Display for Lagged

Source§

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

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

impl Error for Lagged

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 Lagged

Source§

fn eq(&self, other: &Lagged) -> 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 Lagged

Source§

impl StructuralPartialEq for Lagged

Auto Trait Implementations§

§

impl Freeze for Lagged

§

impl RefUnwindSafe for Lagged

§

impl Send for Lagged

§

impl Sync for Lagged

§

impl Unpin for Lagged

§

impl UnwindSafe for Lagged

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.MtuDiscoveryConfig.html b/pr/672/docs/noq/struct.MtuDiscoveryConfig.html new file mode 100644 index 000000000..451766089 --- /dev/null +++ b/pr/672/docs/noq/struct.MtuDiscoveryConfig.html @@ -0,0 +1,87 @@ +MtuDiscoveryConfig in noq - 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

+

noq 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), noq 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.NatTraversalUpdates.html b/pr/672/docs/noq/struct.NatTraversalUpdates.html new file mode 100644 index 000000000..89ac28187 --- /dev/null +++ b/pr/672/docs/noq/struct.NatTraversalUpdates.html @@ -0,0 +1,174 @@ +NatTraversalUpdates in noq - Rust

NatTraversalUpdates

Struct NatTraversalUpdates 

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

A stream of NAT traversal updates for a connection.

+

Trait Implementations§

Source§

impl Debug for NatTraversalUpdates

Source§

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

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

impl Stream for NatTraversalUpdates

Source§

type Item = Result<Event, Lagged>

Values yielded by the stream.
Source§

fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, +) -> Poll<Option<Self::Item>>

Attempt to pull out the next value of this stream, registering the +current task for wakeup if the value is not yet available, and returning +None if the stream is exhausted. Read more
§

fn size_hint(&self) -> (usize, Option<usize>)

Returns the bounds on the remaining length of the stream. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<S> StreamExt for S
where + S: Stream + ?Sized,

§

fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>
where + Self: Unpin,

A convenience for calling [Stream::poll_next()] on !Unpin types.
§

fn next(&mut self) -> NextFuture<'_, Self>
where + Self: Unpin,

Retrieves the next item in the stream. Read more
§

fn try_next<T, E>(&mut self) -> TryNextFuture<'_, Self>
where + Self: Stream<Item = Result<T, E>> + Unpin,

Retrieves the next item in the stream. Read more
§

fn count(self) -> CountFuture<Self>
where + Self: Sized,

Counts the number of items in the stream. Read more
§

fn map<T, F>(self, f: F) -> Map<Self, F>
where + Self: Sized, + F: FnMut(Self::Item) -> T,

Maps items of the stream to new values using a closure. Read more
§

fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>
where + Self: Sized, + U: Stream, + F: FnMut(Self::Item) -> U,

Maps items to streams and then concatenates them. Read more
§

fn flatten(self) -> Flatten<Self>
where + Self: Sized, + Self::Item: Stream,

Concatenates inner streams. Read more
§

fn then<F, Fut>(self, f: F) -> Then<Self, F, Fut>
where + Self: Sized, + F: FnMut(Self::Item) -> Fut, + Fut: Future,

Maps items of the stream to new values using an async closure. Read more
§

fn filter<P>(self, predicate: P) -> Filter<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Keeps items of the stream for which predicate returns true. Read more
§

fn filter_map<T, F>(self, f: F) -> FilterMap<Self, F>
where + Self: Sized, + F: FnMut(Self::Item) -> Option<T>,

Filters and maps items of the stream using a closure. Read more
§

fn take(self, n: usize) -> Take<Self>
where + Self: Sized,

Takes only the first n items of the stream. Read more
§

fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Takes items while predicate returns true. Read more
§

fn map_while<B, P>(self, predicate: P) -> MapWhile<Self, P>
where + Self: Sized, + P: FnMut(Self::Item) -> Option<B>,

Maps items while predicate returns Some. Read more
§

fn skip(self, n: usize) -> Skip<Self>
where + Self: Sized,

Skips the first n items of the stream. Read more
§

fn skip_while<P>(self, predicate: P) -> SkipWhile<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Skips items while predicate returns true. Read more
§

fn step_by(self, step: usize) -> StepBy<Self>
where + Self: Sized,

Yields every stepth item. Read more
§

fn chain<U>(self, other: U) -> Chain<Self, U>
where + Self: Sized, + U: Stream<Item = Self::Item>,

Appends another stream to the end of this one. Read more
§

fn cloned<'a, T>(self) -> Cloned<Self>
where + Self: Sized + Stream<Item = &'a T>, + T: Clone + 'a,

Clones all items. Read more
§

fn copied<'a, T>(self) -> Copied<Self>
where + Self: Sized + Stream<Item = &'a T>, + T: Copy + 'a,

Copies all items. Read more
§

fn collect<C>(self) -> CollectFuture<Self, C>
where + Self: Sized, + C: Default + Extend<Self::Item>,

Collects all items in the stream into a collection. Read more
§

fn try_collect<T, E, C>(self) -> TryCollectFuture<Self, C>
where + Self: Sized + Stream<Item = Result<T, E>>, + C: Default + Extend<T>,

Collects all items in the fallible stream into a collection. Read more
§

fn partition<B, P>(self, predicate: P) -> PartitionFuture<Self, P, B>
where + Self: Sized, + B: Default + Extend<Self::Item>, + P: FnMut(&Self::Item) -> bool,

Partitions items into those for which predicate is true and those for which it is +false, and then collects them into two collections. Read more
§

fn fold<T, F>(self, init: T, f: F) -> FoldFuture<Self, F, T>
where + Self: Sized, + F: FnMut(T, Self::Item) -> T,

Accumulates a computation over the stream. Read more
§

fn try_fold<T, E, F, B>( + &mut self, + init: B, + f: F, +) -> TryFoldFuture<'_, Self, F, B>
where + Self: Sized + Stream<Item = Result<T, E>> + Unpin, + F: FnMut(B, T) -> Result<B, E>,

Accumulates a fallible computation over the stream. Read more
§

fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>
where + Self: Sized, + F: FnMut(&mut St, Self::Item) -> Option<B>,

Maps items of the stream to new values using a state value and a closure. Read more
§

fn fuse(self) -> Fuse<Self>
where + Self: Sized,

Fuses the stream so that it stops yielding items after the first None. Read more
§

fn cycle(self) -> Cycle<Self>
where + Self: Sized + Clone,

Repeats the stream from beginning to end, forever. Read more
§

fn enumerate(self) -> Enumerate<Self>
where + Self: Sized,

Enumerates items, mapping them to (index, item). Read more
§

fn inspect<F>(self, f: F) -> Inspect<Self, F>
where + Self: Sized, + F: FnMut(&Self::Item),

Calls a closure on each item and passes it on. Read more
§

fn nth(&mut self, n: usize) -> NthFuture<'_, Self>
where + Self: Unpin,

Gets the nth item of the stream. Read more
§

fn last(self) -> LastFuture<Self>
where + Self: Sized,

Returns the last item in the stream. Read more
§

fn find<P>(&mut self, predicate: P) -> FindFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(&Self::Item) -> bool,

Finds the first item of the stream for which predicate returns true. Read more
§

fn find_map<F, B>(&mut self, f: F) -> FindMapFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> Option<B>,

Applies a closure to items in the stream and returns the first Some result. Read more
§

fn position<P>(&mut self, predicate: P) -> PositionFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(Self::Item) -> bool,

Finds the index of the first item of the stream for which predicate returns true. Read more
§

fn all<P>(&mut self, predicate: P) -> AllFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(Self::Item) -> bool,

Tests if predicate returns true for all items in the stream. Read more
§

fn any<P>(&mut self, predicate: P) -> AnyFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(Self::Item) -> bool,

Tests if predicate returns true for any item in the stream. Read more
§

fn for_each<F>(self, f: F) -> ForEachFuture<Self, F>
where + Self: Sized, + F: FnMut(Self::Item),

Calls a closure on each item of the stream. Read more
§

fn try_for_each<F, E>(&mut self, f: F) -> TryForEachFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> Result<(), E>,

Calls a fallible closure on each item of the stream, stopping on first error. Read more
§

fn zip<U>(self, other: U) -> Zip<Self, U>
where + Self: Sized, + U: Stream,

Zips up two streams into a single stream of pairs. Read more
§

fn unzip<A, B, FromA, FromB>(self) -> UnzipFuture<Self, FromA, FromB>
where + FromA: Default + Extend<A>, + FromB: Default + Extend<B>, + Self: Sized + Stream<Item = (A, B)>,

Collects a stream of pairs into a pair of collections. Read more
§

fn or<S>(self, other: S) -> Or<Self, S>
where + Self: Sized, + S: Stream<Item = Self::Item>,

Merges with other stream, preferring items from self whenever both streams are ready. Read more
§

fn race<S>(self, other: S) -> Race<Self, S>
where + Self: Sized, + S: Stream<Item = Self::Item>,

Merges with other stream, with no preference for either stream when both are ready. Read more
§

fn drain(&mut self) -> Drain<'_, Self>

Yields all immediately available values from a stream. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Stream<Item = Self::Item> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the stream and changes its type to dyn Stream + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Stream<Item = Self::Item> + 'a>>
where + Self: Sized + 'a,

Boxes the stream and changes its type to dyn Stream + 'a. Read more
§

impl<St> StreamExt for St
where + St: Stream + ?Sized,

§

fn next(&mut self) -> Next<'_, Self>
where + Self: Unpin,

Consumes and returns the next value in the stream or None if the +stream is finished. Read more
§

fn try_next<T, E>(&mut self) -> TryNext<'_, Self>
where + Self: Stream<Item = Result<T, E>> + Unpin,

Consumes and returns the next item in the stream. If an error is +encountered before the next item, the error is returned instead. Read more
§

fn map<T, F>(self, f: F) -> Map<Self, F>
where + F: FnMut(Self::Item) -> T, + Self: Sized,

Maps this stream’s items to a different type, returning a new stream of +the resulting type. Read more
§

fn map_while<T, F>(self, f: F) -> MapWhile<Self, F>
where + F: FnMut(Self::Item) -> Option<T>, + Self: Sized,

Map this stream’s items to a different type for as long as determined by +the provided closure. A stream of the target type will be returned, +which will yield elements until the closure returns None. Read more
§

fn then<F, Fut>(self, f: F) -> Then<Self, Fut, F>
where + F: FnMut(Self::Item) -> Fut, + Fut: Future, + Self: Sized,

Maps this stream’s items asynchronously to a different type, returning a +new stream of the resulting type. Read more
§

fn merge<U>(self, other: U) -> Merge<Self, U>
where + U: Stream<Item = Self::Item>, + Self: Sized,

Combine two streams into one by interleaving the output of both as it +is produced. Read more
§

fn filter<F>(self, f: F) -> Filter<Self, F>
where + F: FnMut(&Self::Item) -> bool, + Self: Sized,

Filters the values produced by this stream according to the provided +predicate. Read more
§

fn filter_map<T, F>(self, f: F) -> FilterMap<Self, F>
where + F: FnMut(Self::Item) -> Option<T>, + Self: Sized,

Filters the values produced by this stream while simultaneously mapping +them to a different type according to the provided closure. Read more
§

fn fuse(self) -> Fuse<Self>
where + Self: Sized,

Creates a stream which ends after the first None. Read more
§

fn take(self, n: usize) -> Take<Self>
where + Self: Sized,

Creates a new stream of at most n items of the underlying stream. Read more
§

fn take_while<F>(self, f: F) -> TakeWhile<Self, F>
where + F: FnMut(&Self::Item) -> bool, + Self: Sized,

Take elements from this stream while the provided predicate +resolves to true. Read more
§

fn skip(self, n: usize) -> Skip<Self>
where + Self: Sized,

Creates a new stream that will skip the n first items of the +underlying stream. Read more
§

fn skip_while<F>(self, f: F) -> SkipWhile<Self, F>
where + F: FnMut(&Self::Item) -> bool, + Self: Sized,

Skip elements from the underlying stream while the provided predicate +resolves to true. Read more
§

fn all<F>(&mut self, f: F) -> AllFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> bool,

Tests if every element of the stream matches a predicate. Read more
§

fn any<F>(&mut self, f: F) -> AnyFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> bool,

Tests if any element of the stream matches a predicate. Read more
§

fn chain<U>(self, other: U) -> Chain<Self, U>
where + U: Stream<Item = Self::Item>, + Self: Sized,

Combine two streams into one by first returning all values from the +first stream then all values from the second stream. Read more
§

fn fold<B, F>(self, init: B, f: F) -> FoldFuture<Self, B, F>
where + Self: Sized, + F: FnMut(B, Self::Item) -> B,

A combinator that applies a function to every element in a stream +producing a single, final value. Read more
§

fn collect<T>(self) -> Collect<Self, T>
where + T: FromStream<Self::Item>, + Self: Sized,

Drain stream pushing all emitted values into a collection. Read more
§

fn timeout(self, duration: Duration) -> Timeout<Self>
where + Self: Sized,

Applies a per-item timeout to the passed stream. Read more
§

fn timeout_repeating(self, interval: Interval) -> TimeoutRepeating<Self>
where + Self: Sized,

Applies a per-item timeout to the passed stream. Read more
§

fn throttle(self, duration: Duration) -> Throttle<Self>
where + Self: Sized,

Slows down a stream by enforcing a delay between items. Read more
§

fn chunks_timeout( + self, + max_size: usize, + duration: Duration, +) -> ChunksTimeout<Self>
where + Self: Sized,

Batches the items in the given stream using a maximum duration and size for each batch. Read more
§

fn peekable(self) -> Peekable<Self>
where + Self: Sized,

Turns the stream into a peekable stream, whose next element can be peeked at without being +consumed. Read more
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

impl<S, T, E> TryStream for S
where + S: Stream<Item = 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_next( + self: Pin<&mut S>, + cx: &mut Context<'_>, +) -> Poll<Option<Result<<S as TryStream>::Ok, <S as TryStream>::Error>>>

Poll this TryStream as if it were a Stream. Read more
§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.NoneTokenLog.html b/pr/672/docs/noq/struct.NoneTokenLog.html new file mode 100644 index 000000000..adfff5de5 --- /dev/null +++ b/pr/672/docs/noq/struct.NoneTokenLog.html @@ -0,0 +1,30 @@ +NoneTokenLog in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.NoneTokenStore.html b/pr/672/docs/noq/struct.NoneTokenStore.html new file mode 100644 index 000000000..474592733 --- /dev/null +++ b/pr/672/docs/noq/struct.NoneTokenStore.html @@ -0,0 +1,24 @@ +NoneTokenStore in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.ObservedExternalAddr.html b/pr/672/docs/noq/struct.ObservedExternalAddr.html new file mode 100644 index 000000000..63d343347 --- /dev/null +++ b/pr/672/docs/noq/struct.ObservedExternalAddr.html @@ -0,0 +1,175 @@ +ObservedExternalAddr in noq - Rust

ObservedExternalAddr

Struct ObservedExternalAddr 

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

Watches the external address reported by the peer for this connection.

+

Implements [Stream] yielding SocketAddr on each change. +Use get to read the current value without waiting.

+

Implementations§

Source§

impl ObservedExternalAddr

Source

pub fn get(&self) -> Option<SocketAddr>

Returns the most recently observed external address.

+

None is returned if the peer has not yet reported an address. Retains +the last value even after the stream is closed.

+

Trait Implementations§

Source§

impl Debug for ObservedExternalAddr

Source§

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

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

impl Stream for ObservedExternalAddr

Source§

type Item = SocketAddr

Values yielded by the stream.
Source§

fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, +) -> Poll<Option<Self::Item>>

Attempt to pull out the next value of this stream, registering the +current task for wakeup if the value is not yet available, and returning +None if the stream is exhausted. Read more
§

fn size_hint(&self) -> (usize, Option<usize>)

Returns the bounds on the remaining length of the stream. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<S> StreamExt for S
where + S: Stream + ?Sized,

§

fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>
where + Self: Unpin,

A convenience for calling [Stream::poll_next()] on !Unpin types.
§

fn next(&mut self) -> NextFuture<'_, Self>
where + Self: Unpin,

Retrieves the next item in the stream. Read more
§

fn try_next<T, E>(&mut self) -> TryNextFuture<'_, Self>
where + Self: Stream<Item = Result<T, E>> + Unpin,

Retrieves the next item in the stream. Read more
§

fn count(self) -> CountFuture<Self>
where + Self: Sized,

Counts the number of items in the stream. Read more
§

fn map<T, F>(self, f: F) -> Map<Self, F>
where + Self: Sized, + F: FnMut(Self::Item) -> T,

Maps items of the stream to new values using a closure. Read more
§

fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>
where + Self: Sized, + U: Stream, + F: FnMut(Self::Item) -> U,

Maps items to streams and then concatenates them. Read more
§

fn flatten(self) -> Flatten<Self>
where + Self: Sized, + Self::Item: Stream,

Concatenates inner streams. Read more
§

fn then<F, Fut>(self, f: F) -> Then<Self, F, Fut>
where + Self: Sized, + F: FnMut(Self::Item) -> Fut, + Fut: Future,

Maps items of the stream to new values using an async closure. Read more
§

fn filter<P>(self, predicate: P) -> Filter<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Keeps items of the stream for which predicate returns true. Read more
§

fn filter_map<T, F>(self, f: F) -> FilterMap<Self, F>
where + Self: Sized, + F: FnMut(Self::Item) -> Option<T>,

Filters and maps items of the stream using a closure. Read more
§

fn take(self, n: usize) -> Take<Self>
where + Self: Sized,

Takes only the first n items of the stream. Read more
§

fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Takes items while predicate returns true. Read more
§

fn map_while<B, P>(self, predicate: P) -> MapWhile<Self, P>
where + Self: Sized, + P: FnMut(Self::Item) -> Option<B>,

Maps items while predicate returns Some. Read more
§

fn skip(self, n: usize) -> Skip<Self>
where + Self: Sized,

Skips the first n items of the stream. Read more
§

fn skip_while<P>(self, predicate: P) -> SkipWhile<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Skips items while predicate returns true. Read more
§

fn step_by(self, step: usize) -> StepBy<Self>
where + Self: Sized,

Yields every stepth item. Read more
§

fn chain<U>(self, other: U) -> Chain<Self, U>
where + Self: Sized, + U: Stream<Item = Self::Item>,

Appends another stream to the end of this one. Read more
§

fn cloned<'a, T>(self) -> Cloned<Self>
where + Self: Sized + Stream<Item = &'a T>, + T: Clone + 'a,

Clones all items. Read more
§

fn copied<'a, T>(self) -> Copied<Self>
where + Self: Sized + Stream<Item = &'a T>, + T: Copy + 'a,

Copies all items. Read more
§

fn collect<C>(self) -> CollectFuture<Self, C>
where + Self: Sized, + C: Default + Extend<Self::Item>,

Collects all items in the stream into a collection. Read more
§

fn try_collect<T, E, C>(self) -> TryCollectFuture<Self, C>
where + Self: Sized + Stream<Item = Result<T, E>>, + C: Default + Extend<T>,

Collects all items in the fallible stream into a collection. Read more
§

fn partition<B, P>(self, predicate: P) -> PartitionFuture<Self, P, B>
where + Self: Sized, + B: Default + Extend<Self::Item>, + P: FnMut(&Self::Item) -> bool,

Partitions items into those for which predicate is true and those for which it is +false, and then collects them into two collections. Read more
§

fn fold<T, F>(self, init: T, f: F) -> FoldFuture<Self, F, T>
where + Self: Sized, + F: FnMut(T, Self::Item) -> T,

Accumulates a computation over the stream. Read more
§

fn try_fold<T, E, F, B>( + &mut self, + init: B, + f: F, +) -> TryFoldFuture<'_, Self, F, B>
where + Self: Sized + Stream<Item = Result<T, E>> + Unpin, + F: FnMut(B, T) -> Result<B, E>,

Accumulates a fallible computation over the stream. Read more
§

fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>
where + Self: Sized, + F: FnMut(&mut St, Self::Item) -> Option<B>,

Maps items of the stream to new values using a state value and a closure. Read more
§

fn fuse(self) -> Fuse<Self>
where + Self: Sized,

Fuses the stream so that it stops yielding items after the first None. Read more
§

fn cycle(self) -> Cycle<Self>
where + Self: Sized + Clone,

Repeats the stream from beginning to end, forever. Read more
§

fn enumerate(self) -> Enumerate<Self>
where + Self: Sized,

Enumerates items, mapping them to (index, item). Read more
§

fn inspect<F>(self, f: F) -> Inspect<Self, F>
where + Self: Sized, + F: FnMut(&Self::Item),

Calls a closure on each item and passes it on. Read more
§

fn nth(&mut self, n: usize) -> NthFuture<'_, Self>
where + Self: Unpin,

Gets the nth item of the stream. Read more
§

fn last(self) -> LastFuture<Self>
where + Self: Sized,

Returns the last item in the stream. Read more
§

fn find<P>(&mut self, predicate: P) -> FindFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(&Self::Item) -> bool,

Finds the first item of the stream for which predicate returns true. Read more
§

fn find_map<F, B>(&mut self, f: F) -> FindMapFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> Option<B>,

Applies a closure to items in the stream and returns the first Some result. Read more
§

fn position<P>(&mut self, predicate: P) -> PositionFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(Self::Item) -> bool,

Finds the index of the first item of the stream for which predicate returns true. Read more
§

fn all<P>(&mut self, predicate: P) -> AllFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(Self::Item) -> bool,

Tests if predicate returns true for all items in the stream. Read more
§

fn any<P>(&mut self, predicate: P) -> AnyFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(Self::Item) -> bool,

Tests if predicate returns true for any item in the stream. Read more
§

fn for_each<F>(self, f: F) -> ForEachFuture<Self, F>
where + Self: Sized, + F: FnMut(Self::Item),

Calls a closure on each item of the stream. Read more
§

fn try_for_each<F, E>(&mut self, f: F) -> TryForEachFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> Result<(), E>,

Calls a fallible closure on each item of the stream, stopping on first error. Read more
§

fn zip<U>(self, other: U) -> Zip<Self, U>
where + Self: Sized, + U: Stream,

Zips up two streams into a single stream of pairs. Read more
§

fn unzip<A, B, FromA, FromB>(self) -> UnzipFuture<Self, FromA, FromB>
where + FromA: Default + Extend<A>, + FromB: Default + Extend<B>, + Self: Sized + Stream<Item = (A, B)>,

Collects a stream of pairs into a pair of collections. Read more
§

fn or<S>(self, other: S) -> Or<Self, S>
where + Self: Sized, + S: Stream<Item = Self::Item>,

Merges with other stream, preferring items from self whenever both streams are ready. Read more
§

fn race<S>(self, other: S) -> Race<Self, S>
where + Self: Sized, + S: Stream<Item = Self::Item>,

Merges with other stream, with no preference for either stream when both are ready. Read more
§

fn drain(&mut self) -> Drain<'_, Self>

Yields all immediately available values from a stream. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Stream<Item = Self::Item> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the stream and changes its type to dyn Stream + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Stream<Item = Self::Item> + 'a>>
where + Self: Sized + 'a,

Boxes the stream and changes its type to dyn Stream + 'a. Read more
§

impl<St> StreamExt for St
where + St: Stream + ?Sized,

§

fn next(&mut self) -> Next<'_, Self>
where + Self: Unpin,

Consumes and returns the next value in the stream or None if the +stream is finished. Read more
§

fn try_next<T, E>(&mut self) -> TryNext<'_, Self>
where + Self: Stream<Item = Result<T, E>> + Unpin,

Consumes and returns the next item in the stream. If an error is +encountered before the next item, the error is returned instead. Read more
§

fn map<T, F>(self, f: F) -> Map<Self, F>
where + F: FnMut(Self::Item) -> T, + Self: Sized,

Maps this stream’s items to a different type, returning a new stream of +the resulting type. Read more
§

fn map_while<T, F>(self, f: F) -> MapWhile<Self, F>
where + F: FnMut(Self::Item) -> Option<T>, + Self: Sized,

Map this stream’s items to a different type for as long as determined by +the provided closure. A stream of the target type will be returned, +which will yield elements until the closure returns None. Read more
§

fn then<F, Fut>(self, f: F) -> Then<Self, Fut, F>
where + F: FnMut(Self::Item) -> Fut, + Fut: Future, + Self: Sized,

Maps this stream’s items asynchronously to a different type, returning a +new stream of the resulting type. Read more
§

fn merge<U>(self, other: U) -> Merge<Self, U>
where + U: Stream<Item = Self::Item>, + Self: Sized,

Combine two streams into one by interleaving the output of both as it +is produced. Read more
§

fn filter<F>(self, f: F) -> Filter<Self, F>
where + F: FnMut(&Self::Item) -> bool, + Self: Sized,

Filters the values produced by this stream according to the provided +predicate. Read more
§

fn filter_map<T, F>(self, f: F) -> FilterMap<Self, F>
where + F: FnMut(Self::Item) -> Option<T>, + Self: Sized,

Filters the values produced by this stream while simultaneously mapping +them to a different type according to the provided closure. Read more
§

fn fuse(self) -> Fuse<Self>
where + Self: Sized,

Creates a stream which ends after the first None. Read more
§

fn take(self, n: usize) -> Take<Self>
where + Self: Sized,

Creates a new stream of at most n items of the underlying stream. Read more
§

fn take_while<F>(self, f: F) -> TakeWhile<Self, F>
where + F: FnMut(&Self::Item) -> bool, + Self: Sized,

Take elements from this stream while the provided predicate +resolves to true. Read more
§

fn skip(self, n: usize) -> Skip<Self>
where + Self: Sized,

Creates a new stream that will skip the n first items of the +underlying stream. Read more
§

fn skip_while<F>(self, f: F) -> SkipWhile<Self, F>
where + F: FnMut(&Self::Item) -> bool, + Self: Sized,

Skip elements from the underlying stream while the provided predicate +resolves to true. Read more
§

fn all<F>(&mut self, f: F) -> AllFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> bool,

Tests if every element of the stream matches a predicate. Read more
§

fn any<F>(&mut self, f: F) -> AnyFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> bool,

Tests if any element of the stream matches a predicate. Read more
§

fn chain<U>(self, other: U) -> Chain<Self, U>
where + U: Stream<Item = Self::Item>, + Self: Sized,

Combine two streams into one by first returning all values from the +first stream then all values from the second stream. Read more
§

fn fold<B, F>(self, init: B, f: F) -> FoldFuture<Self, B, F>
where + Self: Sized, + F: FnMut(B, Self::Item) -> B,

A combinator that applies a function to every element in a stream +producing a single, final value. Read more
§

fn collect<T>(self) -> Collect<Self, T>
where + T: FromStream<Self::Item>, + Self: Sized,

Drain stream pushing all emitted values into a collection. Read more
§

fn timeout(self, duration: Duration) -> Timeout<Self>
where + Self: Sized,

Applies a per-item timeout to the passed stream. Read more
§

fn timeout_repeating(self, interval: Interval) -> TimeoutRepeating<Self>
where + Self: Sized,

Applies a per-item timeout to the passed stream. Read more
§

fn throttle(self, duration: Duration) -> Throttle<Self>
where + Self: Sized,

Slows down a stream by enforcing a delay between items. Read more
§

fn chunks_timeout( + self, + max_size: usize, + duration: Duration, +) -> ChunksTimeout<Self>
where + Self: Sized,

Batches the items in the given stream using a maximum duration and size for each batch. Read more
§

fn peekable(self) -> Peekable<Self>
where + Self: Sized,

Turns the stream into a peekable stream, whose next element can be peeked at without being +consumed. Read more
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

OnClosed

Struct OnClosed 

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

Future returned by Connection::on_closed

+

Resolves to Closed.

+

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 = Closed

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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.OpenBi.html b/pr/672/docs/noq/struct.OpenBi.html new file mode 100644 index 000000000..d43dedcb6 --- /dev/null +++ b/pr/672/docs/noq/struct.OpenBi.html @@ -0,0 +1,53 @@ +OpenBi in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

The type 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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.OpenPath.html b/pr/672/docs/noq/struct.OpenPath.html new file mode 100644 index 000000000..c62f031f0 --- /dev/null +++ b/pr/672/docs/noq/struct.OpenPath.html @@ -0,0 +1,56 @@ +OpenPath in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

The type 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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.OpenUni.html b/pr/672/docs/noq/struct.OpenUni.html new file mode 100644 index 000000000..fae5d228e --- /dev/null +++ b/pr/672/docs/noq/struct.OpenUni.html @@ -0,0 +1,53 @@ +OpenUni in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

The type 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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.Path.html b/pr/672/docs/noq/struct.Path.html new file mode 100644 index 000000000..37e16ae51 --- /dev/null +++ b/pr/672/docs/noq/struct.Path.html @@ -0,0 +1,66 @@ +Path in noq - Rust

Path

Struct Path 

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

An open network transmission within a multipath-enabled connection.

+

As long as a Path or WeakPathHandle is alive, it is ensured that the +PathStats for this path are not dropped even after the path is abandoned.

+

Implementations§

Source§

impl Path

Source

pub fn weak_handle(&self) -> WeakPathHandle

Returns a WeakPathHandle for this path.

+

Holding a WeakPathHandle does not keep a connection alive, but ensures that the +path’s stats are not dropped until the underlying connection is dropped, even if the +path is abandoned.

+
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<PathStatus, SetPathStatusError>

Sets the PathStatus of this path.

+

Returns the previous status of the path.

+
Source

pub fn stats(&self) -> PathStats

Returns the PathStats for this path.

+
Source

pub fn close(&self) -> Result<(), ClosePathError>

Closes this path.

+

The path is immediately considered closed by the local endpoint. Once the state is removed, +after a short period of time for any in-flight packets, a PathEvent::Abandoned is +returned.

+
Source

pub fn set_max_idle_timeout( + &self, + 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_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 local_ip(&self) -> Result<Option<IpAddr>, ClosedPath>

The local IP used for this path, if known.

+

Returns Ok(None) for clients or when the platform does not expose this information; see +noq_udp::RecvMeta::dst_ip for supported platforms.

+
Source

pub fn network_path(&self) -> Result<FourTuple, ClosedPath>

The network path used for this path.

+

Returns a FourTuple, combining Self::remote_address and Self::local_ip.

+
Source

pub fn ping(&self) -> Result<(), ClosedPath>

Ping the remote endpoint over this path.

+

Trait Implementations§

Source§

impl Clone for Path

Source§

fn clone(&self) -> Path

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 Path

Source§

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

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

impl Drop for Path

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl PartialEq for Path

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.

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> 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.PathEvents.html b/pr/672/docs/noq/struct.PathEvents.html new file mode 100644 index 000000000..0557005d2 --- /dev/null +++ b/pr/672/docs/noq/struct.PathEvents.html @@ -0,0 +1,174 @@ +PathEvents in noq - Rust

PathEvents

Struct PathEvents 

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

A stream of PathEvents for all paths in a connection.

+

Trait Implementations§

Source§

impl Debug for PathEvents

Source§

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

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

impl Stream for PathEvents

Source§

type Item = Result<PathEvent, Lagged>

Values yielded by the stream.
Source§

fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, +) -> Poll<Option<Self::Item>>

Attempt to pull out the next value of this stream, registering the +current task for wakeup if the value is not yet available, and returning +None if the stream is exhausted. Read more
§

fn size_hint(&self) -> (usize, Option<usize>)

Returns the bounds on the remaining length of the stream. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<S> StreamExt for S
where + S: Stream + ?Sized,

§

fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>
where + Self: Unpin,

A convenience for calling [Stream::poll_next()] on !Unpin types.
§

fn next(&mut self) -> NextFuture<'_, Self>
where + Self: Unpin,

Retrieves the next item in the stream. Read more
§

fn try_next<T, E>(&mut self) -> TryNextFuture<'_, Self>
where + Self: Stream<Item = Result<T, E>> + Unpin,

Retrieves the next item in the stream. Read more
§

fn count(self) -> CountFuture<Self>
where + Self: Sized,

Counts the number of items in the stream. Read more
§

fn map<T, F>(self, f: F) -> Map<Self, F>
where + Self: Sized, + F: FnMut(Self::Item) -> T,

Maps items of the stream to new values using a closure. Read more
§

fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>
where + Self: Sized, + U: Stream, + F: FnMut(Self::Item) -> U,

Maps items to streams and then concatenates them. Read more
§

fn flatten(self) -> Flatten<Self>
where + Self: Sized, + Self::Item: Stream,

Concatenates inner streams. Read more
§

fn then<F, Fut>(self, f: F) -> Then<Self, F, Fut>
where + Self: Sized, + F: FnMut(Self::Item) -> Fut, + Fut: Future,

Maps items of the stream to new values using an async closure. Read more
§

fn filter<P>(self, predicate: P) -> Filter<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Keeps items of the stream for which predicate returns true. Read more
§

fn filter_map<T, F>(self, f: F) -> FilterMap<Self, F>
where + Self: Sized, + F: FnMut(Self::Item) -> Option<T>,

Filters and maps items of the stream using a closure. Read more
§

fn take(self, n: usize) -> Take<Self>
where + Self: Sized,

Takes only the first n items of the stream. Read more
§

fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Takes items while predicate returns true. Read more
§

fn map_while<B, P>(self, predicate: P) -> MapWhile<Self, P>
where + Self: Sized, + P: FnMut(Self::Item) -> Option<B>,

Maps items while predicate returns Some. Read more
§

fn skip(self, n: usize) -> Skip<Self>
where + Self: Sized,

Skips the first n items of the stream. Read more
§

fn skip_while<P>(self, predicate: P) -> SkipWhile<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Skips items while predicate returns true. Read more
§

fn step_by(self, step: usize) -> StepBy<Self>
where + Self: Sized,

Yields every stepth item. Read more
§

fn chain<U>(self, other: U) -> Chain<Self, U>
where + Self: Sized, + U: Stream<Item = Self::Item>,

Appends another stream to the end of this one. Read more
§

fn cloned<'a, T>(self) -> Cloned<Self>
where + Self: Sized + Stream<Item = &'a T>, + T: Clone + 'a,

Clones all items. Read more
§

fn copied<'a, T>(self) -> Copied<Self>
where + Self: Sized + Stream<Item = &'a T>, + T: Copy + 'a,

Copies all items. Read more
§

fn collect<C>(self) -> CollectFuture<Self, C>
where + Self: Sized, + C: Default + Extend<Self::Item>,

Collects all items in the stream into a collection. Read more
§

fn try_collect<T, E, C>(self) -> TryCollectFuture<Self, C>
where + Self: Sized + Stream<Item = Result<T, E>>, + C: Default + Extend<T>,

Collects all items in the fallible stream into a collection. Read more
§

fn partition<B, P>(self, predicate: P) -> PartitionFuture<Self, P, B>
where + Self: Sized, + B: Default + Extend<Self::Item>, + P: FnMut(&Self::Item) -> bool,

Partitions items into those for which predicate is true and those for which it is +false, and then collects them into two collections. Read more
§

fn fold<T, F>(self, init: T, f: F) -> FoldFuture<Self, F, T>
where + Self: Sized, + F: FnMut(T, Self::Item) -> T,

Accumulates a computation over the stream. Read more
§

fn try_fold<T, E, F, B>( + &mut self, + init: B, + f: F, +) -> TryFoldFuture<'_, Self, F, B>
where + Self: Sized + Stream<Item = Result<T, E>> + Unpin, + F: FnMut(B, T) -> Result<B, E>,

Accumulates a fallible computation over the stream. Read more
§

fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>
where + Self: Sized, + F: FnMut(&mut St, Self::Item) -> Option<B>,

Maps items of the stream to new values using a state value and a closure. Read more
§

fn fuse(self) -> Fuse<Self>
where + Self: Sized,

Fuses the stream so that it stops yielding items after the first None. Read more
§

fn cycle(self) -> Cycle<Self>
where + Self: Sized + Clone,

Repeats the stream from beginning to end, forever. Read more
§

fn enumerate(self) -> Enumerate<Self>
where + Self: Sized,

Enumerates items, mapping them to (index, item). Read more
§

fn inspect<F>(self, f: F) -> Inspect<Self, F>
where + Self: Sized, + F: FnMut(&Self::Item),

Calls a closure on each item and passes it on. Read more
§

fn nth(&mut self, n: usize) -> NthFuture<'_, Self>
where + Self: Unpin,

Gets the nth item of the stream. Read more
§

fn last(self) -> LastFuture<Self>
where + Self: Sized,

Returns the last item in the stream. Read more
§

fn find<P>(&mut self, predicate: P) -> FindFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(&Self::Item) -> bool,

Finds the first item of the stream for which predicate returns true. Read more
§

fn find_map<F, B>(&mut self, f: F) -> FindMapFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> Option<B>,

Applies a closure to items in the stream and returns the first Some result. Read more
§

fn position<P>(&mut self, predicate: P) -> PositionFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(Self::Item) -> bool,

Finds the index of the first item of the stream for which predicate returns true. Read more
§

fn all<P>(&mut self, predicate: P) -> AllFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(Self::Item) -> bool,

Tests if predicate returns true for all items in the stream. Read more
§

fn any<P>(&mut self, predicate: P) -> AnyFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(Self::Item) -> bool,

Tests if predicate returns true for any item in the stream. Read more
§

fn for_each<F>(self, f: F) -> ForEachFuture<Self, F>
where + Self: Sized, + F: FnMut(Self::Item),

Calls a closure on each item of the stream. Read more
§

fn try_for_each<F, E>(&mut self, f: F) -> TryForEachFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> Result<(), E>,

Calls a fallible closure on each item of the stream, stopping on first error. Read more
§

fn zip<U>(self, other: U) -> Zip<Self, U>
where + Self: Sized, + U: Stream,

Zips up two streams into a single stream of pairs. Read more
§

fn unzip<A, B, FromA, FromB>(self) -> UnzipFuture<Self, FromA, FromB>
where + FromA: Default + Extend<A>, + FromB: Default + Extend<B>, + Self: Sized + Stream<Item = (A, B)>,

Collects a stream of pairs into a pair of collections. Read more
§

fn or<S>(self, other: S) -> Or<Self, S>
where + Self: Sized, + S: Stream<Item = Self::Item>,

Merges with other stream, preferring items from self whenever both streams are ready. Read more
§

fn race<S>(self, other: S) -> Race<Self, S>
where + Self: Sized, + S: Stream<Item = Self::Item>,

Merges with other stream, with no preference for either stream when both are ready. Read more
§

fn drain(&mut self) -> Drain<'_, Self>

Yields all immediately available values from a stream. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Stream<Item = Self::Item> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the stream and changes its type to dyn Stream + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Stream<Item = Self::Item> + 'a>>
where + Self: Sized + 'a,

Boxes the stream and changes its type to dyn Stream + 'a. Read more
§

impl<St> StreamExt for St
where + St: Stream + ?Sized,

§

fn next(&mut self) -> Next<'_, Self>
where + Self: Unpin,

Consumes and returns the next value in the stream or None if the +stream is finished. Read more
§

fn try_next<T, E>(&mut self) -> TryNext<'_, Self>
where + Self: Stream<Item = Result<T, E>> + Unpin,

Consumes and returns the next item in the stream. If an error is +encountered before the next item, the error is returned instead. Read more
§

fn map<T, F>(self, f: F) -> Map<Self, F>
where + F: FnMut(Self::Item) -> T, + Self: Sized,

Maps this stream’s items to a different type, returning a new stream of +the resulting type. Read more
§

fn map_while<T, F>(self, f: F) -> MapWhile<Self, F>
where + F: FnMut(Self::Item) -> Option<T>, + Self: Sized,

Map this stream’s items to a different type for as long as determined by +the provided closure. A stream of the target type will be returned, +which will yield elements until the closure returns None. Read more
§

fn then<F, Fut>(self, f: F) -> Then<Self, Fut, F>
where + F: FnMut(Self::Item) -> Fut, + Fut: Future, + Self: Sized,

Maps this stream’s items asynchronously to a different type, returning a +new stream of the resulting type. Read more
§

fn merge<U>(self, other: U) -> Merge<Self, U>
where + U: Stream<Item = Self::Item>, + Self: Sized,

Combine two streams into one by interleaving the output of both as it +is produced. Read more
§

fn filter<F>(self, f: F) -> Filter<Self, F>
where + F: FnMut(&Self::Item) -> bool, + Self: Sized,

Filters the values produced by this stream according to the provided +predicate. Read more
§

fn filter_map<T, F>(self, f: F) -> FilterMap<Self, F>
where + F: FnMut(Self::Item) -> Option<T>, + Self: Sized,

Filters the values produced by this stream while simultaneously mapping +them to a different type according to the provided closure. Read more
§

fn fuse(self) -> Fuse<Self>
where + Self: Sized,

Creates a stream which ends after the first None. Read more
§

fn take(self, n: usize) -> Take<Self>
where + Self: Sized,

Creates a new stream of at most n items of the underlying stream. Read more
§

fn take_while<F>(self, f: F) -> TakeWhile<Self, F>
where + F: FnMut(&Self::Item) -> bool, + Self: Sized,

Take elements from this stream while the provided predicate +resolves to true. Read more
§

fn skip(self, n: usize) -> Skip<Self>
where + Self: Sized,

Creates a new stream that will skip the n first items of the +underlying stream. Read more
§

fn skip_while<F>(self, f: F) -> SkipWhile<Self, F>
where + F: FnMut(&Self::Item) -> bool, + Self: Sized,

Skip elements from the underlying stream while the provided predicate +resolves to true. Read more
§

fn all<F>(&mut self, f: F) -> AllFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> bool,

Tests if every element of the stream matches a predicate. Read more
§

fn any<F>(&mut self, f: F) -> AnyFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> bool,

Tests if any element of the stream matches a predicate. Read more
§

fn chain<U>(self, other: U) -> Chain<Self, U>
where + U: Stream<Item = Self::Item>, + Self: Sized,

Combine two streams into one by first returning all values from the +first stream then all values from the second stream. Read more
§

fn fold<B, F>(self, init: B, f: F) -> FoldFuture<Self, B, F>
where + Self: Sized, + F: FnMut(B, Self::Item) -> B,

A combinator that applies a function to every element in a stream +producing a single, final value. Read more
§

fn collect<T>(self) -> Collect<Self, T>
where + T: FromStream<Self::Item>, + Self: Sized,

Drain stream pushing all emitted values into a collection. Read more
§

fn timeout(self, duration: Duration) -> Timeout<Self>
where + Self: Sized,

Applies a per-item timeout to the passed stream. Read more
§

fn timeout_repeating(self, interval: Interval) -> TimeoutRepeating<Self>
where + Self: Sized,

Applies a per-item timeout to the passed stream. Read more
§

fn throttle(self, duration: Duration) -> Throttle<Self>
where + Self: Sized,

Slows down a stream by enforcing a delay between items. Read more
§

fn chunks_timeout( + self, + max_size: usize, + duration: Duration, +) -> ChunksTimeout<Self>
where + Self: Sized,

Batches the items in the given stream using a maximum duration and size for each batch. Read more
§

fn peekable(self) -> Peekable<Self>
where + Self: Sized,

Turns the stream into a peekable stream, whose next element can be peeked at without being +consumed. Read more
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

impl<S, T, E> TryStream for S
where + S: Stream<Item = 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_next( + self: Pin<&mut S>, + cx: &mut Context<'_>, +) -> Poll<Option<Result<<S as TryStream>::Ok, <S as TryStream>::Error>>>

Poll this TryStream as if it were a Stream. Read more
§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.PathId.html b/pr/672/docs/noq/struct.PathId.html new file mode 100644 index 000000000..e760966e4 --- /dev/null +++ b/pr/672/docs/noq/struct.PathId.html @@ -0,0 +1,52 @@ +PathId in noq - 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 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.PathStats.html b/pr/672/docs/noq/struct.PathStats.html new file mode 100644 index 000000000..51b362682 --- /dev/null +++ b/pr/672/docs/noq/struct.PathStats.html @@ -0,0 +1,62 @@ +PathStats in noq - Rust

PathStats

Struct PathStats 

Source
#[non_exhaustive]
pub struct PathStats {
Show 14 fields + pub rtt: Duration, + pub udp_tx: UdpStats, + pub udp_rx: UdpStats, + pub frame_tx: FrameStats, + pub frame_rx: FrameStats, + pub cwnd: u64, + pub congestion_events: u64, + pub spurious_congestion_events: u64, + pub lost_packets: u64, + pub lost_bytes: u64, + pub sent_plpmtud_probes: u64, + pub lost_plpmtud_probes: u64, + pub black_holes_detected: u64, + pub current_mtu: u16, +
}
Expand description

Statistics related to a transmission path.

+

Fields (Non-exhaustive)§

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

Current best estimate of this connection’s latency (round-trip-time).

+
§udp_tx: UdpStats

Statistics about datagrams and bytes sent on this path.

+
§udp_rx: UdpStats

Statistics about datagrams and bytes received on this path.

+
§frame_tx: FrameStats

Statistics about frames transmitted on this path.

+
§frame_rx: FrameStats

Statistics about frames received on this path.

+
§cwnd: u64

Current congestion window of the connection.

+
§congestion_events: u64

Congestion events on the connection.

+
§spurious_congestion_events: u64

Spurious congestion events on the connection.

+
§lost_packets: u64

The number of packets lost on this path.

+
§lost_bytes: u64

The number of bytes lost on this path.

+
§sent_plpmtud_probes: u64

The number of PLPMTUD probe packets sent on this path.

+

These are also counted by UdpStats::datagrams.

+
§lost_plpmtud_probes: u64

The number of PLPMTUD probe packets lost on this path.

+

These are not included in Self::lost_packets and Self::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 Add<PathStats> for ConnectionStats

Source§

type Output = ConnectionStats

The resulting type after applying the + operator.
Source§

fn add(self, rhs: PathStats) -> <ConnectionStats as Add<PathStats>>::Output

Performs the + operation. Read more
Source§

impl AddAssign<PathStats> for ConnectionStats

Source§

fn add_assign(&mut self, rhs: PathStats)

Performs the += operation. Read more
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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.QlogConfig.html b/pr/672/docs/noq/struct.QlogConfig.html new file mode 100644 index 000000000..4f036077e --- /dev/null +++ b/pr/672/docs/noq/struct.QlogConfig.html @@ -0,0 +1,33 @@ +QlogConfig in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.QlogFileFactory.html b/pr/672/docs/noq/struct.QlogFileFactory.html new file mode 100644 index 000000000..0f5a7038f --- /dev/null +++ b/pr/672/docs/noq/struct.QlogFileFactory.html @@ -0,0 +1,38 @@ +QlogFileFactory in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.ReadDatagram.html b/pr/672/docs/noq/struct.ReadDatagram.html new file mode 100644 index 000000000..f421d2679 --- /dev/null +++ b/pr/672/docs/noq/struct.ReadDatagram.html @@ -0,0 +1,53 @@ +ReadDatagram in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

The type 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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.RecvStream.html b/pr/672/docs/noq/struct.RecvStream.html new file mode 100644 index 000000000..7fc5eff9a --- /dev/null +++ b/pr/672/docs/noq/struct.RecvStream.html @@ -0,0 +1,201 @@ +RecvStream in noq - 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 async fn read_chunk( + &mut self, + max_length: usize, +) -> Result<Option<Bytes>, ReadError>

Reads the next segment of data as zero-copy [Bytes].

+

Yields None if the stream was finished. Otherwise, yields the next segment of data. The +chunk’s offset will be immediately after the last data yielded by RecvStream::read or +RecvStream::read_chunk; use bytes_read() to query that offset +explicitly.

+

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_many_chunks( + &mut self, + bufs: &mut [Bytes], +) -> Result<Option<usize>, ReadError>

Reads the next segments of data.

+

Fills bufs with the segments of data beginning immediately after the last data yielded +by read, read_chunk, or +read_many_chunks, 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 fn bytes_read(&self) -> Result<u64, ClosedStream>

Returns the number of bytes read from this stream.

+

This is the offset of the next byte to be read, i.e. the length of the contiguous +prefix of the stream consumed by the application.

+
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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.RetryError.html b/pr/672/docs/noq/struct.RetryError.html new file mode 100644 index 000000000..2c3212db4 --- /dev/null +++ b/pr/672/docs/noq/struct.RetryError.html @@ -0,0 +1,26 @@ +RetryError in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.SendDatagram.html b/pr/672/docs/noq/struct.SendDatagram.html new file mode 100644 index 000000000..53428fd9a --- /dev/null +++ b/pr/672/docs/noq/struct.SendDatagram.html @@ -0,0 +1,53 @@ +SendDatagram in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

The type 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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.SendStream.html b/pr/672/docs/noq/struct.SendStream.html new file mode 100644 index 000000000..42ab4da14 --- /dev/null +++ b/pr/672/docs/noq/struct.SendStream.html @@ -0,0 +1,216 @@ +SendStream in noq - 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_many_chunks( + &mut self, + bufs: &mut &mut [Bytes], +) -> Result<usize, WriteError>

Writes [Bytes] from a slice of buffers into this stream, returning how many bytes were. +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 returns the number of bytes written:

+
    +
  • bufs is advanced past chunks that were fully written.
  • +
  • If a [Bytes] chunk was partially written, the chunk at the new front of bufs is +split to contain only the suffix of bytes that were not 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>

Writes a single [Bytes] into this stream in its entirety.

+

Bytes to write are provided to this method as a single cheaply cloneable chunk. This +method repeatedly calls write_many_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>

Writes 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_many_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 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) -> Stopped

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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.ServerConfig.html b/pr/672/docs/noq/struct.ServerConfig.html new file mode 100644 index 000000000..792f20a53 --- /dev/null +++ b/pr/672/docs/noq/struct.ServerConfig.html @@ -0,0 +1,113 @@ +ServerConfig in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.SmolRuntime.html b/pr/672/docs/noq/struct.SmolRuntime.html new file mode 100644 index 000000000..0e1e06709 --- /dev/null +++ b/pr/672/docs/noq/struct.SmolRuntime.html @@ -0,0 +1,24 @@ +SmolRuntime in noq - Rust

SmolRuntime

Struct SmolRuntime 

Source
pub struct SmolRuntime;
Expand description

A noq 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.StdSystemTime.html b/pr/672/docs/noq/struct.StdSystemTime.html new file mode 100644 index 000000000..c16cb0823 --- /dev/null +++ b/pr/672/docs/noq/struct.StdSystemTime.html @@ -0,0 +1,25 @@ +StdSystemTime in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.Stopped.html b/pr/672/docs/noq/struct.Stopped.html new file mode 100644 index 000000000..cb0ec42df --- /dev/null +++ b/pr/672/docs/noq/struct.Stopped.html @@ -0,0 +1,53 @@ +Stopped in noq - Rust

Stopped

Struct Stopped 

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

Future returned from SendStream::stopped.

+

Trait Implementations§

Source§

impl Debug for Stopped

Source§

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

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

impl Future for Stopped

Source§

type Output = Result<Option<VarInt>, StoppedError>

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

impl<'__pin> Unpin for Stopped
where + PinnedFieldsOf<__Origin<'__pin>>: Unpin,

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

The type 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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.StreamId.html b/pr/672/docs/noq/struct.StreamId.html new file mode 100644 index 000000000..95125c081 --- /dev/null +++ b/pr/672/docs/noq/struct.StreamId.html @@ -0,0 +1,54 @@ +StreamId in noq - Rust

StreamId

Struct StreamId 

Source
pub struct StreamId(/* private fields */);
Expand description

Identifier for a stream within a particular connection

+

Implementations§

Source§

impl StreamId

Source

pub fn new(initiator: Side, dir: Dir, index: u64) -> StreamId

Create a new StreamId

+
Source

pub fn initiator(self) -> Side

Which side of a connection initiated the stream

+
Source

pub fn dir(self) -> Dir

Which directions data flows in

+
Source

pub fn index(self) -> u64

Distinguishes streams of the same initiator and directionality

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for StreamId

Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<StreamId, Error>

Generate an arbitrary value of Self from the given unstructured data. Read more
§

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self, Error>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for StreamId

Source§

fn clone(&self) -> StreamId

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for StreamId

Source§

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

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

impl Decodable for StreamId

Source§

fn decode<B>(buf: &mut B) -> Result<StreamId, UnexpectedEnd>
where + B: Buf,

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Display for StreamId

Source§

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

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

impl Encodable for StreamId

Source§

fn encode<B>(&self, buf: &mut B)
where + B: BufMut,

Append the encoding of self to the provided buffer.
Source§

impl From<StreamId> for VarInt

Source§

fn from(x: StreamId) -> VarInt

Converts to this type from the input type.
Source§

impl From<VarInt> for StreamId

Source§

fn from(v: VarInt) -> StreamId

Converts to this type from the input type.
Source§

impl Hash for StreamId

Source§

fn hash<__H>(&self, state: &mut __H)
where + __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for StreamId

Source§

fn cmp(&self, other: &StreamId) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for StreamId

Source§

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

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

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

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

impl PartialOrd for StreamId

Source§

fn partial_cmp(&self, other: &StreamId) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

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

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

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

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

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

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

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

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for StreamId

Source§

impl Eq for StreamId

Source§

impl StructuralPartialEq for StreamId

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

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

§

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

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

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

§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.TokenMemoryCache.html b/pr/672/docs/noq/struct.TokenMemoryCache.html new file mode 100644 index 000000000..7f5f7923c --- /dev/null +++ b/pr/672/docs/noq/struct.TokenMemoryCache.html @@ -0,0 +1,30 @@ +TokenMemoryCache in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.TokenReuseError.html b/pr/672/docs/noq/struct.TokenReuseError.html new file mode 100644 index 000000000..e09deff3a --- /dev/null +++ b/pr/672/docs/noq/struct.TokenReuseError.html @@ -0,0 +1,24 @@ +TokenReuseError in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.TokioRuntime.html b/pr/672/docs/noq/struct.TokioRuntime.html new file mode 100644 index 000000000..e48e1e983 --- /dev/null +++ b/pr/672/docs/noq/struct.TokioRuntime.html @@ -0,0 +1,24 @@ +TokioRuntime in noq - Rust

TokioRuntime

Struct TokioRuntime 

Source
pub struct TokioRuntime;
Expand description

A noq 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.Transmit.html b/pr/672/docs/noq/struct.Transmit.html new file mode 100644 index 000000000..77bbf6cd2 --- /dev/null +++ b/pr/672/docs/noq/struct.Transmit.html @@ -0,0 +1,36 @@ +Transmit in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.TransportConfig.html b/pr/672/docs/noq/struct.TransportConfig.html new file mode 100644 index 000000000..80a3a1a44 --- /dev/null +++ b/pr/672/docs/noq/struct.TransportConfig.html @@ -0,0 +1,255 @@ +TransportConfig in noq - 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 max_outgoing_bytes_per_second( + &mut self, + value: Option<u64>, +) -> &mut TransportConfig

Configures an outbound rate limit (in bytes per second) for each connection.

+

Defaults to None, which disables rate limiting.

+
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. noq-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 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 n0’s nat traversal protocol, 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, then that setting is set to 8.

+
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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.TransportErrorCode.html b/pr/672/docs/noq/struct.TransportErrorCode.html new file mode 100644 index 000000000..33617fda7 --- /dev/null +++ b/pr/672/docs/noq/struct.TransportErrorCode.html @@ -0,0 +1,56 @@ +TransportErrorCode in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.UdpStats.html b/pr/672/docs/noq/struct.UdpStats.html new file mode 100644 index 000000000..1d5318a99 --- /dev/null +++ b/pr/672/docs/noq/struct.UdpStats.html @@ -0,0 +1,41 @@ +UdpStats in noq - 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 number of UDP datagrams observed.

+
§bytes: u64

The total amount of bytes which have been transferred inside UDP datagrams.

+
§ios: u64

The number 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 Add for UdpStats

Source§

type Output = UdpStats

The resulting type after applying the + operator.
Source§

fn add(self, __rhs: UdpStats) -> <UdpStats as Add>::Output

Performs the + operation. Read more
Source§

impl AddAssign for UdpStats

Source§

fn add_assign(&mut self, __rhs: UdpStats)

Performs the += operation. Read more
Source§

impl Clone for UdpStats

Source§

fn clone(&self) -> UdpStats

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for UdpStats

Source§

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

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

impl Default for UdpStats

Source§

fn default() -> UdpStats

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

impl PartialEq for UdpStats

Source§

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

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

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

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

impl Copy for UdpStats

Source§

impl Eq for UdpStats

Source§

impl StructuralPartialEq for UdpStats

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

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

§

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

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

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

§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.UnorderedRecvStream.html b/pr/672/docs/noq/struct.UnorderedRecvStream.html new file mode 100644 index 000000000..0346d209d --- /dev/null +++ b/pr/672/docs/noq/struct.UnorderedRecvStream.html @@ -0,0 +1,53 @@ +UnorderedRecvStream in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/struct.ValidationTokenConfig.html b/pr/672/docs/noq/struct.ValidationTokenConfig.html new file mode 100644 index 000000000..3c6d9b8f2 --- /dev/null +++ b/pr/672/docs/noq/struct.ValidationTokenConfig.html @@ -0,0 +1,73 @@ +ValidationTokenConfig in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.VarInt.html b/pr/672/docs/noq/struct.VarInt.html new file mode 100644 index 000000000..019c86b4c --- /dev/null +++ b/pr/672/docs/noq/struct.VarInt.html @@ -0,0 +1,64 @@ +VarInt in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.VarIntBoundsExceeded.html b/pr/672/docs/noq/struct.VarIntBoundsExceeded.html new file mode 100644 index 000000000..932d303ac --- /dev/null +++ b/pr/672/docs/noq/struct.VarIntBoundsExceeded.html @@ -0,0 +1,32 @@ +VarIntBoundsExceeded in noq - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq/struct.WeakConnectionHandle.html b/pr/672/docs/noq/struct.WeakConnectionHandle.html new file mode 100644 index 000000000..59c66e034 --- /dev/null +++ b/pr/672/docs/noq/struct.WeakConnectionHandle.html @@ -0,0 +1,31 @@ +WeakConnectionHandle in noq - 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

+
Source

pub fn is_same_connection(&self, other: &Self) -> bool

Returns true if the two WeakConnectionHandle point at the same 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.WeakPathHandle.html b/pr/672/docs/noq/struct.WeakPathHandle.html new file mode 100644 index 000000000..25d14032b --- /dev/null +++ b/pr/672/docs/noq/struct.WeakPathHandle.html @@ -0,0 +1,37 @@ +WeakPathHandle in noq - Rust

WeakPathHandle

Struct WeakPathHandle 

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

Weak handle for a Path that does not keep the connection alive.

+

As long as a WeakPathHandle for a path exists, that path’s final stats will not be dropped even if +the path was abandoned.

+

The WeakPathHandle can be upgraded to a Path as long as its Connection has not been dropped.

+

Implementations§

Source§

impl WeakPathHandle

Source

pub fn id(&self) -> PathId

Returns the PathId of this path.

+
Source

pub fn upgrade(&self) -> Option<Path>

Upgrades to a Path.

+

Returns None if the connection was dropped.

+

Trait Implementations§

Source§

impl Clone for WeakPathHandle

Source§

fn clone(&self) -> WeakPathHandle

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 WeakPathHandle

Source§

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

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

impl Drop for WeakPathHandle

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl PartialEq for WeakPathHandle

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 WeakPathHandle

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq/struct.ZeroRttAccepted.html b/pr/672/docs/noq/struct.ZeroRttAccepted.html new file mode 100644 index 000000000..619eea487 --- /dev/null +++ b/pr/672/docs/noq/struct.ZeroRttAccepted.html @@ -0,0 +1,50 @@ +ZeroRttAccepted in noq - 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq/trait.AsyncTimer.html b/pr/672/docs/noq/trait.AsyncTimer.html new file mode 100644 index 000000000..69a410798 --- /dev/null +++ b/pr/672/docs/noq/trait.AsyncTimer.html @@ -0,0 +1,11 @@ +AsyncTimer in noq - 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/672/docs/noq/trait.AsyncUdpSocket.html b/pr/672/docs/noq/trait.AsyncUdpSocket.html new file mode 100644 index 000000000..5f1665cfe --- /dev/null +++ b/pr/672/docs/noq/trait.AsyncUdpSocket.html @@ -0,0 +1,38 @@ +AsyncUdpSocket in noq - 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/672/docs/noq/trait.ConnectionIdGenerator.html b/pr/672/docs/noq/trait.ConnectionIdGenerator.html new file mode 100644 index 000000000..3f1194e89 --- /dev/null +++ b/pr/672/docs/noq/trait.ConnectionIdGenerator.html @@ -0,0 +1,21 @@ +ConnectionIdGenerator in noq - 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/672/docs/noq/trait.NetworkChangeHint.html b/pr/672/docs/noq/trait.NetworkChangeHint.html new file mode 100644 index 000000000..6850d534f --- /dev/null +++ b/pr/672/docs/noq/trait.NetworkChangeHint.html @@ -0,0 +1,15 @@ +NetworkChangeHint in noq - Rust

NetworkChangeHint

Trait NetworkChangeHint 

Source
pub trait NetworkChangeHint: Debug + 'static {
+    // Required method
+    fn is_path_recoverable(
+        &self,
+        path_id: PathId,
+        network_path: FourTuple,
+    ) -> bool;
+}
Expand description

Hints when the caller identifies a network change.

+

Required Methods§

Source

fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool

Inform the connection if a path may recover after a network change.

+

After network changes, paths may not be recoverable. In this case, waiting for the path to +become idle may take longer than what is desirable. If Self::is_path_recoverable +returns false, a multipath-enabled, client-side connection will establish a new path to +the same remote, closing the current one, instead of migrating the path.

+

Paths that are deemed recoverable will simply be sent a PING for a liveness check.

+

Implementors§

\ No newline at end of file diff --git a/pr/672/docs/noq/trait.QlogFactory.html b/pr/672/docs/noq/trait.QlogFactory.html new file mode 100644 index 000000000..dacb6954d --- /dev/null +++ b/pr/672/docs/noq/trait.QlogFactory.html @@ -0,0 +1,23 @@ +QlogFactory in noq - 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/672/docs/noq/trait.Runtime.html b/pr/672/docs/noq/trait.Runtime.html new file mode 100644 index 000000000..6a41acc6a --- /dev/null +++ b/pr/672/docs/noq/trait.Runtime.html @@ -0,0 +1,19 @@ +Runtime in noq - 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/672/docs/noq/trait.TimeSource.html b/pr/672/docs/noq/trait.TimeSource.html new file mode 100644 index 000000000..50381acdd --- /dev/null +++ b/pr/672/docs/noq/trait.TimeSource.html @@ -0,0 +1,7 @@ +TimeSource in noq - 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/672/docs/noq/trait.TokenLog.html b/pr/672/docs/noq/trait.TokenLog.html new file mode 100644 index 000000000..1c9909080 --- /dev/null +++ b/pr/672/docs/noq/trait.TokenLog.html @@ -0,0 +1,45 @@ +TokenLog in noq - 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/672/docs/noq/trait.TokenStore.html b/pr/672/docs/noq/trait.TokenStore.html new file mode 100644 index 000000000..5aab57592 --- /dev/null +++ b/pr/672/docs/noq/trait.TokenStore.html @@ -0,0 +1,13 @@ +TokenStore in noq - 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/672/docs/noq/trait.UdpSender.html b/pr/672/docs/noq/trait.UdpSender.html new file mode 100644 index 000000000..815e79037 --- /dev/null +++ b/pr/672/docs/noq/trait.UdpSender.html @@ -0,0 +1,30 @@ +UdpSender in noq - 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.

+

noq 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/672/docs/noq_perf/all.html b/pr/672/docs/noq_perf/all.html new file mode 100644 index 000000000..3d9934367 --- /dev/null +++ b/pr/672/docs/noq_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/672/docs/noq_perf/enum.Commands.html b/pr/672/docs/noq_perf/enum.Commands.html new file mode 100644 index 000000000..1b953f1f9 --- /dev/null +++ b/pr/672/docs/noq_perf/enum.Commands.html @@ -0,0 +1,39 @@ +Commands in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_perf/fn.main.html b/pr/672/docs/noq_perf/fn.main.html new file mode 100644 index 000000000..8c4d9baeb --- /dev/null +++ b/pr/672/docs/noq_perf/fn.main.html @@ -0,0 +1 @@ +main in noq_perf - Rust

main

Function main 

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

Crate noq_perf

Crate noq_perf 

Source

Structs§

Cli 🔒

Enums§

Commands 🔒

Functions§

main 🔒
\ No newline at end of file diff --git a/pr/672/docs/noq_perf/sidebar-items.js b/pr/672/docs/noq_perf/sidebar-items.js new file mode 100644 index 000000000..9a5c272f9 --- /dev/null +++ b/pr/672/docs/noq_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/672/docs/noq_perf/struct.Cli.html b/pr/672/docs/noq_perf/struct.Cli.html new file mode 100644 index 000000000..fa4fe8761 --- /dev/null +++ b/pr/672/docs/noq_perf/struct.Cli.html @@ -0,0 +1,44 @@ +Cli in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/all.html b/pr/672/docs/noq_proto/all.html new file mode 100644 index 000000000..47122df23 --- /dev/null +++ b/pr/672/docs/noq_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/672/docs/noq_proto/bench_exports/index.html b/pr/672/docs/noq_proto/bench_exports/index.html new file mode 100644 index 000000000..979bc1d0c --- /dev/null +++ b/pr/672/docs/noq_proto/bench_exports/index.html @@ -0,0 +1,2 @@ +noq_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/672/docs/noq_proto/bench_exports/send_buffer_benches/fn.get_into_many_segments.html b/pr/672/docs/noq_proto/bench_exports/send_buffer_benches/fn.get_into_many_segments.html new file mode 100644 index 000000000..4a9e578b1 --- /dev/null +++ b/pr/672/docs/noq_proto/bench_exports/send_buffer_benches/fn.get_into_many_segments.html @@ -0,0 +1,2 @@ +get_into_many_segments in noq_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/672/docs/noq_proto/bench_exports/send_buffer_benches/fn.get_loop_many_segments.html b/pr/672/docs/noq_proto/bench_exports/send_buffer_benches/fn.get_loop_many_segments.html new file mode 100644 index 000000000..5e22a090a --- /dev/null +++ b/pr/672/docs/noq_proto/bench_exports/send_buffer_benches/fn.get_loop_many_segments.html @@ -0,0 +1,2 @@ +get_loop_many_segments in noq_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/672/docs/noq_proto/bench_exports/send_buffer_benches/index.html b/pr/672/docs/noq_proto/bench_exports/send_buffer_benches/index.html new file mode 100644 index 000000000..79793736f --- /dev/null +++ b/pr/672/docs/noq_proto/bench_exports/send_buffer_benches/index.html @@ -0,0 +1,4 @@ +noq_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/672/docs/noq_proto/bench_exports/send_buffer_benches/sidebar-items.js b/pr/672/docs/noq_proto/bench_exports/send_buffer_benches/sidebar-items.js new file mode 100644 index 000000000..24edbdcf5 --- /dev/null +++ b/pr/672/docs/noq_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/672/docs/noq_proto/bench_exports/sidebar-items.js b/pr/672/docs/noq_proto/bench_exports/sidebar-items.js new file mode 100644 index 000000000..cb9cfb5fa --- /dev/null +++ b/pr/672/docs/noq_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/672/docs/noq_proto/bloom_token_log/struct.BloomTokenLog.html b/pr/672/docs/noq_proto/bloom_token_log/struct.BloomTokenLog.html new file mode 100644 index 000000000..8df1022f9 --- /dev/null +++ b/pr/672/docs/noq_proto/bloom_token_log/struct.BloomTokenLog.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.BloomTokenLog.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/cid_generator/struct.HashedConnectionIdGenerator.html b/pr/672/docs/noq_proto/cid_generator/struct.HashedConnectionIdGenerator.html new file mode 100644 index 000000000..f4b4f8626 --- /dev/null +++ b/pr/672/docs/noq_proto/cid_generator/struct.HashedConnectionIdGenerator.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.HashedConnectionIdGenerator.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/cid_generator/struct.InvalidCid.html b/pr/672/docs/noq_proto/cid_generator/struct.InvalidCid.html new file mode 100644 index 000000000..eefbbc3b5 --- /dev/null +++ b/pr/672/docs/noq_proto/cid_generator/struct.InvalidCid.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.InvalidCid.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/cid_generator/struct.RandomConnectionIdGenerator.html b/pr/672/docs/noq_proto/cid_generator/struct.RandomConnectionIdGenerator.html new file mode 100644 index 000000000..44b8435b1 --- /dev/null +++ b/pr/672/docs/noq_proto/cid_generator/struct.RandomConnectionIdGenerator.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.RandomConnectionIdGenerator.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/cid_generator/trait.ConnectionIdGenerator.html b/pr/672/docs/noq_proto/cid_generator/trait.ConnectionIdGenerator.html new file mode 100644 index 000000000..4fb6ecf98 --- /dev/null +++ b/pr/672/docs/noq_proto/cid_generator/trait.ConnectionIdGenerator.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/trait.ConnectionIdGenerator.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/coding/index.html b/pr/672/docs/noq_proto/coding/index.html new file mode 100644 index 000000000..f1afc845f --- /dev/null +++ b/pr/672/docs/noq_proto/coding/index.html @@ -0,0 +1,2 @@ +noq_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/672/docs/noq_proto/coding/sidebar-items.js b/pr/672/docs/noq_proto/coding/sidebar-items.js new file mode 100644 index 000000000..e6d58e381 --- /dev/null +++ b/pr/672/docs/noq_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/672/docs/noq_proto/coding/struct.UnexpectedEnd.html b/pr/672/docs/noq_proto/coding/struct.UnexpectedEnd.html new file mode 100644 index 000000000..566244dec --- /dev/null +++ b/pr/672/docs/noq_proto/coding/struct.UnexpectedEnd.html @@ -0,0 +1,32 @@ +UnexpectedEnd in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/coding/trait.Decodable.html b/pr/672/docs/noq_proto/coding/trait.Decodable.html new file mode 100644 index 000000000..6aa2368f0 --- /dev/null +++ b/pr/672/docs/noq_proto/coding/trait.Decodable.html @@ -0,0 +1,6 @@ +Decodable in noq_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/672/docs/noq_proto/coding/trait.Encodable.html b/pr/672/docs/noq_proto/coding/trait.Encodable.html new file mode 100644 index 000000000..68553980c --- /dev/null +++ b/pr/672/docs/noq_proto/coding/trait.Encodable.html @@ -0,0 +1,6 @@ +Encodable in noq_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/672/docs/noq_proto/coding/type.Result.html b/pr/672/docs/noq_proto/coding/type.Result.html new file mode 100644 index 000000000..e17ede1f4 --- /dev/null +++ b/pr/672/docs/noq_proto/coding/type.Result.html @@ -0,0 +1,7 @@ +Result in noq_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/672/docs/noq_proto/config/enum.ConfigError.html b/pr/672/docs/noq_proto/config/enum.ConfigError.html new file mode 100644 index 000000000..924393a0f --- /dev/null +++ b/pr/672/docs/noq_proto/config/enum.ConfigError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/enum.ConfigError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/config/qlog/struct.QlogConfig.html b/pr/672/docs/noq_proto/config/qlog/struct.QlogConfig.html new file mode 100644 index 000000000..309ba43ab --- /dev/null +++ b/pr/672/docs/noq_proto/config/qlog/struct.QlogConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.QlogConfig.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/config/qlog/struct.QlogFileFactory.html b/pr/672/docs/noq_proto/config/qlog/struct.QlogFileFactory.html new file mode 100644 index 000000000..b41c84750 --- /dev/null +++ b/pr/672/docs/noq_proto/config/qlog/struct.QlogFileFactory.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.QlogFileFactory.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/config/qlog/trait.QlogFactory.html b/pr/672/docs/noq_proto/config/qlog/trait.QlogFactory.html new file mode 100644 index 000000000..2ce472d56 --- /dev/null +++ b/pr/672/docs/noq_proto/config/qlog/trait.QlogFactory.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/trait.QlogFactory.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/config/struct.ClientConfig.html b/pr/672/docs/noq_proto/config/struct.ClientConfig.html new file mode 100644 index 000000000..0eaf6aef3 --- /dev/null +++ b/pr/672/docs/noq_proto/config/struct.ClientConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.ClientConfig.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/config/struct.EndpointConfig.html b/pr/672/docs/noq_proto/config/struct.EndpointConfig.html new file mode 100644 index 000000000..e6dc880ba --- /dev/null +++ b/pr/672/docs/noq_proto/config/struct.EndpointConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.EndpointConfig.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/config/struct.ServerConfig.html b/pr/672/docs/noq_proto/config/struct.ServerConfig.html new file mode 100644 index 000000000..8c4dd7c4d --- /dev/null +++ b/pr/672/docs/noq_proto/config/struct.ServerConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.ServerConfig.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/config/struct.StdSystemTime.html b/pr/672/docs/noq_proto/config/struct.StdSystemTime.html new file mode 100644 index 000000000..3977e551f --- /dev/null +++ b/pr/672/docs/noq_proto/config/struct.StdSystemTime.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.StdSystemTime.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/config/struct.ValidationTokenConfig.html b/pr/672/docs/noq_proto/config/struct.ValidationTokenConfig.html new file mode 100644 index 000000000..0fc5aa26a --- /dev/null +++ b/pr/672/docs/noq_proto/config/struct.ValidationTokenConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.ValidationTokenConfig.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/config/trait.TimeSource.html b/pr/672/docs/noq_proto/config/trait.TimeSource.html new file mode 100644 index 000000000..316bd2e7f --- /dev/null +++ b/pr/672/docs/noq_proto/config/trait.TimeSource.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/trait.TimeSource.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/config/transport/struct.AckFrequencyConfig.html b/pr/672/docs/noq_proto/config/transport/struct.AckFrequencyConfig.html new file mode 100644 index 000000000..befe5e73d --- /dev/null +++ b/pr/672/docs/noq_proto/config/transport/struct.AckFrequencyConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.AckFrequencyConfig.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/config/transport/struct.IdleTimeout.html b/pr/672/docs/noq_proto/config/transport/struct.IdleTimeout.html new file mode 100644 index 000000000..9839679ac --- /dev/null +++ b/pr/672/docs/noq_proto/config/transport/struct.IdleTimeout.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.IdleTimeout.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/config/transport/struct.MtuDiscoveryConfig.html b/pr/672/docs/noq_proto/config/transport/struct.MtuDiscoveryConfig.html new file mode 100644 index 000000000..7cfee6b1b --- /dev/null +++ b/pr/672/docs/noq_proto/config/transport/struct.MtuDiscoveryConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.MtuDiscoveryConfig.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/config/transport/struct.TransportConfig.html b/pr/672/docs/noq_proto/config/transport/struct.TransportConfig.html new file mode 100644 index 000000000..763547791 --- /dev/null +++ b/pr/672/docs/noq_proto/config/transport/struct.TransportConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.TransportConfig.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/congestion/bbr3/struct.Bbr3.html b/pr/672/docs/noq_proto/congestion/bbr3/struct.Bbr3.html new file mode 100644 index 000000000..67f06ef21 --- /dev/null +++ b/pr/672/docs/noq_proto/congestion/bbr3/struct.Bbr3.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/congestion/struct.Bbr3.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/congestion/bbr3/struct.Bbr3Config.html b/pr/672/docs/noq_proto/congestion/bbr3/struct.Bbr3Config.html new file mode 100644 index 000000000..ed6003847 --- /dev/null +++ b/pr/672/docs/noq_proto/congestion/bbr3/struct.Bbr3Config.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/congestion/struct.Bbr3Config.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/congestion/cubic/struct.Cubic.html b/pr/672/docs/noq_proto/congestion/cubic/struct.Cubic.html new file mode 100644 index 000000000..62ea263f2 --- /dev/null +++ b/pr/672/docs/noq_proto/congestion/cubic/struct.Cubic.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/congestion/struct.Cubic.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/congestion/cubic/struct.CubicConfig.html b/pr/672/docs/noq_proto/congestion/cubic/struct.CubicConfig.html new file mode 100644 index 000000000..42b5afadb --- /dev/null +++ b/pr/672/docs/noq_proto/congestion/cubic/struct.CubicConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/congestion/struct.CubicConfig.html...

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

Module congestion

Module congestion 

Source
Expand description

Logic for controlling the rate at which data is sent

+

Structs§

Bbr3
Experimental! Use at your own risk.
Bbr3Config
Configuration for the Bbr3 congestion controller
ControllerMetrics
Common congestion controller metrics used both for logging purposes +but also to alter the pacing of the connection with +pacing_rate and send_quantum
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/672/docs/noq_proto/congestion/new_reno/struct.NewReno.html b/pr/672/docs/noq_proto/congestion/new_reno/struct.NewReno.html new file mode 100644 index 000000000..6a4145388 --- /dev/null +++ b/pr/672/docs/noq_proto/congestion/new_reno/struct.NewReno.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/congestion/struct.NewReno.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/congestion/new_reno/struct.NewRenoConfig.html b/pr/672/docs/noq_proto/congestion/new_reno/struct.NewRenoConfig.html new file mode 100644 index 000000000..d97995371 --- /dev/null +++ b/pr/672/docs/noq_proto/congestion/new_reno/struct.NewRenoConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/congestion/struct.NewRenoConfig.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/congestion/sidebar-items.js b/pr/672/docs/noq_proto/congestion/sidebar-items.js new file mode 100644 index 000000000..07e9dc7b1 --- /dev/null +++ b/pr/672/docs/noq_proto/congestion/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["Bbr3","Bbr3Config","ControllerMetrics","Cubic","CubicConfig","NewReno","NewRenoConfig"],"trait":["Controller","ControllerFactory"]}; \ No newline at end of file diff --git a/pr/672/docs/noq_proto/congestion/struct.Bbr3.html b/pr/672/docs/noq_proto/congestion/struct.Bbr3.html new file mode 100644 index 000000000..dbb5082d5 --- /dev/null +++ b/pr/672/docs/noq_proto/congestion/struct.Bbr3.html @@ -0,0 +1,60 @@ +Bbr3 in noq_proto::congestion - Rust

Bbr3

Struct Bbr3 

Source
pub struct Bbr3 { /* 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 https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html +equivalent to a combination of BBR and C states +https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.4 +https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.1

+

Trait Implementations§

Source§

impl Clone for Bbr3

Source§

fn clone(&self) -> Bbr3

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 Bbr3

Source§

fn on_spurious_congestion_event(&mut self)

Source§

fn on_packet_sent(&mut self, now: Instant, bytes: u16, pn: u64)

One packet was just sent
Source§

fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + pn: 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, + largest_lost_pn: u64, +)

Packets were deemed lost or marked congested Read more
Source§

fn on_packet_lost(&mut self, lost_bytes: u16, pn: u64, now: Instant)

One packet was just lost
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

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

fn on_ack_frequency_update( + &mut self, + ack_eliciting_threshold: u64, + requested_max_ack_delay: Duration, +)

The peer’s ACK-frequency parameters have changed Read more
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 +This is also used to alter the pacing of the connection with +pacing_rate and send_quantum
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, largest_pn: u64)

One or more packets were just sent
Source§

impl Debug for Bbr3

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Bbr3

§

impl RefUnwindSafe for Bbr3

§

impl Send for Bbr3

§

impl Sync for Bbr3

§

impl Unpin for Bbr3

§

impl UnwindSafe for Bbr3

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/congestion/struct.Bbr3Config.html b/pr/672/docs/noq_proto/congestion/struct.Bbr3Config.html new file mode 100644 index 000000000..6ee9ef72b --- /dev/null +++ b/pr/672/docs/noq_proto/congestion/struct.Bbr3Config.html @@ -0,0 +1,39 @@ +Bbr3Config in noq_proto::congestion - Rust

Bbr3Config

Struct Bbr3Config 

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

Configuration for the Bbr3 congestion controller

+

Different pacing_gains can be set to modify the multiplier used to +increase the sending rates. +Different cwnd_gains can be set to modify the multiplier used to increase +the congestion windows. +All of these parameters are specific to different states of the algorithm: see BbrState +pacing_margin_percent is used to set a margin when calculating the pacing_rate in order +to not send at 100% capacity when calculating pacing.

+

Implementations§

Source§

impl Bbr3Config

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 Bbr3Config

Source§

fn clone(&self) -> Bbr3Config

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 Bbr3Config

Source§

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

Construct a fresh Controller
Source§

impl Debug for Bbr3Config

Source§

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

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

impl Default for Bbr3Config

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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/congestion/struct.ControllerMetrics.html b/pr/672/docs/noq_proto/congestion/struct.ControllerMetrics.html new file mode 100644 index 000000000..83b9686be --- /dev/null +++ b/pr/672/docs/noq_proto/congestion/struct.ControllerMetrics.html @@ -0,0 +1,35 @@ +ControllerMetrics in noq_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>, + pub send_quantum: Option<u64>, +}
Expand description

Common congestion controller metrics used both for logging purposes +but also to alter the pacing of the connection with +pacing_rate and send_quantum

+

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 (bytes/s)

+
§send_quantum: Option<u64>

Send Quantum (bytes) used to control the size of packet bursts

+

Trait Implementations§

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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/congestion/struct.Cubic.html b/pr/672/docs/noq_proto/congestion/struct.Cubic.html new file mode 100644 index 000000000..2011b0107 --- /dev/null +++ b/pr/672/docs/noq_proto/congestion/struct.Cubic.html @@ -0,0 +1,55 @@ +Cubic in noq_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, + _pn: 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, + _largest_lost_pn: 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 +This is also used to alter the pacing of the connection with +pacing_rate and send_quantum
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, largest_pn: u64)

One or more packets were just sent
Source§

fn on_packet_sent(&mut self, now: Instant, bytes: u16, pn: u64)

One packet was 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_packet_lost(&mut self, lost_bytes: u16, pn: u64, now: Instant)

One packet was just lost
Source§

fn on_ack_frequency_update( + &mut self, + ack_eliciting_threshold: u64, + requested_max_ack_delay: Duration, +)

The peer’s ACK-frequency parameters have changed Read more
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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/congestion/struct.CubicConfig.html b/pr/672/docs/noq_proto/congestion/struct.CubicConfig.html new file mode 100644 index 000000000..fd0aaf23c --- /dev/null +++ b/pr/672/docs/noq_proto/congestion/struct.CubicConfig.html @@ -0,0 +1,28 @@ +CubicConfig in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/congestion/struct.NewReno.html b/pr/672/docs/noq_proto/congestion/struct.NewReno.html new file mode 100644 index 000000000..87e0e4fc8 --- /dev/null +++ b/pr/672/docs/noq_proto/congestion/struct.NewReno.html @@ -0,0 +1,55 @@ +NewReno in noq_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, + _pn: 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, + _largest_lost_pn: 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 +This is also used to alter the pacing of the connection with +pacing_rate and send_quantum
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, largest_pn: u64)

One or more packets were just sent
Source§

fn on_packet_sent(&mut self, now: Instant, bytes: u16, pn: u64)

One packet was 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_packet_lost(&mut self, lost_bytes: u16, pn: u64, now: Instant)

One packet was just lost
Source§

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

fn on_ack_frequency_update( + &mut self, + ack_eliciting_threshold: u64, + requested_max_ack_delay: Duration, +)

The peer’s ACK-frequency parameters have changed 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/congestion/struct.NewRenoConfig.html b/pr/672/docs/noq_proto/congestion/struct.NewRenoConfig.html new file mode 100644 index 000000000..d840b20d5 --- /dev/null +++ b/pr/672/docs/noq_proto/congestion/struct.NewRenoConfig.html @@ -0,0 +1,29 @@ +NewRenoConfig in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/congestion/trait.Controller.html b/pr/672/docs/noq_proto/congestion/trait.Controller.html new file mode 100644 index 000000000..7a8d1b47a --- /dev/null +++ b/pr/672/docs/noq_proto/congestion/trait.Controller.html @@ -0,0 +1,107 @@ +Controller in noq_proto::congestion - Rust

Controller

Trait Controller 

Source
pub trait Controller:
+    Send
+    + Sync
+    + Debug {
+
Show 14 methods // Required methods + fn on_congestion_event( + &mut self, + now: Instant, + sent: Instant, + is_persistent_congestion: bool, + is_ecn: bool, + lost_bytes: u64, + largest_lost_pn: 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, largest_pn: u64) { ... } + fn on_packet_sent(&mut self, now: Instant, bytes: u16, pn: u64) { ... } + fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + pn: 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_packet_lost(&mut self, lost_bytes: u16, pn: u64, now: Instant) { ... } + fn on_spurious_congestion_event(&mut self) { ... } + fn on_ack_frequency_update( + &mut self, + ack_eliciting_threshold: u64, + requested_max_ack_delay: Duration, + ) { ... } + 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, + largest_lost_pn: 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. +largest_lost_pn indicates the packet number of the packet with the highest packet number +in the congestion event.

+
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, largest_pn: u64)

One or more packets were just sent

+
Source

fn on_packet_sent(&mut self, now: Instant, bytes: u16, pn: u64)

One packet was just sent

+
Source

fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + pn: 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_packet_lost(&mut self, lost_bytes: u16, pn: u64, now: Instant)

One packet was just lost

+
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 on_ack_frequency_update( + &mut self, + ack_eliciting_threshold: u64, + requested_max_ack_delay: Duration, +)

The peer’s ACK-frequency parameters have changed

+

ack_eliciting_threshold is the number of ack-eliciting packets the peer may receive +before being required to send an immediate ACK (per the QUIC ACK frequency extension). +requested_max_ack_delay is the maximum delay we asked the peer to wait before sending +an ACK when the threshold hasn’t been reached.

+

Controllers can use this to refine estimates that depend on peer ACK behavior (e.g. +BBR’s offload budget).

+
Source

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled +This is also used to alter the pacing of the connection with +pacing_rate and send_quantum

+

Implementors§

\ No newline at end of file diff --git a/pr/672/docs/noq_proto/congestion/trait.ControllerFactory.html b/pr/672/docs/noq_proto/congestion/trait.ControllerFactory.html new file mode 100644 index 000000000..f4defe94a --- /dev/null +++ b/pr/672/docs/noq_proto/congestion/trait.ControllerFactory.html @@ -0,0 +1,10 @@ +ControllerFactory in noq_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/672/docs/noq_proto/connection/assembler/struct.Chunk.html b/pr/672/docs/noq_proto/connection/assembler/struct.Chunk.html new file mode 100644 index 000000000..898446f8d --- /dev/null +++ b/pr/672/docs/noq_proto/connection/assembler/struct.Chunk.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.Chunk.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/datagrams/enum.SendDatagramError.html b/pr/672/docs/noq_proto/connection/datagrams/enum.SendDatagramError.html new file mode 100644 index 000000000..570c234b4 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/datagrams/enum.SendDatagramError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/enum.SendDatagramError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/datagrams/struct.Datagrams.html b/pr/672/docs/noq_proto/connection/datagrams/struct.Datagrams.html new file mode 100644 index 000000000..3045cc171 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/datagrams/struct.Datagrams.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.Datagrams.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/enum.ClosePathError.html b/pr/672/docs/noq_proto/connection/enum.ClosePathError.html new file mode 100644 index 000000000..32d2e4223 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/enum.ClosePathError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/enum.ClosePathError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/enum.ConnectionError.html b/pr/672/docs/noq_proto/connection/enum.ConnectionError.html new file mode 100644 index 000000000..3f1a9fbfb --- /dev/null +++ b/pr/672/docs/noq_proto/connection/enum.ConnectionError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/enum.ConnectionError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/enum.Event.html b/pr/672/docs/noq_proto/connection/enum.Event.html new file mode 100644 index 000000000..77cd05a9b --- /dev/null +++ b/pr/672/docs/noq_proto/connection/enum.Event.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/enum.Event.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/enum.PathError.html b/pr/672/docs/noq_proto/connection/enum.PathError.html new file mode 100644 index 000000000..95276db00 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/enum.PathError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/enum.PathError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/paths/enum.PathAbandonReason.html b/pr/672/docs/noq_proto/connection/paths/enum.PathAbandonReason.html new file mode 100644 index 000000000..8aa633327 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/paths/enum.PathAbandonReason.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/enum.PathAbandonReason.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/paths/enum.PathEvent.html b/pr/672/docs/noq_proto/connection/paths/enum.PathEvent.html new file mode 100644 index 000000000..38b2b2c8b --- /dev/null +++ b/pr/672/docs/noq_proto/connection/paths/enum.PathEvent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/enum.PathEvent.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/paths/enum.PathStatus.html b/pr/672/docs/noq_proto/connection/paths/enum.PathStatus.html new file mode 100644 index 000000000..de11e934a --- /dev/null +++ b/pr/672/docs/noq_proto/connection/paths/enum.PathStatus.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/enum.PathStatus.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/paths/enum.SetPathStatusError.html b/pr/672/docs/noq_proto/connection/paths/enum.SetPathStatusError.html new file mode 100644 index 000000000..5390959b2 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/paths/enum.SetPathStatusError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/enum.SetPathStatusError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/paths/struct.ClosedPath.html b/pr/672/docs/noq_proto/connection/paths/struct.ClosedPath.html new file mode 100644 index 000000000..f5d8d15d2 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/paths/struct.ClosedPath.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.ClosedPath.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/paths/struct.PathId.html b/pr/672/docs/noq_proto/connection/paths/struct.PathId.html new file mode 100644 index 000000000..ba917e8fb --- /dev/null +++ b/pr/672/docs/noq_proto/connection/paths/struct.PathId.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.PathId.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/paths/struct.RttEstimator.html b/pr/672/docs/noq_proto/connection/paths/struct.RttEstimator.html new file mode 100644 index 000000000..781ef4877 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/paths/struct.RttEstimator.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.RttEstimator.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/send_buffer/send_buffer_benches/fn.get_into_many_segments.html b/pr/672/docs/noq_proto/connection/send_buffer/send_buffer_benches/fn.get_into_many_segments.html new file mode 100644 index 000000000..b9ff60d86 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/send_buffer/send_buffer_benches/fn.get_into_many_segments.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../noq_proto/bench_exports/send_buffer_benches/fn.get_into_many_segments.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/send_buffer/send_buffer_benches/fn.get_loop_many_segments.html b/pr/672/docs/noq_proto/connection/send_buffer/send_buffer_benches/fn.get_loop_many_segments.html new file mode 100644 index 000000000..86b89ce35 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/send_buffer/send_buffer_benches/fn.get_loop_many_segments.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../noq_proto/bench_exports/send_buffer_benches/fn.get_loop_many_segments.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/send_buffer/send_buffer_benches/index.html b/pr/672/docs/noq_proto/connection/send_buffer/send_buffer_benches/index.html new file mode 100644 index 000000000..e31ae92ab --- /dev/null +++ b/pr/672/docs/noq_proto/connection/send_buffer/send_buffer_benches/index.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../noq_proto/bench_exports/send_buffer_benches/index.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/stats/struct.ConnectionStats.html b/pr/672/docs/noq_proto/connection/stats/struct.ConnectionStats.html new file mode 100644 index 000000000..595a1ff81 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/stats/struct.ConnectionStats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.ConnectionStats.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/stats/struct.FrameStats.html b/pr/672/docs/noq_proto/connection/stats/struct.FrameStats.html new file mode 100644 index 000000000..9474dc3bc --- /dev/null +++ b/pr/672/docs/noq_proto/connection/stats/struct.FrameStats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.FrameStats.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/stats/struct.PathStats.html b/pr/672/docs/noq_proto/connection/stats/struct.PathStats.html new file mode 100644 index 000000000..4739ace0a --- /dev/null +++ b/pr/672/docs/noq_proto/connection/stats/struct.PathStats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.PathStats.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/stats/struct.UdpStats.html b/pr/672/docs/noq_proto/connection/stats/struct.UdpStats.html new file mode 100644 index 000000000..c1f56ed4c --- /dev/null +++ b/pr/672/docs/noq_proto/connection/stats/struct.UdpStats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.UdpStats.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/streams/enum.StreamEvent.html b/pr/672/docs/noq_proto/connection/streams/enum.StreamEvent.html new file mode 100644 index 000000000..09af0d7ec --- /dev/null +++ b/pr/672/docs/noq_proto/connection/streams/enum.StreamEvent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/enum.StreamEvent.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/streams/recv/enum.ReadError.html b/pr/672/docs/noq_proto/connection/streams/recv/enum.ReadError.html new file mode 100644 index 000000000..d00ebffa7 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/streams/recv/enum.ReadError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../noq_proto/enum.ReadError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/streams/recv/enum.ReadableError.html b/pr/672/docs/noq_proto/connection/streams/recv/enum.ReadableError.html new file mode 100644 index 000000000..c4752b3f5 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/streams/recv/enum.ReadableError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../noq_proto/enum.ReadableError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/streams/recv/struct.Chunks.html b/pr/672/docs/noq_proto/connection/streams/recv/struct.Chunks.html new file mode 100644 index 000000000..d1cd9b72f --- /dev/null +++ b/pr/672/docs/noq_proto/connection/streams/recv/struct.Chunks.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../noq_proto/struct.Chunks.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/streams/send/enum.FinishError.html b/pr/672/docs/noq_proto/connection/streams/send/enum.FinishError.html new file mode 100644 index 000000000..99e7b1b9d --- /dev/null +++ b/pr/672/docs/noq_proto/connection/streams/send/enum.FinishError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../noq_proto/enum.FinishError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/streams/send/enum.WriteError.html b/pr/672/docs/noq_proto/connection/streams/send/enum.WriteError.html new file mode 100644 index 000000000..7ad6dec0c --- /dev/null +++ b/pr/672/docs/noq_proto/connection/streams/send/enum.WriteError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../noq_proto/enum.WriteError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/streams/struct.ClosedStream.html b/pr/672/docs/noq_proto/connection/streams/struct.ClosedStream.html new file mode 100644 index 000000000..3b52993d9 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/streams/struct.ClosedStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.ClosedStream.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/streams/struct.RecvStream.html b/pr/672/docs/noq_proto/connection/streams/struct.RecvStream.html new file mode 100644 index 000000000..dbc5e85a0 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/streams/struct.RecvStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.RecvStream.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/streams/struct.SendStream.html b/pr/672/docs/noq_proto/connection/streams/struct.SendStream.html new file mode 100644 index 000000000..b223bbbe1 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/streams/struct.SendStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.SendStream.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/streams/struct.ShouldTransmit.html b/pr/672/docs/noq_proto/connection/streams/struct.ShouldTransmit.html new file mode 100644 index 000000000..9c9d31ae9 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/streams/struct.ShouldTransmit.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.ShouldTransmit.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/streams/struct.Streams.html b/pr/672/docs/noq_proto/connection/streams/struct.Streams.html new file mode 100644 index 000000000..e1cb86450 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/streams/struct.Streams.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/struct.Streams.html...

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

Redirecting to ../../noq_proto/struct.Connection.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/struct.MultipathNotNegotiated.html b/pr/672/docs/noq_proto/connection/struct.MultipathNotNegotiated.html new file mode 100644 index 000000000..64d88689a --- /dev/null +++ b/pr/672/docs/noq_proto/connection/struct.MultipathNotNegotiated.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.MultipathNotNegotiated.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/connection/trait.NetworkChangeHint.html b/pr/672/docs/noq_proto/connection/trait.NetworkChangeHint.html new file mode 100644 index 000000000..55ed376f5 --- /dev/null +++ b/pr/672/docs/noq_proto/connection/trait.NetworkChangeHint.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/trait.NetworkChangeHint.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html b/pr/672/docs/noq_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html new file mode 100644 index 000000000..056e4ed87 --- /dev/null +++ b/pr/672/docs/noq_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html @@ -0,0 +1,2 @@ +DEFAULT_SUPPORTED_VERSIONS in noq_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/672/docs/noq_proto/crypto/index.html b/pr/672/docs/noq_proto/crypto/index.html new file mode 100644 index 000000000..72a4880e1 --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/index.html @@ -0,0 +1,8 @@ +noq_proto::crypto - Rust

Module crypto

Module crypto 

Source
Expand description

Traits and implementations for the QUIC cryptography protocol

+

The protocol logic in noq 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§

ClientConfig
Client-side configuration for the crypto protocol
HandshakeTokenKey
The trait for encrypting tokens that power retry packets and NEW_TOKEN frames.
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/672/docs/noq_proto/crypto/rustls/enum.Error.html b/pr/672/docs/noq_proto/crypto/rustls/enum.Error.html new file mode 100644 index 000000000..04bd994c9 --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/rustls/enum.Error.html @@ -0,0 +1,100 @@ +Error in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/crypto/rustls/index.html b/pr/672/docs/noq_proto/crypto/rustls/index.html new file mode 100644 index 000000000..c684d0969 --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/rustls/index.html @@ -0,0 +1,2 @@ +noq_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/672/docs/noq_proto/crypto/rustls/sidebar-items.js b/pr/672/docs/noq_proto/crypto/rustls/sidebar-items.js new file mode 100644 index 000000000..65f98d4d4 --- /dev/null +++ b/pr/672/docs/noq_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/672/docs/noq_proto/crypto/rustls/struct.HandshakeData.html b/pr/672/docs/noq_proto/crypto/rustls/struct.HandshakeData.html new file mode 100644 index 000000000..1b2e811e7 --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/rustls/struct.HandshakeData.html @@ -0,0 +1,34 @@ +HandshakeData in noq_proto::crypto::rustls - Rust

HandshakeData

Struct HandshakeData 

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

Authentication data for (rustls) TLS session

+

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.
§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: Option<NamedGroup>

The key exchange group negotiated with the peer. Useful for logging and metrics.

+

None until the handshake has completed.

+

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/crypto/rustls/struct.NoInitialCipherSuite.html b/pr/672/docs/noq_proto/crypto/rustls/struct.NoInitialCipherSuite.html new file mode 100644 index 000000000..9d63550e6 --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/rustls/struct.NoInitialCipherSuite.html @@ -0,0 +1,30 @@ +NoInitialCipherSuite in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

QuicClientConfig

Struct QuicClientConfig 

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

A QUIC-compatible TLS client configuration

+

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

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

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

+

Implementations§

Source§

impl QuicClientConfig

Source

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

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

+

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

+
Source

pub fn set_alpn_protocols(&mut self, alpn_protocols: Vec<Vec<u8>>)

Updates the set of ALPN protocols configured in the client config.

+

Trait Implementations§

Source§

impl ClientConfig for QuicClientConfig

Source§

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

Start a client session with this configuration
Source§

impl Clone for QuicClientConfig

Source§

fn clone(&self) -> QuicClientConfig

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 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> 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/crypto/rustls/struct.QuicServerConfig.html b/pr/672/docs/noq_proto/crypto/rustls/struct.QuicServerConfig.html new file mode 100644 index 000000000..36765b5e8 --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/rustls/struct.QuicServerConfig.html @@ -0,0 +1,54 @@ +QuicServerConfig in noq_proto::crypto::rustls - Rust

QuicServerConfig

Struct QuicServerConfig 

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

A QUIC-compatible TLS server configuration

+

noq 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.

+
Source

pub fn set_alpn_protocols(&mut self, alpn_protocols: Vec<Vec<u8>>)

Updates the set of ALPN protocols configured in the server config.

+

Trait Implementations§

Source§

impl Clone for QuicServerConfig

Source§

fn clone(&self) -> QuicServerConfig

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 ServerConfig for QuicServerConfig

Source§

fn start_session( + &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> 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/crypto/rustls/struct.TlsSession.html b/pr/672/docs/noq_proto/crypto/rustls/struct.TlsSession.html new file mode 100644 index 000000000..60bb11931 --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/rustls/struct.TlsSession.html @@ -0,0 +1,39 @@ +TlsSession in noq_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 Read more
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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/crypto/sidebar-items.js b/pr/672/docs/noq_proto/crypto/sidebar-items.js new file mode 100644 index 000000000..710b27671 --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["rustls"],"struct":["CryptoError","ExportKeyingMaterialError","KeyPair","Keys","UnsupportedVersion"],"trait":["ClientConfig","HandshakeTokenKey","HeaderKey","HmacKey","PacketKey","ServerConfig","Session"]}; \ No newline at end of file diff --git a/pr/672/docs/noq_proto/crypto/struct.CryptoError.html b/pr/672/docs/noq_proto/crypto/struct.CryptoError.html new file mode 100644 index 000000000..210ea40f9 --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/struct.CryptoError.html @@ -0,0 +1,24 @@ +CryptoError in noq_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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> 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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/crypto/struct.ExportKeyingMaterialError.html b/pr/672/docs/noq_proto/crypto/struct.ExportKeyingMaterialError.html new file mode 100644 index 000000000..d54cca3e8 --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/struct.ExportKeyingMaterialError.html @@ -0,0 +1,30 @@ +ExportKeyingMaterialError in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/crypto/struct.KeyPair.html b/pr/672/docs/noq_proto/crypto/struct.KeyPair.html new file mode 100644 index 000000000..8c22779ba --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/struct.KeyPair.html @@ -0,0 +1,35 @@ +KeyPair in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/crypto/struct.Keys.html b/pr/672/docs/noq_proto/crypto/struct.Keys.html new file mode 100644 index 000000000..137d8a10f --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/struct.Keys.html @@ -0,0 +1,29 @@ +Keys in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/crypto/struct.UnsupportedVersion.html b/pr/672/docs/noq_proto/crypto/struct.UnsupportedVersion.html new file mode 100644 index 000000000..5e1f49729 --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/struct.UnsupportedVersion.html @@ -0,0 +1,24 @@ +UnsupportedVersion in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/crypto/trait.ClientConfig.html b/pr/672/docs/noq_proto/crypto/trait.ClientConfig.html new file mode 100644 index 000000000..2ee668b52 --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/trait.ClientConfig.html @@ -0,0 +1,16 @@ +ClientConfig in noq_proto::crypto - Rust

ClientConfig

Trait ClientConfig 

Source
pub trait ClientConfig: Send + Sync {
+    // Required method
+    fn start_session(
+        &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, + 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/672/docs/noq_proto/crypto/trait.HandshakeTokenKey.html b/pr/672/docs/noq_proto/crypto/trait.HandshakeTokenKey.html new file mode 100644 index 000000000..0aebdede2 --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/trait.HandshakeTokenKey.html @@ -0,0 +1,23 @@ +HandshakeTokenKey in noq_proto::crypto - Rust

HandshakeTokenKey

Trait HandshakeTokenKey 

Source
pub trait HandshakeTokenKey: Send + Sync {
+    // Required methods
+    fn seal(
+        &self,
+        token_nonce: u128,
+        data: &mut Vec<u8>,
+    ) -> Result<(), CryptoError>;
+    fn open<'a>(
+        &self,
+        token_nonce: u128,
+        data: &'a mut [u8],
+    ) -> Result<&'a [u8], CryptoError>;
+}
Expand description

The trait for encrypting tokens that power retry packets and NEW_TOKEN frames.

+

Required Methods§

Source

fn seal(&self, token_nonce: u128, data: &mut Vec<u8>) -> Result<(), CryptoError>

Method for sealing a token in-place.

+

The nonce doesn’t need to be attached to the ciphertext, +but the authentication tag is expected to be appended to data.

+
Source

fn open<'a>( + &self, + token_nonce: u128, + data: &'a mut [u8], +) -> Result<&'a [u8], CryptoError>

Method for opening a sealed message data in-place.

+

Returns the portion of data that contains the decrypted plaintext.

+

Implementors§

\ No newline at end of file diff --git a/pr/672/docs/noq_proto/crypto/trait.HeaderKey.html b/pr/672/docs/noq_proto/crypto/trait.HeaderKey.html new file mode 100644 index 000000000..0e3bdd651 --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/trait.HeaderKey.html @@ -0,0 +1,10 @@ +HeaderKey in noq_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/672/docs/noq_proto/crypto/trait.HmacKey.html b/pr/672/docs/noq_proto/crypto/trait.HmacKey.html new file mode 100644 index 000000000..5fae9f16e --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/trait.HmacKey.html @@ -0,0 +1,10 @@ +HmacKey in noq_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/672/docs/noq_proto/crypto/trait.PacketKey.html b/pr/672/docs/noq_proto/crypto/trait.PacketKey.html new file mode 100644 index 000000000..329f611a5 --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/trait.PacketKey.html @@ -0,0 +1,51 @@ +PacketKey in noq_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/672/docs/noq_proto/crypto/trait.ServerConfig.html b/pr/672/docs/noq_proto/crypto/trait.ServerConfig.html new file mode 100644 index 000000000..1d910fe9d --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/trait.ServerConfig.html @@ -0,0 +1,39 @@ +ServerConfig in noq_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,
+        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.

+

See the specification: https://www.rfc-editor.org/rfc/rfc9001#name-retry-packet-integrity

+
Source

fn start_session( + &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/672/docs/noq_proto/crypto/trait.Session.html b/pr/672/docs/noq_proto/crypto/trait.Session.html new file mode 100644 index 000000000..cbef4f7e0 --- /dev/null +++ b/pr/672/docs/noq_proto/crypto/trait.Session.html @@ -0,0 +1,73 @@ +Session in noq_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

+

See the specification: https://www.rfc-editor.org/rfc/rfc9001#name-retry-packet-integrity

+
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/672/docs/noq_proto/endpoint/enum.ConnectError.html b/pr/672/docs/noq_proto/endpoint/enum.ConnectError.html new file mode 100644 index 000000000..8ef61a0a8 --- /dev/null +++ b/pr/672/docs/noq_proto/endpoint/enum.ConnectError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/enum.ConnectError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/endpoint/enum.DatagramEvent.html b/pr/672/docs/noq_proto/endpoint/enum.DatagramEvent.html new file mode 100644 index 000000000..61330e877 --- /dev/null +++ b/pr/672/docs/noq_proto/endpoint/enum.DatagramEvent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/enum.DatagramEvent.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/endpoint/struct.AcceptError.html b/pr/672/docs/noq_proto/endpoint/struct.AcceptError.html new file mode 100644 index 000000000..6356d3a2b --- /dev/null +++ b/pr/672/docs/noq_proto/endpoint/struct.AcceptError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.AcceptError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/endpoint/struct.ConnectionHandle.html b/pr/672/docs/noq_proto/endpoint/struct.ConnectionHandle.html new file mode 100644 index 000000000..3a4fb506e --- /dev/null +++ b/pr/672/docs/noq_proto/endpoint/struct.ConnectionHandle.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.ConnectionHandle.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/endpoint/struct.DecryptedInitial.html b/pr/672/docs/noq_proto/endpoint/struct.DecryptedInitial.html new file mode 100644 index 000000000..5f603db81 --- /dev/null +++ b/pr/672/docs/noq_proto/endpoint/struct.DecryptedInitial.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.DecryptedInitial.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/endpoint/struct.Endpoint.html b/pr/672/docs/noq_proto/endpoint/struct.Endpoint.html new file mode 100644 index 000000000..2a38c917a --- /dev/null +++ b/pr/672/docs/noq_proto/endpoint/struct.Endpoint.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.Endpoint.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/endpoint/struct.Incoming.html b/pr/672/docs/noq_proto/endpoint/struct.Incoming.html new file mode 100644 index 000000000..50681ded7 --- /dev/null +++ b/pr/672/docs/noq_proto/endpoint/struct.Incoming.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.Incoming.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/endpoint/struct.IncomingAlpns.html b/pr/672/docs/noq_proto/endpoint/struct.IncomingAlpns.html new file mode 100644 index 000000000..552057caf --- /dev/null +++ b/pr/672/docs/noq_proto/endpoint/struct.IncomingAlpns.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.IncomingAlpns.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/endpoint/struct.RetryError.html b/pr/672/docs/noq_proto/endpoint/struct.RetryError.html new file mode 100644 index 000000000..1f8680683 --- /dev/null +++ b/pr/672/docs/noq_proto/endpoint/struct.RetryError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.RetryError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/enum.ClosePathError.html b/pr/672/docs/noq_proto/enum.ClosePathError.html new file mode 100644 index 000000000..c6d1072da --- /dev/null +++ b/pr/672/docs/noq_proto/enum.ClosePathError.html @@ -0,0 +1,40 @@ +ClosePathError in noq_proto - Rust

ClosePathError

Enum ClosePathError 

Source
pub enum ClosePathError {
+    MultipathNotNegotiated,
+    ClosedPath,
+    LastOpenPath,
+}
Expand description

Errors triggered when abandoning a path

+

Variants§

§

MultipathNotNegotiated

Multipath is not negotiated

+
§

ClosedPath

The path is already closed or was never opened

+
§

LastOpenPath

Cannot close the last remaining open path via the local API.

+

Use Connection::close to end the connection instead.

+

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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/enum.ConfigError.html b/pr/672/docs/noq_proto/enum.ConfigError.html new file mode 100644 index 000000000..3f8b299fe --- /dev/null +++ b/pr/672/docs/noq_proto/enum.ConfigError.html @@ -0,0 +1,35 @@ +ConfigError in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/enum.ConnectError.html b/pr/672/docs/noq_proto/enum.ConnectError.html new file mode 100644 index 000000000..9293e7368 --- /dev/null +++ b/pr/672/docs/noq_proto/enum.ConnectError.html @@ -0,0 +1,50 @@ +ConnectError in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/enum.ConnectionError.html b/pr/672/docs/noq_proto/enum.ConnectionError.html new file mode 100644 index 000000000..0730b0353 --- /dev/null +++ b/pr/672/docs/noq_proto/enum.ConnectionError.html @@ -0,0 +1,53 @@ +ConnectionError in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/enum.DatagramEvent.html b/pr/672/docs/noq_proto/enum.DatagramEvent.html new file mode 100644 index 000000000..e00720c5e --- /dev/null +++ b/pr/672/docs/noq_proto/enum.DatagramEvent.html @@ -0,0 +1,31 @@ +DatagramEvent in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/enum.Dir.html b/pr/672/docs/noq_proto/enum.Dir.html new file mode 100644 index 000000000..6e6be725a --- /dev/null +++ b/pr/672/docs/noq_proto/enum.Dir.html @@ -0,0 +1,52 @@ +Dir in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/enum.EcnCodepoint.html b/pr/672/docs/noq_proto/enum.EcnCodepoint.html new file mode 100644 index 000000000..5c3faa6ac --- /dev/null +++ b/pr/672/docs/noq_proto/enum.EcnCodepoint.html @@ -0,0 +1,40 @@ +EcnCodepoint in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/enum.Event.html b/pr/672/docs/noq_proto/enum.Event.html new file mode 100644 index 000000000..03caffa42 --- /dev/null +++ b/pr/672/docs/noq_proto/enum.Event.html @@ -0,0 +1,50 @@ +Event in noq_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 when the connection is closed due to an error, a timeout, or the peer closing it. +This is not emitted when the local application closes the connection via +Connection::close(). In that case, pending operations will +fail with ConnectionError::LocallyClosed.

+

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)

n0’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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/enum.FinishError.html b/pr/672/docs/noq_proto/enum.FinishError.html new file mode 100644 index 000000000..813eaad94 --- /dev/null +++ b/pr/672/docs/noq_proto/enum.FinishError.html @@ -0,0 +1,39 @@ +FinishError in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/enum.FrameType.html b/pr/672/docs/noq_proto/enum.FrameType.html new file mode 100644 index 000000000..047a105c4 --- /dev/null +++ b/pr/672/docs/noq_proto/enum.FrameType.html @@ -0,0 +1,76 @@ +FrameType in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/enum.LongType.html b/pr/672/docs/noq_proto/enum.LongType.html new file mode 100644 index 000000000..77ee4f3ff --- /dev/null +++ b/pr/672/docs/noq_proto/enum.LongType.html @@ -0,0 +1,36 @@ +LongType in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/enum.MaybeFrame.html b/pr/672/docs/noq_proto/enum.MaybeFrame.html new file mode 100644 index 000000000..5951bf116 --- /dev/null +++ b/pr/672/docs/noq_proto/enum.MaybeFrame.html @@ -0,0 +1,39 @@ +MaybeFrame in noq_proto - Rust

MaybeFrame

Enum MaybeFrame 

Source
pub enum MaybeFrame {
+    None,
+    Unknown(u64),
+    Known(FrameType),
+}
Expand description

The type used to refer to FrameTypes in closing and transport errors.

+

Variants§

§

None

Not attributed to any particular FrameType.

+
§

Unknown(u64)

Attributed to some frame type this implementation does not recognize.

+
§

Known(FrameType)

Attributed to a specific FrameType, never FrameType::Padding.

+

Trait Implementations§

Source§

impl Clone for MaybeFrame

Source§

fn clone(&self) -> MaybeFrame

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for MaybeFrame

Source§

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

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

impl Decodable for MaybeFrame

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Display for MaybeFrame

Source§

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

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

impl Encodable for MaybeFrame

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Append the encoding of self to the provided buffer.
Source§

impl PartialEq for MaybeFrame

Source§

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

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

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

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

impl Copy for MaybeFrame

Source§

impl Eq for MaybeFrame

Source§

impl StructuralPartialEq for MaybeFrame

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

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

§

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

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

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

§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/enum.PacketDecodeError.html b/pr/672/docs/noq_proto/enum.PacketDecodeError.html new file mode 100644 index 000000000..35066327b --- /dev/null +++ b/pr/672/docs/noq_proto/enum.PacketDecodeError.html @@ -0,0 +1,54 @@ +PacketDecodeError in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/enum.PathAbandonReason.html b/pr/672/docs/noq_proto/enum.PathAbandonReason.html new file mode 100644 index 000000000..3c0d584f6 --- /dev/null +++ b/pr/672/docs/noq_proto/enum.PathAbandonReason.html @@ -0,0 +1,48 @@ +PathAbandonReason in noq_proto - Rust

PathAbandonReason

Enum PathAbandonReason 

Source
pub enum PathAbandonReason {
+    ApplicationClosed {
+        error_code: VarInt,
+    },
+    ValidationFailed,
+    TimedOut,
+    UnusableAfterNetworkChange,
+    RemoteAbandoned {
+        error_code: VarInt,
+    },
+}
Expand description

Reason for why a path was abandoned.

+

Variants§

§

ApplicationClosed

The path was closed locally by the application.

+

Fields

§error_code: VarInt

The error code to be sent with the abandon frame.

+
§

ValidationFailed

We didn’t receive a path response in time after opening this path.

+
§

TimedOut

We didn’t receive any data from the remote within the path’s idle timeout.

+
§

UnusableAfterNetworkChange

The path became unusable after a local network change.

+
§

RemoteAbandoned

The remote closed the path.

+

Fields

§error_code: VarInt

The error that was sent with the abandon frame.

+

Trait Implementations§

Source§

impl Clone for PathAbandonReason

Source§

fn clone(&self) -> PathAbandonReason

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 PathAbandonReason

Source§

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

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

impl PartialEq for PathAbandonReason

Source§

fn eq(&self, other: &PathAbandonReason) -> 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 PathAbandonReason

Source§

impl StructuralPartialEq for PathAbandonReason

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/enum.PathError.html b/pr/672/docs/noq_proto/enum.PathError.html new file mode 100644 index 000000000..c3707ab7b --- /dev/null +++ b/pr/672/docs/noq_proto/enum.PathError.html @@ -0,0 +1,45 @@ +PathError in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/enum.PathEvent.html b/pr/672/docs/noq_proto/enum.PathEvent.html new file mode 100644 index 000000000..bd1ea5bc4 --- /dev/null +++ b/pr/672/docs/noq_proto/enum.PathEvent.html @@ -0,0 +1,71 @@ +PathEvent in noq_proto - Rust

PathEvent

Enum PathEvent 

Source
#[non_exhaustive]
pub enum PathEvent { +
#[non_exhaustive]
Established { + id: PathId, + }, +
#[non_exhaustive]
Abandoned { + id: PathId, + reason: PathAbandonReason, + }, +
#[non_exhaustive]
Discarded { + id: PathId, + path_stats: Box<PathStats>, + }, +
#[non_exhaustive]
RemoteStatus { + id: PathId, + status: PathStatus, + }, +
#[non_exhaustive]
ObservedAddr { + id: PathId, + addr: SocketAddr, + }, +}
Expand description

Application events about paths

+

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

#[non_exhaustive]
Established

A new path has established connection with the peer.

+

Fields

This variant is marked as non-exhaustive
Non-exhaustive enum variants could have additional fields added in future. Therefore, non-exhaustive enum variants cannot be constructed in external crates and cannot be matched against.
§id: PathId

The path which can now be used for application data.

+
§

#[non_exhaustive]
Abandoned

A path was abandoned and is no longer usable.

+

This event will always be followed by Self::Discarded after some time.

+

Fields

This variant is marked as non-exhaustive
Non-exhaustive enum variants could have additional fields added in future. Therefore, non-exhaustive enum variants cannot be constructed in external crates and cannot be matched against.
§id: PathId

With path was abandoned.

+
§reason: PathAbandonReason

Reason why this path was abandoned.

+
§

#[non_exhaustive]
Discarded

A path was discarded and all remaining state for it has been removed.

+

This event is the last event for a path, and is always emitted after Self::Abandoned.

+

Fields

This variant is marked as non-exhaustive
Non-exhaustive enum variants could have additional fields added in future. Therefore, non-exhaustive enum variants cannot be constructed in external crates and cannot be matched against.
§id: PathId

Which path had its state dropped

+
§path_stats: Box<PathStats>

The final path stats, they are no longer available via Connection::stats

+
§

#[non_exhaustive]
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

This variant is marked as non-exhaustive
Non-exhaustive enum variants could have additional fields added in future. Therefore, non-exhaustive enum variants cannot be constructed in external crates and cannot be matched against.
§id: PathId

Path which has changed status

+
§status: PathStatus

The new status set by the remote

+
§

#[non_exhaustive]
ObservedAddr

Received an observation of our external address from the peer.

+

Fields

This variant is marked as non-exhaustive
Non-exhaustive enum variants could have additional fields added in future. Therefore, non-exhaustive enum variants cannot be constructed in external crates and cannot be matched against.
§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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/enum.PathStatus.html b/pr/672/docs/noq_proto/enum.PathStatus.html new file mode 100644 index 000000000..803487d80 --- /dev/null +++ b/pr/672/docs/noq_proto/enum.PathStatus.html @@ -0,0 +1,42 @@ +PathStatus in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/enum.ProtectedHeader.html b/pr/672/docs/noq_proto/enum.ProtectedHeader.html new file mode 100644 index 000000000..a1b9dbb82 --- /dev/null +++ b/pr/672/docs/noq_proto/enum.ProtectedHeader.html @@ -0,0 +1,74 @@ +ProtectedHeader in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/enum.ReadError.html b/pr/672/docs/noq_proto/enum.ReadError.html new file mode 100644 index 000000000..6d5d42590 --- /dev/null +++ b/pr/672/docs/noq_proto/enum.ReadError.html @@ -0,0 +1,50 @@ +ReadError in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/enum.ReadableError.html b/pr/672/docs/noq_proto/enum.ReadableError.html new file mode 100644 index 000000000..f90d4ba0c --- /dev/null +++ b/pr/672/docs/noq_proto/enum.ReadableError.html @@ -0,0 +1,49 @@ +ReadableError in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/enum.SendDatagramError.html b/pr/672/docs/noq_proto/enum.SendDatagramError.html new file mode 100644 index 000000000..c29ae2a77 --- /dev/null +++ b/pr/672/docs/noq_proto/enum.SendDatagramError.html @@ -0,0 +1,53 @@ +SendDatagramError in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/enum.SetPathStatusError.html b/pr/672/docs/noq_proto/enum.SetPathStatusError.html new file mode 100644 index 000000000..5bf1ccedc --- /dev/null +++ b/pr/672/docs/noq_proto/enum.SetPathStatusError.html @@ -0,0 +1,37 @@ +SetPathStatusError in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/enum.Side.html b/pr/672/docs/noq_proto/enum.Side.html new file mode 100644 index 000000000..60e140589 --- /dev/null +++ b/pr/672/docs/noq_proto/enum.Side.html @@ -0,0 +1,53 @@ +Side in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/enum.StreamEvent.html b/pr/672/docs/noq_proto/enum.StreamEvent.html new file mode 100644 index 000000000..016fbe344 --- /dev/null +++ b/pr/672/docs/noq_proto/enum.StreamEvent.html @@ -0,0 +1,63 @@ +StreamEvent in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/enum.WriteError.html b/pr/672/docs/noq_proto/enum.WriteError.html new file mode 100644 index 000000000..daf5f6ffb --- /dev/null +++ b/pr/672/docs/noq_proto/enum.WriteError.html @@ -0,0 +1,53 @@ +WriteError in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/frame/enum.FrameType.html b/pr/672/docs/noq_proto/frame/enum.FrameType.html new file mode 100644 index 000000000..488123b8f --- /dev/null +++ b/pr/672/docs/noq_proto/frame/enum.FrameType.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/enum.FrameType.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/frame/enum.MaybeFrame.html b/pr/672/docs/noq_proto/frame/enum.MaybeFrame.html new file mode 100644 index 000000000..d93dce96c --- /dev/null +++ b/pr/672/docs/noq_proto/frame/enum.MaybeFrame.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/enum.MaybeFrame.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/frame/struct.ApplicationClose.html b/pr/672/docs/noq_proto/frame/struct.ApplicationClose.html new file mode 100644 index 000000000..b065b5302 --- /dev/null +++ b/pr/672/docs/noq_proto/frame/struct.ApplicationClose.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.ApplicationClose.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/frame/struct.ConnectionClose.html b/pr/672/docs/noq_proto/frame/struct.ConnectionClose.html new file mode 100644 index 000000000..040a8dc2a --- /dev/null +++ b/pr/672/docs/noq_proto/frame/struct.ConnectionClose.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.ConnectionClose.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/frame/struct.Datagram.html b/pr/672/docs/noq_proto/frame/struct.Datagram.html new file mode 100644 index 000000000..8fdb7d9f7 --- /dev/null +++ b/pr/672/docs/noq_proto/frame/struct.Datagram.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.Datagram.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/frame/struct.DatagramInfo.html b/pr/672/docs/noq_proto/frame/struct.DatagramInfo.html new file mode 100644 index 000000000..8bc2fc4fb --- /dev/null +++ b/pr/672/docs/noq_proto/frame/struct.DatagramInfo.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.DatagramInfo.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/frame/struct.InvalidFrameId.html b/pr/672/docs/noq_proto/frame/struct.InvalidFrameId.html new file mode 100644 index 000000000..384bde4f9 --- /dev/null +++ b/pr/672/docs/noq_proto/frame/struct.InvalidFrameId.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.InvalidFrameId.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/frame/struct.StreamInfo.html b/pr/672/docs/noq_proto/frame/struct.StreamInfo.html new file mode 100644 index 000000000..032d16b41 --- /dev/null +++ b/pr/672/docs/noq_proto/frame/struct.StreamInfo.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.StreamInfo.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/index.html b/pr/672/docs/noq_proto/index.html new file mode 100644 index 000000000..b4a560336 --- /dev/null +++ b/pr/672/docs/noq_proto/index.html @@ -0,0 +1,14 @@ +noq_proto - Rust

Crate noq_proto

Crate noq_proto 

Source
Expand description

Low-level protocol logic for the QUIC protoocol

+

noq-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 noq API instead.

+

The noq-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
n0_nat_traversal
n0’s (https://n0.computer) NAT Traversal protocol implementation.
transport_parameters
QUIC connection transport parameters

Structs§

AcceptError
Error type for attempting to accept an Incoming
AckFrequencyConfig
Parameters for controlling the peer’s acknowledgement frequency
ApplicationClose
Reason given by an application for closing the connection
BloomTokenLog
Bloom filter-based TokenLog
Chunk
A chunk of data from the receive stream
Chunks
Chunks returned from RecvStream::read().
ClientConfig
Configuration for outgoing connections
ClosedPath
Error indicating that a path has not been opened or has already been abandoned
ClosedStream
Error indicating that a stream has not been opened or has already been finished or reset
Connection
Protocol state and logic for a single QUIC connection
ConnectionClose
Reason given by the transport for closing the connection
ConnectionEvent
Events sent from an Endpoint to a Connection
ConnectionHandle
Internal identifier for a Connection currently associated with an endpoint
ConnectionId
Protocol-level identifier for a connection.
ConnectionStats
Connection statistics.
Datagram
An unreliable datagram
DatagramInfo
Datagrams
API to control datagram traffic
DecryptedInitial
Decrypted payload of a QUIC Initial packet
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.
IncomingAlpns
Iterator over ALPN protocol names from a TLS ClientHello
InvalidCid
The connection ID was not recognized by the ConnectionIdGenerator
InvalidFrameId
Encounter a frame ID that was not valid.
MtuDiscoveryConfig
Parameters governing MTU discovery.
MultipathNotNegotiated
Error when the multipath extension was not negotiated, but attempted to be used.
NoneTokenLog
Null implementation of TokenLog, which never accepts tokens
NoneTokenStore
Null implementation of TokenStore, which does not store any tokens
PartialDecode
Decodes a QUIC packet’s invariant header
PathId
Id representing different paths when using multipath extension
PathStats
Statistics related to a transmission path.
ProtectedInitialHeader
Header of an Initial packet, before decryption
QlogConfig
Configuration for qlog trace logging.
QlogFileFactory
Enables writing qlog traces to a directory.
RandomConnectionIdGenerator
Generates purely random connection IDs of a specified length
RecvStream
Access to streams
RetryError
Error for attempting to retry an Incoming which already bears a token from a previous retry
RttEstimator
RTT estimation for a particular network path
SendStream
Access to streams
ServerConfig
Parameters governing incoming connections
ShouldTransmit
Indicates whether a frame needs to be transmitted
StdSystemTime
Default implementation of TimeSource
StreamId
Identifier for a stream within a particular connection
StreamInfo
Streams
Access to streams
TokenMemoryCache
TokenStore implementation that stores up to N tokens per server name for up to a +limited number of server names, in-memory
TokenReuseError
Error for when a validation token may have been reused
Transmit
An outgoing packet
TransportConfig
Parameters governing the core QUIC state machine
TransportError
Transport-level errors occur when a peer violates the protocol specification
TransportErrorCode
Transport-level error code
UdpStats
Statistics about UDP datagrams transmitted or received on a connection.
ValidationTokenConfig
Configuration for sending and handling validation tokens in incoming connections
VarInt
An integer less than 2^62
VarIntBoundsExceeded
Error returned when constructing a VarInt from a value >= 2^62

Enums§

ClosePathError
Errors triggered when abandoning a path
ConfigError
Errors in the configuration of an endpoint
ConnectError
Errors in the parameters being used to create a new connection
ConnectionError
Reasons why a connection might be lost
DatagramEvent
Event resulting from processing a single datagram
Dir
Whether a stream communicates data in both directions or only from the initiator
EcnCodepoint
Explicit congestion notification codepoint
Event
Events of interest to the application
FinishError
Reasons why attempting to finish a stream might fail
FrameType
LongType
Long packet types with uniform header structure
MaybeFrame
The type used to refer to FrameTypes in closing and transport errors.
PacketDecodeError
Packet decode error
PathAbandonReason
Reason for why a path was abandoned.
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_SUPPORTED_VERSIONS
The QUIC protocol version implemented.

Traits§

ConnectionIdGenerator
Generates connection IDs for incoming connections
ConnectionIdParser
Parse connection id in short header packet
NetworkChangeHint
Hints when the caller identifies a network change.
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/672/docs/noq_proto/n0_nat_traversal/enum.Error.html b/pr/672/docs/noq_proto/n0_nat_traversal/enum.Error.html new file mode 100644 index 000000000..2a505f88a --- /dev/null +++ b/pr/672/docs/noq_proto/n0_nat_traversal/enum.Error.html @@ -0,0 +1,38 @@ +Error in noq_proto::n0_nat_traversal - 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/n0_nat_traversal/enum.Event.html b/pr/672/docs/noq_proto/n0_nat_traversal/enum.Event.html new file mode 100644 index 000000000..0ec00c454 --- /dev/null +++ b/pr/672/docs/noq_proto/n0_nat_traversal/enum.Event.html @@ -0,0 +1,31 @@ +Event in noq_proto::n0_nat_traversal - 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/n0_nat_traversal/index.html b/pr/672/docs/noq_proto/n0_nat_traversal/index.html new file mode 100644 index 000000000..4e82805a8 --- /dev/null +++ b/pr/672/docs/noq_proto/n0_nat_traversal/index.html @@ -0,0 +1,2 @@ +noq_proto::n0_nat_traversal - Rust

Module n0_nat_traversal

Module n0_nat_traversal 

Source
Expand description

n0’s (https://n0.computer) NAT Traversal protocol implementation.

+

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/672/docs/noq_proto/n0_nat_traversal/sidebar-items.js b/pr/672/docs/noq_proto/n0_nat_traversal/sidebar-items.js new file mode 100644 index 000000000..e03b07ea1 --- /dev/null +++ b/pr/672/docs/noq_proto/n0_nat_traversal/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Error","Event"]}; \ No newline at end of file diff --git a/pr/672/docs/noq_proto/packet/enum.LongType.html b/pr/672/docs/noq_proto/packet/enum.LongType.html new file mode 100644 index 000000000..4cc962748 --- /dev/null +++ b/pr/672/docs/noq_proto/packet/enum.LongType.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/enum.LongType.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/packet/enum.PacketDecodeError.html b/pr/672/docs/noq_proto/packet/enum.PacketDecodeError.html new file mode 100644 index 000000000..a1853fef7 --- /dev/null +++ b/pr/672/docs/noq_proto/packet/enum.PacketDecodeError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/enum.PacketDecodeError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/packet/enum.ProtectedHeader.html b/pr/672/docs/noq_proto/packet/enum.ProtectedHeader.html new file mode 100644 index 000000000..20b1faa75 --- /dev/null +++ b/pr/672/docs/noq_proto/packet/enum.ProtectedHeader.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/enum.ProtectedHeader.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/packet/struct.FixedLengthConnectionIdParser.html b/pr/672/docs/noq_proto/packet/struct.FixedLengthConnectionIdParser.html new file mode 100644 index 000000000..ae49b212b --- /dev/null +++ b/pr/672/docs/noq_proto/packet/struct.FixedLengthConnectionIdParser.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.FixedLengthConnectionIdParser.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/packet/struct.PartialDecode.html b/pr/672/docs/noq_proto/packet/struct.PartialDecode.html new file mode 100644 index 000000000..c588f9314 --- /dev/null +++ b/pr/672/docs/noq_proto/packet/struct.PartialDecode.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.PartialDecode.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/packet/struct.ProtectedInitialHeader.html b/pr/672/docs/noq_proto/packet/struct.ProtectedInitialHeader.html new file mode 100644 index 000000000..f93b4575c --- /dev/null +++ b/pr/672/docs/noq_proto/packet/struct.ProtectedInitialHeader.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.ProtectedInitialHeader.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/packet/trait.ConnectionIdParser.html b/pr/672/docs/noq_proto/packet/trait.ConnectionIdParser.html new file mode 100644 index 000000000..dac6135b4 --- /dev/null +++ b/pr/672/docs/noq_proto/packet/trait.ConnectionIdParser.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/trait.ConnectionIdParser.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/shared/enum.EcnCodepoint.html b/pr/672/docs/noq_proto/shared/enum.EcnCodepoint.html new file mode 100644 index 000000000..dd57eb734 --- /dev/null +++ b/pr/672/docs/noq_proto/shared/enum.EcnCodepoint.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/enum.EcnCodepoint.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/shared/struct.ConnectionEvent.html b/pr/672/docs/noq_proto/shared/struct.ConnectionEvent.html new file mode 100644 index 000000000..bd2aa06f1 --- /dev/null +++ b/pr/672/docs/noq_proto/shared/struct.ConnectionEvent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.ConnectionEvent.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/shared/struct.ConnectionId.html b/pr/672/docs/noq_proto/shared/struct.ConnectionId.html new file mode 100644 index 000000000..07078baea --- /dev/null +++ b/pr/672/docs/noq_proto/shared/struct.ConnectionId.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.ConnectionId.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/shared/struct.EndpointEvent.html b/pr/672/docs/noq_proto/shared/struct.EndpointEvent.html new file mode 100644 index 000000000..b86119665 --- /dev/null +++ b/pr/672/docs/noq_proto/shared/struct.EndpointEvent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.EndpointEvent.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/sidebar-items.js b/pr/672/docs/noq_proto/sidebar-items.js new file mode 100644 index 000000000..5173b4a61 --- /dev/null +++ b/pr/672/docs/noq_proto/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"constant":["DEFAULT_SUPPORTED_VERSIONS"],"enum":["ClosePathError","ConfigError","ConnectError","ConnectionError","DatagramEvent","Dir","EcnCodepoint","Event","FinishError","FrameType","LongType","MaybeFrame","PacketDecodeError","PathAbandonReason","PathError","PathEvent","PathStatus","ProtectedHeader","ReadError","ReadableError","SendDatagramError","SetPathStatusError","Side","StreamEvent","WriteError"],"mod":["bench_exports","coding","congestion","crypto","n0_nat_traversal","transport_parameters"],"struct":["AcceptError","AckFrequencyConfig","ApplicationClose","BloomTokenLog","Chunk","Chunks","ClientConfig","ClosedPath","ClosedStream","Connection","ConnectionClose","ConnectionEvent","ConnectionHandle","ConnectionId","ConnectionStats","Datagram","DatagramInfo","Datagrams","DecryptedInitial","Endpoint","EndpointConfig","EndpointEvent","FixedLengthConnectionIdParser","FourTuple","FrameStats","HashedConnectionIdGenerator","IdleTimeout","Incoming","IncomingAlpns","InvalidCid","InvalidFrameId","MtuDiscoveryConfig","MultipathNotNegotiated","NoneTokenLog","NoneTokenStore","PartialDecode","PathId","PathStats","ProtectedInitialHeader","QlogConfig","QlogFileFactory","RandomConnectionIdGenerator","RecvStream","RetryError","RttEstimator","SendStream","ServerConfig","ShouldTransmit","StdSystemTime","StreamId","StreamInfo","Streams","TokenMemoryCache","TokenReuseError","Transmit","TransportConfig","TransportError","TransportErrorCode","UdpStats","ValidationTokenConfig","VarInt","VarIntBoundsExceeded"],"trait":["ConnectionIdGenerator","ConnectionIdParser","NetworkChangeHint","QlogFactory","TimeSource","TokenLog","TokenStore"]}; \ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.AcceptError.html b/pr/672/docs/noq_proto/struct.AcceptError.html new file mode 100644 index 000000000..c517411d1 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.AcceptError.html @@ -0,0 +1,29 @@ +AcceptError in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/struct.AckFrequencyConfig.html b/pr/672/docs/noq_proto/struct.AckFrequencyConfig.html new file mode 100644 index 000000000..b16a2f361 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.AckFrequencyConfig.html @@ -0,0 +1,58 @@ +AckFrequencyConfig in noq_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).

+

noq’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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.ApplicationClose.html b/pr/672/docs/noq_proto/struct.ApplicationClose.html new file mode 100644 index 000000000..2d38fcb75 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.ApplicationClose.html @@ -0,0 +1,37 @@ +ApplicationClose in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.BloomTokenLog.html b/pr/672/docs/noq_proto/struct.BloomTokenLog.html new file mode 100644 index 000000000..db432addb --- /dev/null +++ b/pr/672/docs/noq_proto/struct.BloomTokenLog.html @@ -0,0 +1,47 @@ +BloomTokenLog in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/struct.Chunk.html b/pr/672/docs/noq_proto/struct.Chunk.html new file mode 100644 index 000000000..7eda38bc5 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.Chunk.html @@ -0,0 +1,34 @@ +Chunk in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/struct.Chunks.html b/pr/672/docs/noq_proto/struct.Chunks.html new file mode 100644 index 000000000..ee465f92f --- /dev/null +++ b/pr/672/docs/noq_proto/struct.Chunks.html @@ -0,0 +1,40 @@ +Chunks in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/struct.ClientConfig.html b/pr/672/docs/noq_proto/struct.ClientConfig.html new file mode 100644 index 000000000..b23dbf153 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.ClientConfig.html @@ -0,0 +1,45 @@ +ClientConfig in noq_proto - Rust

ClientConfig

Struct ClientConfig 

Source
#[non_exhaustive]
pub struct ClientConfig { /* private fields */ }
Expand description

Configuration for outgoing connections

+

Default values should be suitable for most internet applications.

+

Implementations§

Source§

impl ClientConfig

Source

pub fn new(crypto: Arc<dyn ClientConfig>) -> Self

Create a default config with a particular cryptographic config

+
Source

pub fn initial_dst_cid_provider( + &mut self, + initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>, +) -> &mut Self

Configure how to populate the destination CID of the initial packet when attempting to +establish a new connection

+

By default, it’s populated with random bytes with reasonable length, so unless you have +a good reason, you do not need to change it.

+

When prefer to override the default, please note that the generated connection ID MUST be +at least 8 bytes long and unpredictable, as per section 7.2 of RFC 9000.

+
Source

pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self

Set a custom TransportConfig

+
Source

pub fn token_store(&mut self, store: Arc<dyn TokenStore>) -> &mut Self

Set a custom TokenStore

+

Defaults to TokenMemoryCache, which is suitable for most internet applications.

+
Source

pub fn version(&mut self, version: u32) -> &mut Self

Set the QUIC version to use

+
Source§

impl ClientConfig

Source

pub fn try_with_platform_verifier() -> Result<Self, Error>

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

+
Source

pub fn with_root_certificates( + roots: Arc<RootCertStore>, +) -> Result<Self, VerifierBuilderError>

Create a client configuration that trusts specified trust anchors

+

Trait Implementations§

Source§

impl Clone for ClientConfig

Source§

fn clone(&self) -> ClientConfig

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for ClientConfig

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.ClosedPath.html b/pr/672/docs/noq_proto/struct.ClosedPath.html new file mode 100644 index 000000000..09ddebec9 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.ClosedPath.html @@ -0,0 +1,32 @@ +ClosedPath in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.ClosedStream.html b/pr/672/docs/noq_proto/struct.ClosedStream.html new file mode 100644 index 000000000..a3e015f5a --- /dev/null +++ b/pr/672/docs/noq_proto/struct.ClosedStream.html @@ -0,0 +1,32 @@ +ClosedStream in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.Connection.html b/pr/672/docs/noq_proto/struct.Connection.html new file mode 100644 index 000000000..6b13c6c58 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.Connection.html @@ -0,0 +1,277 @@ +Connection in noq_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.

+

Returns (path_id, true) if the path already existed, or (path_id, false) +if was opened.

+

If network_path has no local IP set, then this will open a new path +if no path exists for this remote address, independent of the existing +path’s local IP. If a local IP is set, it will match against the full +four-tuple of existing paths. Not setting the local IP 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.

+
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::Abandoned +events with this path id. Once the path is opened and can carry application data it +will be reported using a PathEvent::Established event.

+
Source

pub fn close_path( + &mut self, + now: Instant, + path_id: PathId, + error_code: VarInt, +) -> Result<(), ClosePathError>

Closes a path and sends a PATH_ABANDON frame with the passed error code.

+

Returns ClosePathError::LastOpenPath if this is the last open 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, + now: Instant, + path_id: PathId, + timeout: Option<Duration>, +) -> Result<Option<Duration>, ClosedPath>

Sets the max_idle_timeout for a specific path.

+

The PathIdle timer is immediately re-armed accounting for already-elapsed +idle time. Setting None disables the timeout and stops the timer.

+

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 +event respectively. Note that locally-initiated closes via close() do not +emit a ConnectionLost event.

+

For an established connection this essentially means the handshake is completed, +but not necessarily yet confirmed.

+
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 is closed by the peer or due to an error. When the local application closes +the connection via close(), no ConnectionLost event is emitted; +instead, pending operations fail with ConnectionError::LocallyClosed.

+
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 handle_network_change( + &mut self, + hint: Option<&dyn NetworkChangeHint>, + now: Instant, +)

Handle a change in the local address, i.e. an active migration

+

In the general (non-multipath) case, paths will perform a RFC9000 migration and be pinged +for a liveness check. This is the behaviour of a path assumed to be recoverable, even if +this is not the case.

+

Clients in a connection in which multipath has been negotiated should migrate paths to new +PathIds. For paths that are known to be non-recoverable can be migrated to a new +PathId by closing the current path, and opening a new one to the same remote. Treating +paths as non recoverable when necessary accelerates connectivity re-establishment, or might +allow it altogether.

+

The optional hint allows callers to indicate when paths are non-recoverable and should be +migrated to new a PathId.

+
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.

+

For all probes that succeed, if any, a new path will be opened on the successful +4-tuple.

+

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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/struct.ConnectionClose.html b/pr/672/docs/noq_proto/struct.ConnectionClose.html new file mode 100644 index 000000000..628b5a5c8 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.ConnectionClose.html @@ -0,0 +1,39 @@ +ConnectionClose in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.ConnectionEvent.html b/pr/672/docs/noq_proto/struct.ConnectionEvent.html new file mode 100644 index 000000000..f64a215b7 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.ConnectionEvent.html @@ -0,0 +1,24 @@ +ConnectionEvent in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

The type 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/672/docs/noq_proto/struct.ConnectionHandle.html b/pr/672/docs/noq_proto/struct.ConnectionHandle.html new file mode 100644 index 000000000..b05f201e4 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.ConnectionHandle.html @@ -0,0 +1,41 @@ +ConnectionHandle in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.ConnectionId.html b/pr/672/docs/noq_proto/struct.ConnectionId.html new file mode 100644 index 000000000..3d4fe21e6 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.ConnectionId.html @@ -0,0 +1,2467 @@ +ConnectionId in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.ConnectionStats.html b/pr/672/docs/noq_proto/struct.ConnectionStats.html new file mode 100644 index 000000000..78f941775 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.ConnectionStats.html @@ -0,0 +1,41 @@ +ConnectionStats in noq_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, + pub lost_packets: u64, + pub lost_bytes: u64, +}
Expand description

Connection statistics.

+

The fields here are a sum of the respective fields in the PathStats for all the +paths that exist as well as all paths that previously existed.

+

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 the connection.

+
§udp_rx: UdpStats

Statistics about UDP datagrams received on the connection.

+
§frame_tx: FrameStats

Statistics about frames transmitted on the connection.

+
§frame_rx: FrameStats

Statistics about frames received on the connection.

+
§lost_packets: u64

The number of packets lost on the connection.

+
§lost_bytes: u64

The number of bytes lost on the connection.

+

Trait Implementations§

Source§

impl Add<PathStats> for ConnectionStats

Source§

type Output = ConnectionStats

The resulting type after applying the + operator.
Source§

fn add(self, rhs: PathStats) -> Self::Output

Performs the + operation. Read more
Source§

impl AddAssign<PathStats> for ConnectionStats

Source§

fn add_assign(&mut self, rhs: PathStats)

Performs the += operation. Read more
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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.Datagram.html b/pr/672/docs/noq_proto/struct.Datagram.html new file mode 100644 index 000000000..632739b3a --- /dev/null +++ b/pr/672/docs/noq_proto/struct.Datagram.html @@ -0,0 +1,30 @@ +Datagram in noq_proto - Rust

Datagram

Struct Datagram 

Source
pub struct Datagram {
+    pub data: Bytes,
+}
Expand description

An unreliable datagram

+

Fields§

§data: Bytes

Payload

+

Trait Implementations§

Source§

impl Clone for Datagram

Source§

fn clone(&self) -> Datagram

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for Datagram

Source§

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

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

impl Display for Datagram

Source§

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

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

impl Encodable for Datagram

Source§

fn encode<B: BufMut>(&self, out: &mut B)

Append the encoding of self to the provided buffer.

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.DatagramInfo.html b/pr/672/docs/noq_proto/struct.DatagramInfo.html new file mode 100644 index 000000000..d3970a5d0 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.DatagramInfo.html @@ -0,0 +1,31 @@ +DatagramInfo in noq_proto - Rust

DatagramInfo

Struct DatagramInfo 

Source
pub struct DatagramInfo(/* private fields */);

Trait Implementations§

Source§

impl Clone for DatagramInfo

Source§

fn clone(&self) -> DatagramInfo

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for DatagramInfo

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for DatagramInfo

Source§

fn fmt(&self, __derive_more_f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for DatagramInfo

Source§

fn eq(&self, other: &DatagramInfo) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for DatagramInfo

Source§

impl Eq for DatagramInfo

Source§

impl StructuralPartialEq for DatagramInfo

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.Datagrams.html b/pr/672/docs/noq_proto/struct.Datagrams.html new file mode 100644 index 000000000..858631338 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.Datagrams.html @@ -0,0 +1,43 @@ +Datagrams in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.DecryptedInitial.html b/pr/672/docs/noq_proto/struct.DecryptedInitial.html new file mode 100644 index 000000000..3a3600d2d --- /dev/null +++ b/pr/672/docs/noq_proto/struct.DecryptedInitial.html @@ -0,0 +1,30 @@ +DecryptedInitial in noq_proto - Rust

DecryptedInitial

Struct DecryptedInitial 

Source
pub struct DecryptedInitial(/* private fields */);
Expand description

Decrypted payload of a QUIC Initial packet

+

Obtained via Incoming::decrypt. Can be used to extract information from +the TLS ClientHello without completing the handshake.

+

Implementations§

Source§

impl DecryptedInitial

Source

pub fn alpns(&self) -> Option<IncomingAlpns>

Best-effort extraction of the ALPN protocols from the TLS ClientHello

+

Parses the CRYPTO frames to extract the ALPN extension. This is intended +for routing and filtering; it is not guaranteed to succeed if the +ClientHello spans multiple packets. Returns None if parsing fails.

+

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> 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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.Endpoint.html b/pr/672/docs/noq_proto/struct.Endpoint.html new file mode 100644 index 000000000..40e0b9026 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.Endpoint.html @@ -0,0 +1,77 @@ +Endpoint in noq_proto - Rust

Endpoint

Struct Endpoint 

Source
pub struct Endpoint { /* private fields */ }
Expand description

The main entry point to the library

+

This object performs no I/O whatsoever. Instead, it consumes incoming packets and +connection-generated events via handle and handle_event.

+

Implementations§

Source§

impl Endpoint

Source

pub fn new( + config: Arc<EndpointConfig>, + server_config: Option<Arc<ServerConfig>>, + allow_mtud: bool, +) -> Self

Create a new endpoint

+

allow_mtud enables path MTU detection when requested by Connection configuration for +better performance. This requires that outgoing packets are never fragmented, which can be +achieved via e.g. the IPV6_DONTFRAG socket option.

+
Source

pub fn set_server_config(&mut self, server_config: Option<Arc<ServerConfig>>)

Replace the server configuration, affecting new incoming connections only

+
Source

pub fn handle_event( + &mut self, + ch: ConnectionHandle, + event: EndpointEvent, +) -> Option<ConnectionEvent>

Process EndpointEvents emitted from related Connections

+

In turn, processing this event may return a ConnectionEvent for the same Connection.

+
Source

pub fn handle( + &mut self, + now: Instant, + network_path: FourTuple, + ecn: Option<EcnCodepoint>, + data: BytesMut, + buf: &mut Vec<u8>, +) -> Option<DatagramEvent>

Process an incoming UDP datagram

+
Source

pub fn connect( + &mut self, + now: Instant, + config: ClientConfig, + remote: SocketAddr, + server_name: &str, +) -> Result<(ConnectionHandle, Connection), ConnectError>

Initiate a connection

+
Source

pub fn accept( + &mut self, + incoming: Incoming, + now: Instant, + buf: &mut Vec<u8>, + server_config: Option<Arc<ServerConfig>>, +) -> Result<(ConnectionHandle, Connection), Box<AcceptError>>

Attempt to accept this incoming connection (an error may still occur)

+
Source

pub fn refuse(&mut self, incoming: Incoming, buf: &mut Vec<u8>) -> Transmit

Reject this incoming connection attempt

+
Source

pub fn retry( + &mut self, + incoming: Incoming, + buf: &mut Vec<u8>, +) -> Result<Transmit, RetryError>

Respond with a retry packet, requiring the client to retry with address validation

+

Errors if incoming.may_retry() is false.

+
Source

pub fn ignore(&mut self, incoming: Incoming)

Ignore this incoming connection attempt, not sending any packet in response

+

Doing this actively, rather than merely dropping the Incoming, is necessary to prevent +memory leaks due to state within Endpoint tracking the incoming connection.

+
Source

pub fn config(&self) -> &EndpointConfig

Access the configuration used by this endpoint

+
Source

pub fn open_connections(&self) -> usize

Number of connections that are currently open

+
Source

pub fn incoming_buffer_bytes(&self) -> u64

Counter for the number of bytes currently used +in the buffers for Initial and 0-RTT messages for pending incoming connections

+

Trait Implementations§

Source§

impl Debug for Endpoint

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.EndpointConfig.html b/pr/672/docs/noq_proto/struct.EndpointConfig.html new file mode 100644 index 000000000..48ef8058f --- /dev/null +++ b/pr/672/docs/noq_proto/struct.EndpointConfig.html @@ -0,0 +1,65 @@ +EndpointConfig in noq_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( + &mut self, + factory: Arc<dyn Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync>, +) -> &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, noq 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 noq +deterministically or if you are using noq 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.EndpointEvent.html b/pr/672/docs/noq_proto/struct.EndpointEvent.html new file mode 100644 index 000000000..5aa03db95 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.EndpointEvent.html @@ -0,0 +1,30 @@ +EndpointEvent in noq_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.

+
Source

pub fn is_draining(&self) -> bool

Whether this is the event is the event indicating the start of the draining period.

+

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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.FixedLengthConnectionIdParser.html b/pr/672/docs/noq_proto/struct.FixedLengthConnectionIdParser.html new file mode 100644 index 000000000..c22ffd7ec --- /dev/null +++ b/pr/672/docs/noq_proto/struct.FixedLengthConnectionIdParser.html @@ -0,0 +1,25 @@ +FixedLengthConnectionIdParser in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.FourTuple.html b/pr/672/docs/noq_proto/struct.FourTuple.html new file mode 100644 index 000000000..f07ece9be --- /dev/null +++ b/pr/672/docs/noq_proto/struct.FourTuple.html @@ -0,0 +1,43 @@ +FourTuple in noq_proto - Rust

FourTuple

Struct FourTuple 

Source
pub struct FourTuple { /* private fields */ }
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.

+

FourTuple implements From<SocketAddr>, which expands to Self::from_remote.

+

Implementations§

Source§

impl FourTuple

Source

pub fn new(remote: SocketAddr, local_ip: Option<IpAddr>) -> Self

Creates a new FourTuple.

+
Source

pub fn from_remote(remote: SocketAddr) -> Self

Creates a new FourTuple without a known local address.

+
Source

pub fn remote(&self) -> SocketAddr

Returns the remote address of the network path.

+
Source

pub fn local_ip(&self) -> Option<IpAddr>

Returns the local address of the network path.

+

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 From<SocketAddr> for FourTuple

Source§

fn from(value: SocketAddr) -> Self

Converts to this type from the input type.
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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.FrameStats.html b/pr/672/docs/noq_proto/struct.FrameStats.html new file mode 100644 index 000000000..2d3075205 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.FrameStats.html @@ -0,0 +1,69 @@ +FrameStats in noq_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 Add for FrameStats

Source§

type Output = FrameStats

The resulting type after applying the + operator.
Source§

fn add(self, __rhs: Self) -> Self::Output

Performs the + operation. Read more
Source§

impl AddAssign for FrameStats

Source§

fn add_assign(&mut self, __rhs: Self)

Performs the += operation. Read more
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 PartialEq for FrameStats

Source§

fn eq(&self, other: &FrameStats) -> 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 FrameStats

Source§

impl Eq for FrameStats

Source§

impl StructuralPartialEq 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
§

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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.HashedConnectionIdGenerator.html b/pr/672/docs/noq_proto/struct.HashedConnectionIdGenerator.html new file mode 100644 index 000000000..cabe55e59 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.HashedConnectionIdGenerator.html @@ -0,0 +1,32 @@ +HashedConnectionIdGenerator in noq_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 noq 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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.IdleTimeout.html b/pr/672/docs/noq_proto/struct.IdleTimeout.html new file mode 100644 index 000000000..ca46a047f --- /dev/null +++ b/pr/672/docs/noq_proto/struct.IdleTimeout.html @@ -0,0 +1,48 @@ +IdleTimeout in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.Incoming.html b/pr/672/docs/noq_proto/struct.Incoming.html new file mode 100644 index 000000000..b19db67fe --- /dev/null +++ b/pr/672/docs/noq_proto/struct.Incoming.html @@ -0,0 +1,38 @@ +Incoming in noq_proto - Rust

Incoming

Struct Incoming 

Source
pub struct Incoming { /* private fields */ }
Expand description

An incoming connection for which the server has not yet begun its part of the handshake.

+

Implementations§

Source§

impl Incoming

Source

pub fn local_ip(&self) -> Option<IpAddr>

The local IP address which was used when the peer established the connection

+
Source

pub fn remote_address(&self) -> SocketAddr

The peer’s UDP address

+
Source

pub fn remote_address_validated(&self) -> bool

Whether the socket address that is initiating this connection has been validated

+

This means that the sender of the initial packet has proved that they can receive traffic +sent to self.remote_address().

+

If self.remote_address_validated() is false, self.may_retry() is guaranteed to be true. +The inverse is not guaranteed.

+
Source

pub fn may_retry(&self) -> bool

Whether it is legal to respond with a retry packet

+

If self.remote_address_validated() is false, self.may_retry() is guaranteed to be true. +The inverse is not guaranteed.

+
Source

pub fn orig_dst_cid(&self) -> ConnectionId

The original destination connection ID sent by the client

+
Source

pub fn decrypt(&self) -> Option<DecryptedInitial>

Decrypt the Initial packet payload

+

This clones and decrypts the packet payload (~1200 bytes). +Can be used to extract information from the TLS ClientHello without completing the handshake.

+

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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.IncomingAlpns.html b/pr/672/docs/noq_proto/struct.IncomingAlpns.html new file mode 100644 index 000000000..ed7a8fa71 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.IncomingAlpns.html @@ -0,0 +1,639 @@ +IncomingAlpns in noq_proto - Rust

IncomingAlpns

Struct IncomingAlpns 

Source
pub struct IncomingAlpns { /* private fields */ }
Expand description

Iterator over ALPN protocol names from a TLS ClientHello

+

Yields protocol names as [Bytes] slices. On the common fast path (single +CRYPTO frame), the only allocation is the payload clone for decryption.

+

Trait Implementations§

Source§

impl Iterator for IncomingAlpns

Source§

type Item = Result<Bytes, UnexpectedEnd>

The type of the elements being iterated over.
Source§

fn next(&mut self) -> Option<Self::Item>

Advances the iterator and returns the next value. Read more
Source§

fn next_chunk<const N: usize>( + &mut self, +) -> Result<[Self::Item; N], IntoIter<Self::Item, N>>
where + Self: Sized,

🔬This is a nightly-only experimental API. (iter_next_chunk)
Advances the iterator and returns an array containing the next N values. Read more
1.0.0 · Source§

fn size_hint(&self) -> (usize, Option<usize>)

Returns the bounds on the remaining length of the iterator. Read more
1.0.0 · Source§

fn count(self) -> usize
where + Self: Sized,

Consumes the iterator, counting the number of iterations and returning it. Read more
1.0.0 · Source§

fn last(self) -> Option<Self::Item>
where + Self: Sized,

Consumes the iterator, returning the last element. Read more
Source§

fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>>

🔬This is a nightly-only experimental API. (iter_advance_by)
Advances the iterator by n elements. Read more
1.0.0 · Source§

fn nth(&mut self, n: usize) -> Option<Self::Item>

Returns the nth element of the iterator. Read more
1.28.0 · Source§

fn step_by(self, step: usize) -> StepBy<Self>
where + Self: Sized,

Creates an iterator starting at the same point, but stepping by +the given amount at each iteration. Read more
1.0.0 · Source§

fn chain<U>(self, other: U) -> Chain<Self, <U as IntoIterator>::IntoIter>
where + Self: Sized, + U: IntoIterator<Item = Self::Item>,

Takes two iterators and creates a new iterator over both in sequence. Read more
1.0.0 · Source§

fn zip<U>(self, other: U) -> Zip<Self, <U as IntoIterator>::IntoIter>
where + Self: Sized, + U: IntoIterator,

‘Zips up’ two iterators into a single iterator of pairs. Read more
Source§

fn intersperse(self, separator: Self::Item) -> Intersperse<Self>
where + Self: Sized, + Self::Item: Clone,

🔬This is a nightly-only experimental API. (iter_intersperse)
Creates a new iterator which places a copy of separator between adjacent +items of the original iterator. Read more
Source§

fn intersperse_with<G>(self, separator: G) -> IntersperseWith<Self, G>
where + Self: Sized, + G: FnMut() -> Self::Item,

🔬This is a nightly-only experimental API. (iter_intersperse)
Creates a new iterator which places an item generated by separator +between adjacent items of the original iterator. Read more
1.0.0 · Source§

fn map<B, F>(self, f: F) -> Map<Self, F>
where + Self: Sized, + F: FnMut(Self::Item) -> B,

Takes a closure and creates an iterator which calls that closure on each +element. Read more
1.21.0 · Source§

fn for_each<F>(self, f: F)
where + Self: Sized, + F: FnMut(Self::Item),

Calls a closure on each element of an iterator. Read more
1.0.0 · Source§

fn filter<P>(self, predicate: P) -> Filter<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Creates an iterator which uses a closure to determine if an element +should be yielded. Read more
1.0.0 · Source§

fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F>
where + Self: Sized, + F: FnMut(Self::Item) -> Option<B>,

Creates an iterator that both filters and maps. Read more
1.0.0 · Source§

fn enumerate(self) -> Enumerate<Self>
where + Self: Sized,

Creates an iterator which gives the current iteration count as well as +the next value. Read more
1.0.0 · Source§

fn peekable(self) -> Peekable<Self>
where + Self: Sized,

Creates an iterator which can use the peek and peek_mut methods +to look at the next element of the iterator without consuming it. See +their documentation for more information. Read more
1.0.0 · Source§

fn skip_while<P>(self, predicate: P) -> SkipWhile<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Creates an iterator that skips elements based on a predicate. Read more
1.0.0 · Source§

fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Creates an iterator that yields elements based on a predicate. Read more
1.57.0 · Source§

fn map_while<B, P>(self, predicate: P) -> MapWhile<Self, P>
where + Self: Sized, + P: FnMut(Self::Item) -> Option<B>,

Creates an iterator that both yields elements based on a predicate and maps. Read more
1.0.0 · Source§

fn skip(self, n: usize) -> Skip<Self>
where + Self: Sized,

Creates an iterator that skips the first n elements. Read more
1.0.0 · Source§

fn take(self, n: usize) -> Take<Self>
where + Self: Sized,

Creates an iterator that yields the first n elements, or fewer +if the underlying iterator ends sooner. Read more
1.0.0 · Source§

fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>
where + Self: Sized, + F: FnMut(&mut St, Self::Item) -> Option<B>,

An iterator adapter which, like fold, holds internal state, but +unlike fold, produces a new iterator. Read more
1.0.0 · Source§

fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>
where + Self: Sized, + U: IntoIterator, + F: FnMut(Self::Item) -> U,

Creates an iterator that works like map, but flattens nested structure. Read more
1.29.0 · Source§

fn flatten(self) -> Flatten<Self>
where + Self: Sized, + Self::Item: IntoIterator,

Creates an iterator that flattens nested structure. Read more
Source§

fn map_windows<F, R, const N: usize>(self, f: F) -> MapWindows<Self, F, N>
where + Self: Sized, + F: FnMut(&[Self::Item; N]) -> R,

🔬This is a nightly-only experimental API. (iter_map_windows)
Calls the given function f for each contiguous window of size N over +self and returns an iterator over the outputs of f. Like slice::windows(), +the windows during mapping overlap as well. Read more
1.0.0 · Source§

fn fuse(self) -> Fuse<Self>
where + Self: Sized,

Creates an iterator which ends after the first None. Read more
1.0.0 · Source§

fn inspect<F>(self, f: F) -> Inspect<Self, F>
where + Self: Sized, + F: FnMut(&Self::Item),

Does something with each element of an iterator, passing the value on. Read more
1.0.0 · Source§

fn by_ref(&mut self) -> &mut Self
where + Self: Sized,

Creates a “by reference” adapter for this instance of Iterator. Read more
1.0.0 · Source§

fn collect<B>(self) -> B
where + B: FromIterator<Self::Item>, + Self: Sized,

Transforms an iterator into a collection. Read more
Source§

fn try_collect<B>( + &mut self, +) -> <<Self::Item as Try>::Residual as Residual<B>>::TryType
where + Self: Sized, + Self::Item: Try, + <Self::Item as Try>::Residual: Residual<B>, + B: FromIterator<<Self::Item as Try>::Output>,

🔬This is a nightly-only experimental API. (iterator_try_collect)
Fallibly transforms an iterator into a collection, short circuiting if +a failure is encountered. Read more
Source§

fn collect_into<E>(self, collection: &mut E) -> &mut E
where + E: Extend<Self::Item>, + Self: Sized,

🔬This is a nightly-only experimental API. (iter_collect_into)
Collects all the items from an iterator into a collection. Read more
1.0.0 · Source§

fn partition<B, F>(self, f: F) -> (B, B)
where + Self: Sized, + B: Default + Extend<Self::Item>, + F: FnMut(&Self::Item) -> bool,

Consumes an iterator, creating two collections from it. Read more
Source§

fn is_partitioned<P>(self, predicate: P) -> bool
where + Self: Sized, + P: FnMut(Self::Item) -> bool,

🔬This is a nightly-only experimental API. (iter_is_partitioned)
Checks if the elements of this iterator are partitioned according to the given predicate, +such that all those that return true precede all those that return false. Read more
1.27.0 · Source§

fn try_fold<B, F, R>(&mut self, init: B, f: F) -> R
where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try<Output = B>,

An iterator method that applies a function as long as it returns +successfully, producing a single, final value. Read more
1.27.0 · Source§

fn try_for_each<F, R>(&mut self, f: F) -> R
where + Self: Sized, + F: FnMut(Self::Item) -> R, + R: Try<Output = ()>,

An iterator method that applies a fallible function to each item in the +iterator, stopping at the first error and returning that error. Read more
1.0.0 · Source§

fn fold<B, F>(self, init: B, f: F) -> B
where + Self: Sized, + F: FnMut(B, Self::Item) -> B,

Folds every element into an accumulator by applying an operation, +returning the final result. Read more
1.51.0 · Source§

fn reduce<F>(self, f: F) -> Option<Self::Item>
where + Self: Sized, + F: FnMut(Self::Item, Self::Item) -> Self::Item,

Reduces the elements to a single one, by repeatedly applying a reducing +operation. Read more
Source§

fn try_reduce<R>( + &mut self, + f: impl FnMut(Self::Item, Self::Item) -> R, +) -> <<R as Try>::Residual as Residual<Option<<R as Try>::Output>>>::TryType
where + Self: Sized, + R: Try<Output = Self::Item>, + <R as Try>::Residual: Residual<Option<Self::Item>>,

🔬This is a nightly-only experimental API. (iterator_try_reduce)
Reduces the elements to a single one by repeatedly applying a reducing operation. If the +closure returns a failure, the failure is propagated back to the caller immediately. Read more
1.0.0 · Source§

fn all<F>(&mut self, f: F) -> bool
where + Self: Sized, + F: FnMut(Self::Item) -> bool,

Tests if every element of the iterator matches a predicate. Read more
1.0.0 · Source§

fn any<F>(&mut self, f: F) -> bool
where + Self: Sized, + F: FnMut(Self::Item) -> bool,

Tests if any element of the iterator matches a predicate. Read more
1.0.0 · Source§

fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Searches for an element of an iterator that satisfies a predicate. Read more
1.30.0 · Source§

fn find_map<B, F>(&mut self, f: F) -> Option<B>
where + Self: Sized, + F: FnMut(Self::Item) -> Option<B>,

Applies function to the elements of iterator and returns +the first non-none result. Read more
Source§

fn try_find<R>( + &mut self, + f: impl FnMut(&Self::Item) -> R, +) -> <<R as Try>::Residual as Residual<Option<Self::Item>>>::TryType
where + Self: Sized, + R: Try<Output = bool>, + <R as Try>::Residual: Residual<Option<Self::Item>>,

🔬This is a nightly-only experimental API. (try_find)
Applies function to the elements of iterator and returns +the first true result or the first error. Read more
1.0.0 · Source§

fn position<P>(&mut self, predicate: P) -> Option<usize>
where + Self: Sized, + P: FnMut(Self::Item) -> bool,

Searches for an element in an iterator, returning its index. Read more
1.0.0 · Source§

fn max(self) -> Option<Self::Item>
where + Self: Sized, + Self::Item: Ord,

Returns the maximum element of an iterator. Read more
1.0.0 · Source§

fn min(self) -> Option<Self::Item>
where + Self: Sized, + Self::Item: Ord,

Returns the minimum element of an iterator. Read more
1.6.0 · Source§

fn max_by_key<B, F>(self, f: F) -> Option<Self::Item>
where + B: Ord, + Self: Sized, + F: FnMut(&Self::Item) -> B,

Returns the element that gives the maximum value from the +specified function. Read more
1.15.0 · Source§

fn max_by<F>(self, compare: F) -> Option<Self::Item>
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Returns the element that gives the maximum value with respect to the +specified comparison function. Read more
1.6.0 · Source§

fn min_by_key<B, F>(self, f: F) -> Option<Self::Item>
where + B: Ord, + Self: Sized, + F: FnMut(&Self::Item) -> B,

Returns the element that gives the minimum value from the +specified function. Read more
1.15.0 · Source§

fn min_by<F>(self, compare: F) -> Option<Self::Item>
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Returns the element that gives the minimum value with respect to the +specified comparison function. Read more
1.0.0 · Source§

fn unzip<A, B, FromA, FromB>(self) -> (FromA, FromB)
where + FromA: Default + Extend<A>, + FromB: Default + Extend<B>, + Self: Sized + Iterator<Item = (A, B)>,

Converts an iterator of pairs into a pair of containers. Read more
1.36.0 · Source§

fn copied<'a, T>(self) -> Copied<Self>
where + T: Copy + 'a, + Self: Sized + Iterator<Item = &'a T>,

Creates an iterator which copies all of its elements. Read more
1.0.0 · Source§

fn cloned<'a, T>(self) -> Cloned<Self>
where + T: Clone + 'a, + Self: Sized + Iterator<Item = &'a T>,

Creates an iterator which clones all of its elements. Read more
Source§

fn array_chunks<const N: usize>(self) -> ArrayChunks<Self, N>
where + Self: Sized,

🔬This is a nightly-only experimental API. (iter_array_chunks)
Returns an iterator over N elements of the iterator at a time. Read more
1.11.0 · Source§

fn sum<S>(self) -> S
where + Self: Sized, + S: Sum<Self::Item>,

Sums the elements of an iterator. Read more
1.11.0 · Source§

fn product<P>(self) -> P
where + Self: Sized, + P: Product<Self::Item>,

Iterates over the entire iterator, multiplying all the elements Read more
1.5.0 · Source§

fn cmp<I>(self, other: I) -> Ordering
where + I: IntoIterator<Item = Self::Item>, + Self::Item: Ord, + Self: Sized,

Lexicographically compares the elements of this Iterator with those +of another. Read more
Source§

fn cmp_by<I, F>(self, other: I, cmp: F) -> Ordering
where + Self: Sized, + I: IntoIterator, + F: FnMut(Self::Item, <I as IntoIterator>::Item) -> Ordering,

🔬This is a nightly-only experimental API. (iter_order_by)
Lexicographically compares the elements of this Iterator with those +of another with respect to the specified comparison function. Read more
1.5.0 · Source§

fn partial_cmp<I>(self, other: I) -> Option<Ordering>
where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Lexicographically compares the PartialOrd elements of +this Iterator with those of another. The comparison works like short-circuit +evaluation, returning a result without comparing the remaining elements. +As soon as an order can be determined, the evaluation stops and a result is returned. Read more
Source§

fn partial_cmp_by<I, F>(self, other: I, partial_cmp: F) -> Option<Ordering>
where + Self: Sized, + I: IntoIterator, + F: FnMut(Self::Item, <I as IntoIterator>::Item) -> Option<Ordering>,

🔬This is a nightly-only experimental API. (iter_order_by)
Lexicographically compares the elements of this Iterator with those +of another with respect to the specified comparison function. Read more
1.5.0 · Source§

fn eq<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialEq<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are equal to those of +another. Read more
Source§

fn eq_by<I, F>(self, other: I, eq: F) -> bool
where + Self: Sized, + I: IntoIterator, + F: FnMut(Self::Item, <I as IntoIterator>::Item) -> bool,

🔬This is a nightly-only experimental API. (iter_order_by)
Determines if the elements of this Iterator are equal to those of +another with respect to the specified equality function. Read more
1.5.0 · Source§

fn ne<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialEq<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are not equal to those of +another. Read more
1.5.0 · Source§

fn lt<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +less than those of another. Read more
1.5.0 · Source§

fn le<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +less or equal to those of another. Read more
1.5.0 · Source§

fn gt<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +greater than those of another. Read more
1.5.0 · Source§

fn ge<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +greater than or equal to those of another. Read more
1.82.0 · Source§

fn is_sorted(self) -> bool
where + Self: Sized, + Self::Item: PartialOrd,

Checks if the elements of this iterator are sorted. Read more
1.82.0 · Source§

fn is_sorted_by<F>(self, compare: F) -> bool
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> bool,

Checks if the elements of this iterator are sorted using the given comparator function. Read more
1.82.0 · Source§

fn is_sorted_by_key<F, K>(self, f: F) -> bool
where + Self: Sized, + F: FnMut(Self::Item) -> K, + K: PartialOrd,

Checks if the elements of this iterator are sorted using the given key extraction +function. 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<I> IntoIterator for I
where + I: Iterator,

Source§

type Item = <I as Iterator>::Item

The type of the elements being iterated over.
Source§

type IntoIter = I

Which kind of iterator are we turning this into?
Source§

fn into_iter(self) -> I

Creates an iterator from a value. Read more
§

impl<I> IteratorRandom for I
where + I: Iterator,

§

fn choose<R>(self, rng: &mut R) -> Option<Self::Item>
where + R: Rng + ?Sized,

Uniformly sample one element Read more
§

fn choose_stable<R>(self, rng: &mut R) -> Option<Self::Item>
where + R: Rng + ?Sized,

Uniformly sample one element (stable) Read more
§

fn sample_fill<R>(self, rng: &mut R, buf: &mut [Self::Item]) -> usize
where + R: Rng + ?Sized,

Uniformly sample amount distinct elements into a buffer Read more
§

fn sample<R>(self, rng: &mut R, amount: usize) -> Vec<Self::Item>
where + R: Rng + ?Sized,

Uniformly sample amount distinct elements into a Vec Read more
§

fn choose_multiple_fill<R>(self, rng: &mut R, buf: &mut [Self::Item]) -> usize
where + R: Rng + ?Sized,

👎Deprecated since 0.10.0: Renamed to sample_fill
Deprecated: use [Self::sample_fill] instead
§

fn choose_multiple<R>(self, rng: &mut R, amount: usize) -> Vec<Self::Item>
where + R: Rng + ?Sized,

👎Deprecated since 0.10.0: Renamed to sample
Deprecated: use [Self::sample] instead
§

impl<T> Itertools for T
where + T: Iterator + ?Sized,

§

fn interleave<J>( + self, + other: J, +) -> Interleave<Self, <J as IntoIterator>::IntoIter>
where + J: IntoIterator<Item = Self::Item>, + Self: Sized,

Alternate elements from two iterators until both have run out. Read more
§

fn interleave_shortest<J>( + self, + other: J, +) -> InterleaveShortest<Self, <J as IntoIterator>::IntoIter>
where + J: IntoIterator<Item = Self::Item>, + Self: Sized,

Alternate elements from two iterators until at least one of them has run +out. Read more
§

fn intersperse( + self, + element: Self::Item, +) -> IntersperseWith<Self, IntersperseElementSimple<Self::Item>>
where + Self: Sized, + Self::Item: Clone,

An iterator adaptor to insert a particular value +between each element of the adapted iterator. Read more
§

fn intersperse_with<F>(self, element: F) -> IntersperseWith<Self, F>
where + Self: Sized, + F: FnMut() -> Self::Item,

An iterator adaptor to insert a particular value created by a function +between each element of the adapted iterator. Read more
§

fn get<R>(self, index: R) -> <R as IteratorIndex<Self>>::Output
where + Self: Sized, + R: IteratorIndex<Self>,

Returns an iterator over a subsection of the iterator. Read more
§

fn zip_longest<J>( + self, + other: J, +) -> ZipLongest<Self, <J as IntoIterator>::IntoIter>
where + J: IntoIterator, + Self: Sized,

Create an iterator which iterates over both this and the specified +iterator simultaneously, yielding pairs of two optional elements. Read more
§

fn zip_eq<J>(self, other: J) -> ZipEq<Self, <J as IntoIterator>::IntoIter>
where + J: IntoIterator, + Self: Sized,

Create an iterator which iterates over both this and the specified +iterator simultaneously, yielding pairs of elements. Read more
§

fn batching<B, F>(self, f: F) -> Batching<Self, F>
where + F: FnMut(&mut Self) -> Option<B>, + Self: Sized,

A “meta iterator adaptor”. Its closure receives a reference to the +iterator and may pick off as many elements as it likes, to produce the +next iterator element. Read more
§

fn chunk_by<K, F>(self, key: F) -> ChunkBy<K, Self, F>
where + Self: Sized, + F: FnMut(&Self::Item) -> K, + K: PartialEq,

Return an iterable that can group iterator elements. +Consecutive elements that map to the same key (“runs”), are assigned +to the same group. Read more
§

fn group_by<K, F>(self, key: F) -> ChunkBy<K, Self, F>
where + Self: Sized, + F: FnMut(&Self::Item) -> K, + K: PartialEq,

👎Deprecated since 0.13.0: Use .chunk_by() instead
§

fn chunks(self, size: usize) -> IntoChunks<Self>
where + Self: Sized,

Return an iterable that can chunk the iterator. Read more
§

fn tuple_windows<T>(self) -> TupleWindows<Self, T>
where + Self: Sized + Iterator<Item = <T as TupleCollect>::Item>, + T: HomogeneousTuple, + <T as TupleCollect>::Item: Clone,

Return an iterator over all contiguous windows producing tuples of +a specific size (up to 12). Read more
§

fn circular_tuple_windows<T>(self) -> CircularTupleWindows<Self, T>
where + Self: Sized + Clone + Iterator<Item = <T as TupleCollect>::Item> + ExactSizeIterator, + T: TupleCollect + Clone, + <T as TupleCollect>::Item: Clone,

Return an iterator over all windows, wrapping back to the first +elements when the window would otherwise exceed the length of the +iterator, producing tuples of a specific size (up to 12). Read more
§

fn tuples<T>(self) -> Tuples<Self, T>
where + Self: Sized + Iterator<Item = <T as TupleCollect>::Item>, + T: HomogeneousTuple,

Return an iterator that groups the items in tuples of a specific size +(up to 12). Read more
§

fn tee(self) -> (Tee<Self>, Tee<Self>)
where + Self: Sized, + Self::Item: Clone,

Split into an iterator pair that both yield all elements from +the original iterator. Read more
§

fn map_into<R>(self) -> MapSpecialCase<Self, MapSpecialCaseFnInto<R>>
where + Self: Sized, + Self::Item: Into<R>,

Convert each item of the iterator using the Into trait. Read more
§

fn map_ok<F, T, U, E>(self, f: F) -> MapSpecialCase<Self, MapSpecialCaseFnOk<F>>
where + Self: Sized + Iterator<Item = Result<T, E>>, + F: FnMut(T) -> U,

Return an iterator adaptor that applies the provided closure +to every Result::Ok value. Result::Err values are +unchanged. Read more
§

fn filter_ok<F, T, E>(self, f: F) -> FilterOk<Self, F>
where + Self: Sized + Iterator<Item = Result<T, E>>, + F: FnMut(&T) -> bool,

Return an iterator adaptor that filters every Result::Ok +value with the provided closure. Result::Err values are +unchanged. Read more
§

fn filter_map_ok<F, T, U, E>(self, f: F) -> FilterMapOk<Self, F>
where + Self: Sized + Iterator<Item = Result<T, E>>, + F: FnMut(T) -> Option<U>,

Return an iterator adaptor that filters and transforms every +Result::Ok value with the provided closure. Result::Err +values are unchanged. Read more
§

fn flatten_ok<T, E>(self) -> FlattenOk<Self, T, E>
where + Self: Sized + Iterator<Item = Result<T, E>>, + T: IntoIterator,

Return an iterator adaptor that flattens every Result::Ok value into +a series of Result::Ok values. Result::Err values are unchanged. Read more
§

fn process_results<F, T, E, R>(self, processor: F) -> Result<R, E>
where + Self: Sized + Iterator<Item = Result<T, E>>, + F: FnOnce(ProcessResults<'_, Self, E>) -> R,

“Lift” a function of the values of the current iterator so as to process +an iterator of Result values instead. Read more
§

fn merge<J>( + self, + other: J, +) -> MergeBy<Self, <J as IntoIterator>::IntoIter, MergeLte>
where + Self: Sized, + Self::Item: PartialOrd, + J: IntoIterator<Item = Self::Item>,

Return an iterator adaptor that merges the two base iterators in +ascending order. If both base iterators are sorted (ascending), the +result is sorted. Read more
§

fn merge_by<J, F>( + self, + other: J, + is_first: F, +) -> MergeBy<Self, <J as IntoIterator>::IntoIter, F>
where + Self: Sized, + J: IntoIterator<Item = Self::Item>, + F: FnMut(&Self::Item, &Self::Item) -> bool,

Return an iterator adaptor that merges the two base iterators in order. +This is much like .merge() but allows for a custom ordering. Read more
§

fn merge_join_by<J, F, T>( + self, + other: J, + cmp_fn: F, +) -> MergeBy<Self, <J as IntoIterator>::IntoIter, MergeFuncLR<F, <F as FuncLR<Self::Item, <<J as IntoIterator>::IntoIter as Iterator>::Item>>::T>>
where + J: IntoIterator, + F: FnMut(&Self::Item, &<J as IntoIterator>::Item) -> T, + Self: Sized,

Create an iterator that merges items from both this and the specified +iterator in ascending order. Read more
§

fn kmerge(self) -> KMergeBy<<Self::Item as IntoIterator>::IntoIter, KMergeByLt>
where + Self: Sized, + Self::Item: IntoIterator, + <Self::Item as IntoIterator>::Item: PartialOrd,

Return an iterator adaptor that flattens an iterator of iterators by +merging them in ascending order. Read more
§

fn kmerge_by<F>( + self, + first: F, +) -> KMergeBy<<Self::Item as IntoIterator>::IntoIter, F>
where + Self: Sized, + Self::Item: IntoIterator, + F: FnMut(&<Self::Item as IntoIterator>::Item, &<Self::Item as IntoIterator>::Item) -> bool,

Return an iterator adaptor that flattens an iterator of iterators by +merging them according to the given closure. Read more
§

fn cartesian_product<J>( + self, + other: J, +) -> Product<Self, <J as IntoIterator>::IntoIter>
where + Self: Sized, + Self::Item: Clone, + J: IntoIterator, + <J as IntoIterator>::IntoIter: Clone,

Return an iterator adaptor that iterates over the cartesian product of +the element sets of two iterators self and J. Read more
§

fn multi_cartesian_product( + self, +) -> MultiProduct<<Self::Item as IntoIterator>::IntoIter>
where + Self: Sized, + Self::Item: IntoIterator, + <Self::Item as IntoIterator>::IntoIter: Clone, + <Self::Item as IntoIterator>::Item: Clone,

Return an iterator adaptor that iterates over the cartesian product of +all subiterators returned by meta-iterator self. Read more
§

fn coalesce<F>(self, f: F) -> CoalesceBy<Self, F, NoCount>
where + Self: Sized, + F: FnMut(Self::Item, Self::Item) -> Result<Self::Item, (Self::Item, Self::Item)>,

Return an iterator adaptor that uses the passed-in closure to +optionally merge together consecutive elements. Read more
§

fn dedup(self) -> CoalesceBy<Self, DedupPred2CoalescePred<DedupEq>, NoCount>
where + Self: Sized, + Self::Item: PartialEq,

Remove duplicates from sections of consecutive identical elements. +If the iterator is sorted, all elements will be unique. Read more
§

fn dedup_by<Cmp>( + self, + cmp: Cmp, +) -> CoalesceBy<Self, DedupPred2CoalescePred<Cmp>, NoCount>
where + Self: Sized, + Cmp: FnMut(&Self::Item, &Self::Item) -> bool,

Remove duplicates from sections of consecutive identical elements, +determining equality using a comparison function. +If the iterator is sorted, all elements will be unique. Read more
§

fn dedup_with_count( + self, +) -> CoalesceBy<Self, DedupPredWithCount2CoalescePred<DedupEq>, WithCount>
where + Self: Sized,

Remove duplicates from sections of consecutive identical elements, while keeping a count of +how many repeated elements were present. +If the iterator is sorted, all elements will be unique. Read more
§

fn dedup_by_with_count<Cmp>( + self, + cmp: Cmp, +) -> CoalesceBy<Self, DedupPredWithCount2CoalescePred<Cmp>, WithCount>
where + Self: Sized, + Cmp: FnMut(&Self::Item, &Self::Item) -> bool,

Remove duplicates from sections of consecutive identical elements, while keeping a count of +how many repeated elements were present. +This will determine equality using a comparison function. +If the iterator is sorted, all elements will be unique. Read more
§

fn duplicates(self) -> DuplicatesBy<Self, Self::Item, ById>
where + Self: Sized, + Self::Item: Eq + Hash,

Return an iterator adaptor that produces elements that appear more than once during the +iteration. Duplicates are detected using hash and equality. Read more
§

fn duplicates_by<V, F>(self, f: F) -> DuplicatesBy<Self, V, ByFn<F>>
where + Self: Sized, + V: Eq + Hash, + F: FnMut(&Self::Item) -> V,

Return an iterator adaptor that produces elements that appear more than once during the +iteration. Duplicates are detected using hash and equality. Read more
§

fn unique(self) -> Unique<Self>
where + Self: Sized, + Self::Item: Clone + Eq + Hash,

Return an iterator adaptor that filters out elements that have +already been produced once during the iteration. Duplicates +are detected using hash and equality. Read more
§

fn unique_by<V, F>(self, f: F) -> UniqueBy<Self, V, F>
where + Self: Sized, + V: Eq + Hash, + F: FnMut(&Self::Item) -> V,

Return an iterator adaptor that filters out elements that have +already been produced once during the iteration. Read more
§

fn peeking_take_while<F>(&mut self, accept: F) -> PeekingTakeWhile<'_, Self, F>
where + Self: Sized + PeekingNext, + F: FnMut(&Self::Item) -> bool,

Return an iterator adaptor that borrows from this iterator and +takes items while the closure accept returns true. Read more
§

fn take_while_ref<F>(&mut self, accept: F) -> TakeWhileRef<'_, Self, F>
where + Self: Clone, + F: FnMut(&Self::Item) -> bool,

Return an iterator adaptor that borrows from a Clone-able iterator +to only pick off elements while the predicate accept returns true. Read more
§

fn take_while_inclusive<F>(self, accept: F) -> TakeWhileInclusive<Self, F>
where + Self: Sized, + F: FnMut(&Self::Item) -> bool,

Returns an iterator adaptor that consumes elements while the given +predicate is true, including the element for which the predicate +first returned false. Read more
§

fn while_some<A>(self) -> WhileSome<Self>
where + Self: Sized + Iterator<Item = Option<A>>,

Return an iterator adaptor that filters Option<A> iterator elements +and produces A. Stops on the first None encountered. Read more
§

fn tuple_combinations<T>(self) -> TupleCombinations<Self, T>
where + Self: Sized + Clone, + Self::Item: Clone, + T: HasCombination<Self>,

Return an iterator adaptor that iterates over the combinations of the +elements from an iterator. Read more
§

fn combinations(self, k: usize) -> Combinations<Self>
where + Self: Sized, + Self::Item: Clone,

Return an iterator adaptor that iterates over the k-length combinations of +the elements from an iterator. Read more
§

fn combinations_with_replacement( + self, + k: usize, +) -> CombinationsWithReplacement<Self>
where + Self: Sized, + Self::Item: Clone,

Return an iterator that iterates over the k-length combinations of +the elements from an iterator, with replacement. Read more
§

fn permutations(self, k: usize) -> Permutations<Self>
where + Self: Sized, + Self::Item: Clone,

Return an iterator adaptor that iterates over all k-permutations of the +elements from an iterator. Read more
§

fn powerset(self) -> Powerset<Self>
where + Self: Sized, + Self::Item: Clone,

Return an iterator that iterates through the powerset of the elements from an +iterator. Read more
§

fn pad_using<F>(self, min: usize, f: F) -> PadUsing<Self, F>
where + Self: Sized, + F: FnMut(usize) -> Self::Item,

Return an iterator adaptor that pads the sequence to a minimum length of +min by filling missing elements using a closure f. Read more
§

fn with_position(self) -> WithPosition<Self>
where + Self: Sized,

Return an iterator adaptor that combines each element with a Position to +ease special-case handling of the first or last elements. Read more
§

fn positions<P>(self, predicate: P) -> Positions<Self, P>
where + Self: Sized, + P: FnMut(Self::Item) -> bool,

Return an iterator adaptor that yields the indices of all elements +satisfying a predicate, counted from the start of the iterator. Read more
§

fn update<F>(self, updater: F) -> Update<Self, F>
where + Self: Sized, + F: FnMut(&mut Self::Item),

Return an iterator adaptor that applies a mutating function +to each element before yielding it. Read more
§

fn next_tuple<T>(&mut self) -> Option<T>
where + Self: Sized + Iterator<Item = <T as TupleCollect>::Item>, + T: HomogeneousTuple,

Advances the iterator and returns the next items grouped in a tuple of +a specific size (up to 12). Read more
§

fn collect_tuple<T>(self) -> Option<T>
where + Self: Sized + Iterator<Item = <T as TupleCollect>::Item>, + T: HomogeneousTuple,

Collects all items from the iterator into a tuple of a specific size +(up to 12). Read more
§

fn find_position<P>(&mut self, pred: P) -> Option<(usize, Self::Item)>
where + P: FnMut(&Self::Item) -> bool,

Find the position and value of the first element satisfying a predicate. Read more
§

fn find_or_last<P>(self, predicate: P) -> Option<Self::Item>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Find the value of the first element satisfying a predicate or return the last element, if any. Read more
§

fn find_or_first<P>(self, predicate: P) -> Option<Self::Item>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Find the value of the first element satisfying a predicate or return the first element, if any. Read more
§

fn contains<Q>(&mut self, query: &Q) -> bool
where + Self: Sized, + Self::Item: Borrow<Q>, + Q: PartialEq,

Returns true if the given item is present in this iterator. Read more
§

fn all_equal(&mut self) -> bool
where + Self: Sized, + Self::Item: PartialEq,

Check whether all elements compare equal. Read more
§

fn all_equal_value( + &mut self, +) -> Result<Self::Item, Option<(Self::Item, Self::Item)>>
where + Self: Sized, + Self::Item: PartialEq,

If there are elements and they are all equal, return a single copy of that element. +If there are no elements, return an Error containing None. +If there are elements and they are not all equal, return a tuple containing the first +two non-equal elements found. Read more
§

fn all_unique(&mut self) -> bool
where + Self: Sized, + Self::Item: Eq + Hash,

Check whether all elements are unique (non equal). Read more
§

fn dropping(self, n: usize) -> Self
where + Self: Sized,

Consume the first n elements from the iterator eagerly, +and return the same iterator again. Read more
§

fn dropping_back(self, n: usize) -> Self
where + Self: Sized + DoubleEndedIterator,

Consume the last n elements from the iterator eagerly, +and return the same iterator again. Read more
§

fn concat(self) -> Self::Item
where + Self: Sized, + Self::Item: Extend<<Self::Item as IntoIterator>::Item> + IntoIterator + Default,

Combine all an iterator’s elements into one element by using Extend. Read more
§

fn collect_vec(self) -> Vec<Self::Item>
where + Self: Sized,

.collect_vec() is simply a type specialization of Iterator::collect, +for convenience.
§

fn try_collect<T, U, E>(self) -> Result<U, E>
where + Self: Sized + Iterator<Item = Result<T, E>>, + Result<U, E>: FromIterator<Result<T, E>>,

.try_collect() is more convenient way of writing +.collect::<Result<_, _>>() Read more
§

fn set_from<'a, A, J>(&mut self, from: J) -> usize
where + A: 'a, + Self: Iterator<Item = &'a mut A>, + J: IntoIterator<Item = A>,

Assign to each reference in self from the from iterator, +stopping at the shortest of the two iterators. Read more
§

fn join(&mut self, sep: &str) -> String
where + Self::Item: Display,

Combine all iterator elements into one String, separated by sep. Read more
§

fn format(self, sep: &str) -> Format<'_, Self>
where + Self: Sized,

Format all iterator elements, separated by sep. Read more
§

fn format_with<F>(self, sep: &str, format: F) -> FormatWith<'_, Self, F>
where + Self: Sized, + F: FnMut(Self::Item, &mut dyn FnMut(&dyn Display) -> Result<(), Error>) -> Result<(), Error>,

Format all iterator elements, separated by sep. Read more
§

fn fold_ok<A, E, B, F>(&mut self, start: B, f: F) -> Result<B, E>
where + Self: Iterator<Item = Result<A, E>>, + F: FnMut(B, A) -> B,

Fold Result values from an iterator. Read more
§

fn fold_options<A, B, F>(&mut self, start: B, f: F) -> Option<B>
where + Self: Iterator<Item = Option<A>>, + F: FnMut(B, A) -> B,

Fold Option values from an iterator. Read more
§

fn fold1<F>(self, f: F) -> Option<Self::Item>
where + F: FnMut(Self::Item, Self::Item) -> Self::Item, + Self: Sized,

👎Deprecated since 0.10.2: Use Iterator::reduce instead
Accumulator of the elements in the iterator. Read more
§

fn tree_reduce<F>(self, f: F) -> Option<Self::Item>
where + F: FnMut(Self::Item, Self::Item) -> Self::Item, + Self: Sized,

Accumulate the elements in the iterator in a tree-like manner. Read more
§

fn tree_fold1<F>(self, f: F) -> Option<Self::Item>
where + F: FnMut(Self::Item, Self::Item) -> Self::Item, + Self: Sized,

👎Deprecated since 0.13.0: Use .tree_reduce() instead
§

fn fold_while<B, F>(&mut self, init: B, f: F) -> FoldWhile<B>
where + Self: Sized, + F: FnMut(B, Self::Item) -> FoldWhile<B>,

An iterator method that applies a function, producing a single, final value. Read more
§

fn sum1<S>(self) -> Option<S>
where + Self: Sized, + S: Sum<Self::Item>,

Iterate over the entire iterator and add all the elements. Read more
§

fn product1<P>(self) -> Option<P>
where + Self: Sized, + P: Product<Self::Item>,

Iterate over the entire iterator and multiply all the elements. Read more
§

fn sorted_unstable(self) -> IntoIter<Self::Item>
where + Self: Sized, + Self::Item: Ord,

Sort all iterator elements into a new iterator in ascending order. Read more
§

fn sorted_unstable_by<F>(self, cmp: F) -> IntoIter<Self::Item>
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Sort all iterator elements into a new iterator in ascending order. Read more
§

fn sorted_unstable_by_key<K, F>(self, f: F) -> IntoIter<Self::Item>
where + Self: Sized, + K: Ord, + F: FnMut(&Self::Item) -> K,

Sort all iterator elements into a new iterator in ascending order. Read more
§

fn sorted(self) -> IntoIter<Self::Item>
where + Self: Sized, + Self::Item: Ord,

Sort all iterator elements into a new iterator in ascending order. Read more
§

fn sorted_by<F>(self, cmp: F) -> IntoIter<Self::Item>
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Sort all iterator elements into a new iterator in ascending order. Read more
§

fn sorted_by_key<K, F>(self, f: F) -> IntoIter<Self::Item>
where + Self: Sized, + K: Ord, + F: FnMut(&Self::Item) -> K,

Sort all iterator elements into a new iterator in ascending order. Read more
§

fn sorted_by_cached_key<K, F>(self, f: F) -> IntoIter<Self::Item>
where + Self: Sized, + K: Ord, + F: FnMut(&Self::Item) -> K,

Sort all iterator elements into a new iterator in ascending order. The key function is +called exactly once per key. Read more
§

fn k_smallest(self, k: usize) -> IntoIter<Self::Item>
where + Self: Sized, + Self::Item: Ord,

Sort the k smallest elements into a new iterator, in ascending order. Read more
§

fn k_smallest_by<F>(self, k: usize, cmp: F) -> IntoIter<Self::Item>
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Sort the k smallest elements into a new iterator using the provided comparison. Read more
§

fn k_smallest_by_key<F, K>(self, k: usize, key: F) -> IntoIter<Self::Item>
where + Self: Sized, + F: FnMut(&Self::Item) -> K, + K: Ord,

Return the elements producing the k smallest outputs of the provided function. Read more
§

fn k_largest(self, k: usize) -> IntoIter<Self::Item>
where + Self: Sized, + Self::Item: Ord,

Sort the k largest elements into a new iterator, in descending order. Read more
§

fn k_largest_by<F>(self, k: usize, cmp: F) -> IntoIter<Self::Item>
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Sort the k largest elements into a new iterator using the provided comparison. Read more
§

fn k_largest_by_key<F, K>(self, k: usize, key: F) -> IntoIter<Self::Item>
where + Self: Sized, + F: FnMut(&Self::Item) -> K, + K: Ord,

Return the elements producing the k largest outputs of the provided function. Read more
§

fn tail(self, n: usize) -> IntoIter<Self::Item>
where + Self: Sized,

Consumes the iterator and return an iterator of the last n elements. Read more
§

fn partition_map<A, B, F, L, R>(self, predicate: F) -> (A, B)
where + Self: Sized, + F: FnMut(Self::Item) -> Either<L, R>, + A: Default + Extend<L>, + B: Default + Extend<R>,

Collect all iterator elements into one of two +partitions. Unlike Iterator::partition, each partition may +have a distinct type. Read more
§

fn partition_result<A, B, T, E>(self) -> (A, B)
where + Self: Sized + Iterator<Item = Result<T, E>>, + A: Default + Extend<T>, + B: Default + Extend<E>,

Partition a sequence of Results into one list of all the Ok elements +and another list of all the Err elements. Read more
§

fn into_group_map<K, V>(self) -> HashMap<K, Vec<V>>
where + Self: Sized + Iterator<Item = (K, V)>, + K: Hash + Eq,

Return a HashMap of keys mapped to Vecs of values. Keys and values +are taken from (Key, Value) tuple pairs yielded by the input iterator. Read more
§

fn into_group_map_by<K, V, F>(self, f: F) -> HashMap<K, Vec<V>>
where + Self: Sized + Iterator<Item = V>, + K: Hash + Eq, + F: FnMut(&V) -> K,

Return an Iterator on a HashMap. Keys mapped to Vecs of values. The key is specified +in the closure. Read more
§

fn into_grouping_map<K, V>(self) -> GroupingMap<Self>
where + Self: Sized + Iterator<Item = (K, V)>, + K: Hash + Eq,

Constructs a GroupingMap to be used later with one of the efficient +group-and-fold operations it allows to perform. Read more
§

fn into_grouping_map_by<K, V, F>( + self, + key_mapper: F, +) -> GroupingMap<MapSpecialCase<Self, GroupingMapFn<F>>>
where + Self: Sized + Iterator<Item = V>, + K: Hash + Eq, + F: FnMut(&V) -> K,

Constructs a GroupingMap to be used later with one of the efficient +group-and-fold operations it allows to perform. Read more
§

fn min_set(self) -> Vec<Self::Item>
where + Self: Sized, + Self::Item: Ord,

Return all minimum elements of an iterator. Read more
§

fn min_set_by<F>(self, compare: F) -> Vec<Self::Item>
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Return all minimum elements of an iterator, as determined by +the specified function. Read more
§

fn min_set_by_key<K, F>(self, key: F) -> Vec<Self::Item>
where + Self: Sized, + K: Ord, + F: FnMut(&Self::Item) -> K,

Return all minimum elements of an iterator, as determined by +the specified function. Read more
§

fn max_set(self) -> Vec<Self::Item>
where + Self: Sized, + Self::Item: Ord,

Return all maximum elements of an iterator. Read more
§

fn max_set_by<F>(self, compare: F) -> Vec<Self::Item>
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Return all maximum elements of an iterator, as determined by +the specified function. Read more
§

fn max_set_by_key<K, F>(self, key: F) -> Vec<Self::Item>
where + Self: Sized, + K: Ord, + F: FnMut(&Self::Item) -> K,

Return all maximum elements of an iterator, as determined by +the specified function. Read more
§

fn minmax(self) -> MinMaxResult<Self::Item>
where + Self: Sized, + Self::Item: PartialOrd,

Return the minimum and maximum elements in the iterator. Read more
§

fn minmax_by_key<K, F>(self, key: F) -> MinMaxResult<Self::Item>
where + Self: Sized, + K: PartialOrd, + F: FnMut(&Self::Item) -> K,

Return the minimum and maximum element of an iterator, as determined by +the specified function. Read more
§

fn minmax_by<F>(self, compare: F) -> MinMaxResult<Self::Item>
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Return the minimum and maximum element of an iterator, as determined by +the specified comparison function. Read more
§

fn position_max(self) -> Option<usize>
where + Self: Sized, + Self::Item: Ord,

Return the position of the maximum element in the iterator. Read more
§

fn position_max_by_key<K, F>(self, key: F) -> Option<usize>
where + Self: Sized, + K: Ord, + F: FnMut(&Self::Item) -> K,

Return the position of the maximum element in the iterator, as +determined by the specified function. Read more
§

fn position_max_by<F>(self, compare: F) -> Option<usize>
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Return the position of the maximum element in the iterator, as +determined by the specified comparison function. Read more
§

fn position_min(self) -> Option<usize>
where + Self: Sized, + Self::Item: Ord,

Return the position of the minimum element in the iterator. Read more
§

fn position_min_by_key<K, F>(self, key: F) -> Option<usize>
where + Self: Sized, + K: Ord, + F: FnMut(&Self::Item) -> K,

Return the position of the minimum element in the iterator, as +determined by the specified function. Read more
§

fn position_min_by<F>(self, compare: F) -> Option<usize>
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Return the position of the minimum element in the iterator, as +determined by the specified comparison function. Read more
§

fn position_minmax(self) -> MinMaxResult<usize>
where + Self: Sized, + Self::Item: PartialOrd,

Return the positions of the minimum and maximum elements in +the iterator. Read more
§

fn position_minmax_by_key<K, F>(self, key: F) -> MinMaxResult<usize>
where + Self: Sized, + K: PartialOrd, + F: FnMut(&Self::Item) -> K,

Return the postions of the minimum and maximum elements of an +iterator, as determined by the specified function. Read more
§

fn position_minmax_by<F>(self, compare: F) -> MinMaxResult<usize>
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Return the postions of the minimum and maximum elements of an +iterator, as determined by the specified comparison function. Read more
§

fn exactly_one(self) -> Result<Self::Item, ExactlyOneError<Self>>
where + Self: Sized,

If the iterator yields exactly one element, that element will be returned, otherwise +an error will be returned containing an iterator that has the same output as the input +iterator. Read more
§

fn at_most_one(self) -> Result<Option<Self::Item>, ExactlyOneError<Self>>
where + Self: Sized,

If the iterator yields no elements, Ok(None) will be returned. If the iterator yields +exactly one element, that element will be returned, otherwise an error will be returned +containing an iterator that has the same output as the input iterator. Read more
§

fn multipeek(self) -> MultiPeek<Self>
where + Self: Sized,

An iterator adaptor that allows the user to peek at multiple .next() +values without advancing the base iterator. Read more
§

fn counts(self) -> HashMap<Self::Item, usize>
where + Self: Sized, + Self::Item: Eq + Hash,

Collect the items in this iterator and return a HashMap which +contains each item that appears in the iterator and the number +of times it appears. Read more
§

fn counts_by<K, F>(self, f: F) -> HashMap<K, usize>
where + Self: Sized, + K: Eq + Hash, + F: FnMut(Self::Item) -> K,

Collect the items in this iterator and return a HashMap which +contains each item that appears in the iterator and the number +of times it appears, +determining identity using a keying function. Read more
§

fn multiunzip<FromI>(self) -> FromI
where + Self: Sized + MultiUnzip<FromI>,

Converts an iterator of tuples into a tuple of containers. Read more
§

fn try_len(&self) -> Result<usize, (usize, Option<usize>)>

Returns the length of the iterator if one exists. +Otherwise return self.size_hint(). Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.InvalidCid.html b/pr/672/docs/noq_proto/struct.InvalidCid.html new file mode 100644 index 000000000..7e2d5aa1b --- /dev/null +++ b/pr/672/docs/noq_proto/struct.InvalidCid.html @@ -0,0 +1,26 @@ +InvalidCid in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.InvalidFrameId.html b/pr/672/docs/noq_proto/struct.InvalidFrameId.html new file mode 100644 index 000000000..a234efae8 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.InvalidFrameId.html @@ -0,0 +1,30 @@ +InvalidFrameId in noq_proto - Rust

InvalidFrameId

Struct InvalidFrameId 

Source
pub struct InvalidFrameId(/* private fields */);
Expand description

Encounter a frame ID that was not valid.

+

Trait Implementations§

Source§

impl Debug for InvalidFrameId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for InvalidFrameId

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for InvalidFrameId

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for InvalidFrameId

Source§

fn eq(&self, other: &InvalidFrameId) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for InvalidFrameId

Source§

impl StructuralPartialEq for InvalidFrameId

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.MtuDiscoveryConfig.html b/pr/672/docs/noq_proto/struct.MtuDiscoveryConfig.html new file mode 100644 index 000000000..f5e56801a --- /dev/null +++ b/pr/672/docs/noq_proto/struct.MtuDiscoveryConfig.html @@ -0,0 +1,84 @@ +MtuDiscoveryConfig in noq_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

+

noq 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), noq 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.MultipathNotNegotiated.html b/pr/672/docs/noq_proto/struct.MultipathNotNegotiated.html new file mode 100644 index 000000000..7ab891e0c --- /dev/null +++ b/pr/672/docs/noq_proto/struct.MultipathNotNegotiated.html @@ -0,0 +1,27 @@ +MultipathNotNegotiated in noq_proto - Rust

MultipathNotNegotiated

Struct MultipathNotNegotiated 

Source
pub struct MultipathNotNegotiated { /* private fields */ }
Expand description

Error when the multipath extension was not negotiated, but attempted to be used.

+

Trait Implementations§

Source§

impl Clone for MultipathNotNegotiated

Source§

fn clone(&self) -> MultipathNotNegotiated

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for MultipathNotNegotiated

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for MultipathNotNegotiated

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for MultipathNotNegotiated

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl Copy for MultipathNotNegotiated

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.NoneTokenLog.html b/pr/672/docs/noq_proto/struct.NoneTokenLog.html new file mode 100644 index 000000000..a10849572 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.NoneTokenLog.html @@ -0,0 +1,30 @@ +NoneTokenLog in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.NoneTokenStore.html b/pr/672/docs/noq_proto/struct.NoneTokenStore.html new file mode 100644 index 000000000..0b7459e4e --- /dev/null +++ b/pr/672/docs/noq_proto/struct.NoneTokenStore.html @@ -0,0 +1,24 @@ +NoneTokenStore in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.PartialDecode.html b/pr/672/docs/noq_proto/struct.PartialDecode.html new file mode 100644 index 000000000..7c94e1266 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.PartialDecode.html @@ -0,0 +1,41 @@ +PartialDecode in noq_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 noq) 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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.PathId.html b/pr/672/docs/noq_proto/struct.PathId.html new file mode 100644 index 000000000..cb6bea772 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.PathId.html @@ -0,0 +1,48 @@ +PathId in noq_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 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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.PathStats.html b/pr/672/docs/noq_proto/struct.PathStats.html new file mode 100644 index 000000000..d4d27a8d3 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.PathStats.html @@ -0,0 +1,62 @@ +PathStats in noq_proto - Rust

PathStats

Struct PathStats 

Source
#[non_exhaustive]
pub struct PathStats {
Show 14 fields + pub rtt: Duration, + pub udp_tx: UdpStats, + pub udp_rx: UdpStats, + pub frame_tx: FrameStats, + pub frame_rx: FrameStats, + pub cwnd: u64, + pub congestion_events: u64, + pub spurious_congestion_events: u64, + pub lost_packets: u64, + pub lost_bytes: u64, + pub sent_plpmtud_probes: u64, + pub lost_plpmtud_probes: u64, + pub black_holes_detected: u64, + pub current_mtu: u16, +
}
Expand description

Statistics related to a transmission path.

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§rtt: Duration

Current best estimate of this connection’s latency (round-trip-time).

+
§udp_tx: UdpStats

Statistics about datagrams and bytes sent on this path.

+
§udp_rx: UdpStats

Statistics about datagrams and bytes received on this path.

+
§frame_tx: FrameStats

Statistics about frames transmitted on this path.

+
§frame_rx: FrameStats

Statistics about frames received on this path.

+
§cwnd: u64

Current congestion window of the connection.

+
§congestion_events: u64

Congestion events on the connection.

+
§spurious_congestion_events: u64

Spurious congestion events on the connection.

+
§lost_packets: u64

The number of packets lost on this path.

+
§lost_bytes: u64

The number of bytes lost on this path.

+
§sent_plpmtud_probes: u64

The number of PLPMTUD probe packets sent on this path.

+

These are also counted by UdpStats::datagrams.

+
§lost_plpmtud_probes: u64

The number of PLPMTUD probe packets lost on this path.

+

These are not included in Self::lost_packets and Self::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 Add<PathStats> for ConnectionStats

Source§

type Output = ConnectionStats

The resulting type after applying the + operator.
Source§

fn add(self, rhs: PathStats) -> Self::Output

Performs the + operation. Read more
Source§

impl AddAssign<PathStats> for ConnectionStats

Source§

fn add_assign(&mut self, rhs: PathStats)

Performs the += operation. Read more
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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.ProtectedInitialHeader.html b/pr/672/docs/noq_proto/struct.ProtectedInitialHeader.html new file mode 100644 index 000000000..6f3019e13 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.ProtectedInitialHeader.html @@ -0,0 +1,37 @@ +ProtectedInitialHeader in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.QlogConfig.html b/pr/672/docs/noq_proto/struct.QlogConfig.html new file mode 100644 index 000000000..d4f085a03 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.QlogConfig.html @@ -0,0 +1,33 @@ +QlogConfig in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.QlogFileFactory.html b/pr/672/docs/noq_proto/struct.QlogFileFactory.html new file mode 100644 index 000000000..1a77e7ba8 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.QlogFileFactory.html @@ -0,0 +1,38 @@ +QlogFileFactory in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.RandomConnectionIdGenerator.html b/pr/672/docs/noq_proto/struct.RandomConnectionIdGenerator.html new file mode 100644 index 000000000..c6ead5e9f --- /dev/null +++ b/pr/672/docs/noq_proto/struct.RandomConnectionIdGenerator.html @@ -0,0 +1,32 @@ +RandomConnectionIdGenerator in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.RecvStream.html b/pr/672/docs/noq_proto/struct.RecvStream.html new file mode 100644 index 000000000..b8f4ea9e1 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.RecvStream.html @@ -0,0 +1,46 @@ +RecvStream in noq_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 bytes_read(&self) -> Result<u64, ClosedStream>

Returns the number of bytes read from this stream.

+

This is the offset of the next byte to be read, i.e. the length of the contiguous +prefix of the stream consumed by the application.

+
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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.RetryError.html b/pr/672/docs/noq_proto/struct.RetryError.html new file mode 100644 index 000000000..2ba409d79 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.RetryError.html @@ -0,0 +1,26 @@ +RetryError in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.RttEstimator.html b/pr/672/docs/noq_proto/struct.RttEstimator.html new file mode 100644 index 000000000..3447ce0be --- /dev/null +++ b/pr/672/docs/noq_proto/struct.RttEstimator.html @@ -0,0 +1,31 @@ +RttEstimator in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.SendStream.html b/pr/672/docs/noq_proto/struct.SendStream.html new file mode 100644 index 000000000..a3200d250 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.SendStream.html @@ -0,0 +1,52 @@ +SendStream in noq_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 &mut [Bytes], +) -> Result<usize, WriteError>

Send data on the given stream

+

Returns the number of bytes written and advances the provided Bytes +slice, removing all completely written chunks.

+

Note that this method might also write a partial chunk. In this case +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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.ServerConfig.html b/pr/672/docs/noq_proto/struct.ServerConfig.html new file mode 100644 index 000000000..bac00e55f --- /dev/null +++ b/pr/672/docs/noq_proto/struct.ServerConfig.html @@ -0,0 +1,101 @@ +ServerConfig in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.ShouldTransmit.html b/pr/672/docs/noq_proto/struct.ShouldTransmit.html new file mode 100644 index 000000000..49d7aba77 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.ShouldTransmit.html @@ -0,0 +1,34 @@ +ShouldTransmit in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.StdSystemTime.html b/pr/672/docs/noq_proto/struct.StdSystemTime.html new file mode 100644 index 000000000..7a5b73d39 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.StdSystemTime.html @@ -0,0 +1,25 @@ +StdSystemTime in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.StreamId.html b/pr/672/docs/noq_proto/struct.StreamId.html new file mode 100644 index 000000000..6a6be5f99 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.StreamId.html @@ -0,0 +1,51 @@ +StreamId in noq_proto - Rust

StreamId

Struct StreamId 

Source
pub struct StreamId(/* private fields */);
Expand description

Identifier for a stream within a particular connection

+

Implementations§

Source§

impl StreamId

Source

pub fn new(initiator: Side, dir: Dir, index: u64) -> Self

Create a new StreamId

+
Source

pub fn initiator(self) -> Side

Which side of a connection initiated the stream

+
Source

pub fn dir(self) -> Dir

Which directions data flows in

+
Source

pub fn index(self) -> u64

Distinguishes streams of the same initiator and directionality

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for StreamId

Available on crate feature arbitrary only.
Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self>

Generate an arbitrary value of Self from the given unstructured data. Read more
§

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self, Error>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for StreamId

Source§

fn clone(&self) -> StreamId

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for StreamId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Decodable for StreamId

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Display for StreamId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Encodable for StreamId

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Append the encoding of self to the provided buffer.
Source§

impl From<StreamId> for VarInt

Source§

fn from(x: StreamId) -> Self

Converts to this type from the input type.
Source§

impl From<StreamId> for u64

Source§

fn from(x: StreamId) -> Self

Converts to this type from the input type.
Source§

impl From<VarInt> for StreamId

Source§

fn from(v: VarInt) -> Self

Converts to this type from the input type.
Source§

impl Hash for StreamId

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for StreamId

Source§

fn cmp(&self, other: &StreamId) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for StreamId

Source§

fn eq(&self, other: &StreamId) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for StreamId

Source§

fn partial_cmp(&self, other: &StreamId) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for StreamId

Source§

impl Eq for StreamId

Source§

impl StructuralPartialEq for StreamId

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.StreamInfo.html b/pr/672/docs/noq_proto/struct.StreamInfo.html new file mode 100644 index 000000000..668963336 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.StreamInfo.html @@ -0,0 +1,31 @@ +StreamInfo in noq_proto - Rust

StreamInfo

Struct StreamInfo 

Source
pub struct StreamInfo(/* private fields */);

Trait Implementations§

Source§

impl Clone for StreamInfo

Source§

fn clone(&self) -> StreamInfo

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for StreamInfo

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for StreamInfo

Source§

fn fmt(&self, __derive_more_f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for StreamInfo

Source§

fn eq(&self, other: &StreamInfo) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for StreamInfo

Source§

impl Eq for StreamInfo

Source§

impl StructuralPartialEq for StreamInfo

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.Streams.html b/pr/672/docs/noq_proto/struct.Streams.html new file mode 100644 index 000000000..46e6a856d --- /dev/null +++ b/pr/672/docs/noq_proto/struct.Streams.html @@ -0,0 +1,34 @@ +Streams in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.TokenMemoryCache.html b/pr/672/docs/noq_proto/struct.TokenMemoryCache.html new file mode 100644 index 000000000..9e707a23c --- /dev/null +++ b/pr/672/docs/noq_proto/struct.TokenMemoryCache.html @@ -0,0 +1,27 @@ +TokenMemoryCache in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.TokenReuseError.html b/pr/672/docs/noq_proto/struct.TokenReuseError.html new file mode 100644 index 000000000..7f694595f --- /dev/null +++ b/pr/672/docs/noq_proto/struct.TokenReuseError.html @@ -0,0 +1,24 @@ +TokenReuseError in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.Transmit.html b/pr/672/docs/noq_proto/struct.Transmit.html new file mode 100644 index 000000000..ca0d54dfa --- /dev/null +++ b/pr/672/docs/noq_proto/struct.Transmit.html @@ -0,0 +1,36 @@ +Transmit in noq_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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/noq_proto/struct.TransportConfig.html b/pr/672/docs/noq_proto/struct.TransportConfig.html new file mode 100644 index 000000000..9b66154ec --- /dev/null +++ b/pr/672/docs/noq_proto/struct.TransportConfig.html @@ -0,0 +1,222 @@ +TransportConfig in noq_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 max_outgoing_bytes_per_second(&mut self, value: Option<u64>) -> &mut Self

Configures an outbound rate limit (in bytes per second) for each connection.

+

Defaults to None, which disables rate limiting.

+
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. noq-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 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 n0’s nat traversal protocol, 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, then that setting is set to 8.

+
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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.TransportError.html b/pr/672/docs/noq_proto/struct.TransportError.html new file mode 100644 index 000000000..857aecfbe --- /dev/null +++ b/pr/672/docs/noq_proto/struct.TransportError.html @@ -0,0 +1,44 @@ +TransportError in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.TransportErrorCode.html b/pr/672/docs/noq_proto/struct.TransportErrorCode.html new file mode 100644 index 000000000..d6b2a9bbc --- /dev/null +++ b/pr/672/docs/noq_proto/struct.TransportErrorCode.html @@ -0,0 +1,54 @@ +TransportErrorCode in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.UdpStats.html b/pr/672/docs/noq_proto/struct.UdpStats.html new file mode 100644 index 000000000..9b596b615 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.UdpStats.html @@ -0,0 +1,41 @@ +UdpStats in noq_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 number of UDP datagrams observed.

+
§bytes: u64

The total amount of bytes which have been transferred inside UDP datagrams.

+
§ios: u64

The number 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 Add for UdpStats

Source§

type Output = UdpStats

The resulting type after applying the + operator.
Source§

fn add(self, __rhs: Self) -> Self::Output

Performs the + operation. Read more
Source§

impl AddAssign for UdpStats

Source§

fn add_assign(&mut self, __rhs: Self)

Performs the += operation. Read more
Source§

impl Clone for UdpStats

Source§

fn clone(&self) -> UdpStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for UdpStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for UdpStats

Source§

fn default() -> UdpStats

Returns the “default value” for a type. Read more
Source§

impl PartialEq for UdpStats

Source§

fn eq(&self, other: &UdpStats) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for UdpStats

Source§

impl Eq for UdpStats

Source§

impl StructuralPartialEq for UdpStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.ValidationTokenConfig.html b/pr/672/docs/noq_proto/struct.ValidationTokenConfig.html new file mode 100644 index 000000000..82ff44e81 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.ValidationTokenConfig.html @@ -0,0 +1,73 @@ +ValidationTokenConfig in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/struct.VarInt.html b/pr/672/docs/noq_proto/struct.VarInt.html new file mode 100644 index 000000000..8204132a7 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.VarInt.html @@ -0,0 +1,61 @@ +VarInt in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/struct.VarIntBoundsExceeded.html b/pr/672/docs/noq_proto/struct.VarIntBoundsExceeded.html new file mode 100644 index 000000000..bbf796052 --- /dev/null +++ b/pr/672/docs/noq_proto/struct.VarIntBoundsExceeded.html @@ -0,0 +1,32 @@ +VarIntBoundsExceeded in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/token/struct.NoneTokenLog.html b/pr/672/docs/noq_proto/token/struct.NoneTokenLog.html new file mode 100644 index 000000000..e684fd3a7 --- /dev/null +++ b/pr/672/docs/noq_proto/token/struct.NoneTokenLog.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.NoneTokenLog.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/token/struct.NoneTokenStore.html b/pr/672/docs/noq_proto/token/struct.NoneTokenStore.html new file mode 100644 index 000000000..5a0f21893 --- /dev/null +++ b/pr/672/docs/noq_proto/token/struct.NoneTokenStore.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.NoneTokenStore.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/token/struct.TokenReuseError.html b/pr/672/docs/noq_proto/token/struct.TokenReuseError.html new file mode 100644 index 000000000..d97456b89 --- /dev/null +++ b/pr/672/docs/noq_proto/token/struct.TokenReuseError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.TokenReuseError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/token/trait.TokenLog.html b/pr/672/docs/noq_proto/token/trait.TokenLog.html new file mode 100644 index 000000000..f938fa057 --- /dev/null +++ b/pr/672/docs/noq_proto/token/trait.TokenLog.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/trait.TokenLog.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/token/trait.TokenStore.html b/pr/672/docs/noq_proto/token/trait.TokenStore.html new file mode 100644 index 000000000..6fc644c89 --- /dev/null +++ b/pr/672/docs/noq_proto/token/trait.TokenStore.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/trait.TokenStore.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/token_memory_cache/struct.TokenMemoryCache.html b/pr/672/docs/noq_proto/token_memory_cache/struct.TokenMemoryCache.html new file mode 100644 index 000000000..2bf2b5dd8 --- /dev/null +++ b/pr/672/docs/noq_proto/token_memory_cache/struct.TokenMemoryCache.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.TokenMemoryCache.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/trait.ConnectionIdGenerator.html b/pr/672/docs/noq_proto/trait.ConnectionIdGenerator.html new file mode 100644 index 000000000..4bda392db --- /dev/null +++ b/pr/672/docs/noq_proto/trait.ConnectionIdGenerator.html @@ -0,0 +1,21 @@ +ConnectionIdGenerator in noq_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/672/docs/noq_proto/trait.ConnectionIdParser.html b/pr/672/docs/noq_proto/trait.ConnectionIdParser.html new file mode 100644 index 000000000..e9d81941a --- /dev/null +++ b/pr/672/docs/noq_proto/trait.ConnectionIdParser.html @@ -0,0 +1,9 @@ +ConnectionIdParser in noq_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/672/docs/noq_proto/trait.NetworkChangeHint.html b/pr/672/docs/noq_proto/trait.NetworkChangeHint.html new file mode 100644 index 000000000..cfcb7f1ed --- /dev/null +++ b/pr/672/docs/noq_proto/trait.NetworkChangeHint.html @@ -0,0 +1,15 @@ +NetworkChangeHint in noq_proto - Rust

NetworkChangeHint

Trait NetworkChangeHint 

Source
pub trait NetworkChangeHint: Debug + 'static {
+    // Required method
+    fn is_path_recoverable(
+        &self,
+        path_id: PathId,
+        network_path: FourTuple,
+    ) -> bool;
+}
Expand description

Hints when the caller identifies a network change.

+

Required Methods§

Source

fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool

Inform the connection if a path may recover after a network change.

+

After network changes, paths may not be recoverable. In this case, waiting for the path to +become idle may take longer than what is desirable. If Self::is_path_recoverable +returns false, a multipath-enabled, client-side connection will establish a new path to +the same remote, closing the current one, instead of migrating the path.

+

Paths that are deemed recoverable will simply be sent a PING for a liveness check.

+

Implementors§

\ No newline at end of file diff --git a/pr/672/docs/noq_proto/trait.QlogFactory.html b/pr/672/docs/noq_proto/trait.QlogFactory.html new file mode 100644 index 000000000..9aada7d50 --- /dev/null +++ b/pr/672/docs/noq_proto/trait.QlogFactory.html @@ -0,0 +1,23 @@ +QlogFactory in noq_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/672/docs/noq_proto/trait.TimeSource.html b/pr/672/docs/noq_proto/trait.TimeSource.html new file mode 100644 index 000000000..bdf9b3c8e --- /dev/null +++ b/pr/672/docs/noq_proto/trait.TimeSource.html @@ -0,0 +1,7 @@ +TimeSource in noq_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/672/docs/noq_proto/trait.TokenLog.html b/pr/672/docs/noq_proto/trait.TokenLog.html new file mode 100644 index 000000000..223cbfa33 --- /dev/null +++ b/pr/672/docs/noq_proto/trait.TokenLog.html @@ -0,0 +1,45 @@ +TokenLog in noq_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/672/docs/noq_proto/trait.TokenStore.html b/pr/672/docs/noq_proto/trait.TokenStore.html new file mode 100644 index 000000000..adb21f7e5 --- /dev/null +++ b/pr/672/docs/noq_proto/trait.TokenStore.html @@ -0,0 +1,13 @@ +TokenStore in noq_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/672/docs/noq_proto/transport_error/struct.Code.html b/pr/672/docs/noq_proto/transport_error/struct.Code.html new file mode 100644 index 000000000..7df069c6f --- /dev/null +++ b/pr/672/docs/noq_proto/transport_error/struct.Code.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.TransportErrorCode.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/transport_error/struct.Error.html b/pr/672/docs/noq_proto/transport_error/struct.Error.html new file mode 100644 index 000000000..9f626518c --- /dev/null +++ b/pr/672/docs/noq_proto/transport_error/struct.Error.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.TransportError.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/transport_parameters/enum.Error.html b/pr/672/docs/noq_proto/transport_parameters/enum.Error.html new file mode 100644 index 000000000..7237a41b5 --- /dev/null +++ b/pr/672/docs/noq_proto/transport_parameters/enum.Error.html @@ -0,0 +1,37 @@ +Error in noq_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> Same for T

Source§

type Output = T

Should always be Self
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<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/672/docs/noq_proto/transport_parameters/index.html b/pr/672/docs/noq_proto/transport_parameters/index.html new file mode 100644 index 000000000..cc1b10a32 --- /dev/null +++ b/pr/672/docs/noq_proto/transport_parameters/index.html @@ -0,0 +1,7 @@ +noq_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/672/docs/noq_proto/transport_parameters/sidebar-items.js b/pr/672/docs/noq_proto/transport_parameters/sidebar-items.js new file mode 100644 index 000000000..be0904c68 --- /dev/null +++ b/pr/672/docs/noq_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/672/docs/noq_proto/transport_parameters/struct.TransportParameters.html b/pr/672/docs/noq_proto/transport_parameters/struct.TransportParameters.html new file mode 100644 index 000000000..35de07fb4 --- /dev/null +++ b/pr/672/docs/noq_proto/transport_parameters/struct.TransportParameters.html @@ -0,0 +1,37 @@ +TransportParameters in noq_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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/noq_proto/varint/struct.VarInt.html b/pr/672/docs/noq_proto/varint/struct.VarInt.html new file mode 100644 index 000000000..480d9e5ac --- /dev/null +++ b/pr/672/docs/noq_proto/varint/struct.VarInt.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.VarInt.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_proto/varint/struct.VarIntBoundsExceeded.html b/pr/672/docs/noq_proto/varint/struct.VarIntBoundsExceeded.html new file mode 100644 index 000000000..514f3961b --- /dev/null +++ b/pr/672/docs/noq_proto/varint/struct.VarIntBoundsExceeded.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_proto/struct.VarIntBoundsExceeded.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_udp/all.html b/pr/672/docs/noq_udp/all.html new file mode 100644 index 000000000..e2c3b0d59 --- /dev/null +++ b/pr/672/docs/noq_udp/all.html @@ -0,0 +1 @@ +List of all items in this crate

All

\ No newline at end of file diff --git a/pr/672/docs/noq_udp/constant.BATCH_SIZE.html b/pr/672/docs/noq_udp/constant.BATCH_SIZE.html new file mode 100644 index 000000000..e00fd2532 --- /dev/null +++ b/pr/672/docs/noq_udp/constant.BATCH_SIZE.html @@ -0,0 +1,2 @@ +BATCH_SIZE in noq_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/672/docs/noq_udp/enum.EcnCodepoint.html b/pr/672/docs/noq_udp/enum.EcnCodepoint.html new file mode 100644 index 000000000..a590db43f --- /dev/null +++ b/pr/672/docs/noq_udp/enum.EcnCodepoint.html @@ -0,0 +1,28 @@ +EcnCodepoint in noq_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/672/docs/noq_udp/imp/struct.UdpSocketState.html b/pr/672/docs/noq_udp/imp/struct.UdpSocketState.html new file mode 100644 index 000000000..ada8b24e1 --- /dev/null +++ b/pr/672/docs/noq_udp/imp/struct.UdpSocketState.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../noq_udp/struct.UdpSocketState.html...

+ + + \ No newline at end of file diff --git a/pr/672/docs/noq_udp/index.html b/pr/672/docs/noq_udp/index.html new file mode 100644 index 000000000..f6ff6cefa --- /dev/null +++ b/pr/672/docs/noq_udp/index.html @@ -0,0 +1,22 @@ +noq_udp - Rust

Crate noq_udp

Crate noq_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/672/docs/noq_udp/sidebar-items.js b/pr/672/docs/noq_udp/sidebar-items.js new file mode 100644 index 000000000..d1db6b942 --- /dev/null +++ b/pr/672/docs/noq_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/672/docs/noq_udp/struct.RecvMeta.html b/pr/672/docs/noq_udp/struct.RecvMeta.html new file mode 100644 index 000000000..fa27d5986 --- /dev/null +++ b/pr/672/docs/noq_udp/struct.RecvMeta.html @@ -0,0 +1,41 @@ +RecvMeta in noq_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 (except under Wine), 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/672/docs/noq_udp/struct.Transmit.html b/pr/672/docs/noq_udp/struct.Transmit.html new file mode 100644 index 000000000..730bcf09a --- /dev/null +++ b/pr/672/docs/noq_udp/struct.Transmit.html @@ -0,0 +1,31 @@ +Transmit in noq_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/672/docs/noq_udp/struct.UdpSockRef.html b/pr/672/docs/noq_udp/struct.UdpSockRef.html new file mode 100644 index 000000000..80c6f6e94 --- /dev/null +++ b/pr/672/docs/noq_udp/struct.UdpSockRef.html @@ -0,0 +1,19 @@ +UdpSockRef in noq_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/672/docs/noq_udp/struct.UdpSocketState.html b/pr/672/docs/noq_udp/struct.UdpSocketState.html new file mode 100644 index 000000000..5a45e8fb2 --- /dev/null +++ b/pr/672/docs/noq_udp/struct.UdpSocketState.html @@ -0,0 +1,62 @@ +UdpSocketState in noq_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/672/docs/perf/all.html b/pr/672/docs/perf/all.html new file mode 100644 index 000000000..98ab50b67 --- /dev/null +++ b/pr/672/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/672/docs/perf/client/fn.run.html b/pr/672/docs/perf/client/fn.run.html new file mode 100644 index 000000000..d78e6dd08 --- /dev/null +++ b/pr/672/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/672/docs/perf/client/index.html b/pr/672/docs/perf/client/index.html new file mode 100644 index 000000000..c731a56b9 --- /dev/null +++ b/pr/672/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/672/docs/perf/client/sidebar-items.js b/pr/672/docs/perf/client/sidebar-items.js new file mode 100644 index 000000000..3dceadfac --- /dev/null +++ b/pr/672/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/672/docs/perf/client/struct.Opt.html b/pr/672/docs/perf/client/struct.Opt.html new file mode 100644 index 000000000..fb98ce887 --- /dev/null +++ b/pr/672/docs/perf/client/struct.Opt.html @@ -0,0 +1,42 @@ +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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/perf/enum.CongestionAlgorithm.html b/pr/672/docs/perf/enum.CongestionAlgorithm.html new file mode 100644 index 000000000..5624f4695 --- /dev/null +++ b/pr/672/docs/perf/enum.CongestionAlgorithm.html @@ -0,0 +1,29 @@ +CongestionAlgorithm in perf - Rust

CongestionAlgorithm

Enum CongestionAlgorithm 

Source
pub enum CongestionAlgorithm {
+    Cubic,
+    Bbr3,
+    NewReno,
+}

Variants§

§

Cubic

§

Bbr3

§

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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/perf/fn.parse_byte_size.html b/pr/672/docs/perf/fn.parse_byte_size.html new file mode 100644 index 000000000..6d4afc897 --- /dev/null +++ b/pr/672/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/672/docs/perf/index.html b/pr/672/docs/perf/index.html new file mode 100644 index 000000000..754aa4966 --- /dev/null +++ b/pr/672/docs/perf/index.html @@ -0,0 +1 @@ +perf - Rust

Crate perf

\ No newline at end of file diff --git a/pr/672/docs/perf/noprotection/index.html b/pr/672/docs/perf/noprotection/index.html new file mode 100644 index 000000000..e8a673713 --- /dev/null +++ b/pr/672/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/672/docs/perf/noprotection/sidebar-items.js b/pr/672/docs/perf/noprotection/sidebar-items.js new file mode 100644 index 000000000..7ed1562e0 --- /dev/null +++ b/pr/672/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/672/docs/perf/noprotection/struct.NoProtectionClientConfig.html b/pr/672/docs/perf/noprotection/struct.NoProtectionClientConfig.html new file mode 100644 index 000000000..dac4cdfaf --- /dev/null +++ b/pr/672/docs/perf/noprotection/struct.NoProtectionClientConfig.html @@ -0,0 +1,30 @@ +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, + version: u32, + server_name: &str, + params: &TransportParameters, +) -> Result<Box<dyn Session>, ConnectError>

Start a client session with this configuration
Source§

impl Clone for NoProtectionClientConfig

Source§

fn clone(&self) -> NoProtectionClientConfig

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

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/perf/noprotection/struct.NoProtectionServerConfig.html b/pr/672/docs/perf/noprotection/struct.NoProtectionServerConfig.html new file mode 100644 index 000000000..f62e39c66 --- /dev/null +++ b/pr/672/docs/perf/noprotection/struct.NoProtectionServerConfig.html @@ -0,0 +1,38 @@ +NoProtectionServerConfig in perf::noprotection - Rust

NoProtectionServerConfig

Struct NoProtectionServerConfig 

Source
pub struct NoProtectionServerConfig { /* private fields */ }

Implementations§

Trait Implementations§

Source§

impl Clone for NoProtectionServerConfig

Source§

fn clone(&self) -> NoProtectionServerConfig

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 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, + 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> 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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/perf/server/fn.run.html b/pr/672/docs/perf/server/fn.run.html new file mode 100644 index 000000000..e1e250aae --- /dev/null +++ b/pr/672/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/672/docs/perf/server/index.html b/pr/672/docs/perf/server/index.html new file mode 100644 index 000000000..4aa8e181b --- /dev/null +++ b/pr/672/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/672/docs/perf/server/sidebar-items.js b/pr/672/docs/perf/server/sidebar-items.js new file mode 100644 index 000000000..3dceadfac --- /dev/null +++ b/pr/672/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/672/docs/perf/server/struct.Opt.html b/pr/672/docs/perf/server/struct.Opt.html new file mode 100644 index 000000000..73e441e3f --- /dev/null +++ b/pr/672/docs/perf/server/struct.Opt.html @@ -0,0 +1,41 @@ +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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/perf/sidebar-items.js b/pr/672/docs/perf/sidebar-items.js new file mode 100644 index 000000000..317d83630 --- /dev/null +++ b/pr/672/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/672/docs/perf/static.PERF_CIPHER_SUITES.html b/pr/672/docs/perf/static.PERF_CIPHER_SUITES.html new file mode 100644 index 000000000..859ee319e --- /dev/null +++ b/pr/672/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/672/docs/perf/stats/index.html b/pr/672/docs/perf/stats/index.html new file mode 100644 index 000000000..d51e70a3b --- /dev/null +++ b/pr/672/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/672/docs/perf/stats/sidebar-items.js b/pr/672/docs/perf/stats/sidebar-items.js new file mode 100644 index 000000000..5c7b90d68 --- /dev/null +++ b/pr/672/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/672/docs/perf/stats/struct.OpenStreamStats.html b/pr/672/docs/perf/stats/struct.OpenStreamStats.html new file mode 100644 index 000000000..d22b55612 --- /dev/null +++ b/pr/672/docs/perf/stats/struct.OpenStreamStats.html @@ -0,0 +1,34 @@ +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> Same for T

Source§

type Output = T

Should always be Self
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/672/docs/perf/stats/struct.Stats.html b/pr/672/docs/perf/stats/struct.Stats.html new file mode 100644 index 000000000..44ce4452d --- /dev/null +++ b/pr/672/docs/perf/stats/struct.Stats.html @@ -0,0 +1,23 @@ +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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/perf/stats/struct.StreamStats.html b/pr/672/docs/perf/stats/struct.StreamStats.html new file mode 100644 index 000000000..d344307d6 --- /dev/null +++ b/pr/672/docs/perf/stats/struct.StreamStats.html @@ -0,0 +1,23 @@ +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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/perf/struct.CommonOpt.html b/pr/672/docs/perf/struct.CommonOpt.html new file mode 100644 index 000000000..12f255781 --- /dev/null +++ b/pr/672/docs/perf/struct.CommonOpt.html @@ -0,0 +1,83 @@ +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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type 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/672/docs/search.index/000ba6d7fd92.js b/pr/672/docs/search.index/000ba6d7fd92.js new file mode 100644 index 000000000..3eb2b3070 --- /dev/null +++ b/pr/672/docs/search.index/000ba6d7fd92.js @@ -0,0 +1 @@ +rn_("AQECsCAM8QABsAAMaQABZXPyYwcAAAEAAQUA8x4TAAABAAEA8wQBcvTkDwAAAQDvAAEAE4ACoLAAABReYXPzgAJiYw==") \ No newline at end of file diff --git a/pr/672/docs/search.index/00ee768feb28.js b/pr/672/docs/search.index/00ee768feb28.js new file mode 100644 index 000000000..b0a390133 --- /dev/null +++ b/pr/672/docs/search.index/00ee768feb28.js @@ -0,0 +1 @@ +rn_("AYQCoCAAABEnoDAAABGNaXQBAQA7MAAAAQAAjQACAHMHAAB1CYwA8wACZWz1twQAAMACzQlVAgEAAQIAOzAAAAEAAO4AAgAMDAAAGA3tAMcAAACFsLATQwABsBATyQABsDANFgABFDACOzAAAAEAABoBCwDgAQYAowUAAM0F7QBCBwcAQxACAJwRDQDLEQIApBIBACsTAQB+EwEAFBQCAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/0198ee31ee3b.js b/pr/672/docs/search.index/0198ee31ee3b.js new file mode 100644 index 000000000..550568f30 --- /dev/null +++ b/pr/672/docs/search.index/0198ee31ee3b.js @@ -0,0 +1 @@ +rn_("AQsBoEAAABQ6ZfJoFAAAAQABBQD0AxIAAAEAAQABAAGDArAAEO8AAbBQE34AAWR3E4ACoKAAABPyZXYBAgD0ChEAAAEAkgEBAAEAAPTyDwAAAQD1AAcBI4ICsFATVQABZXMBAgD0JRMAAAEAkgABAAEDAPRbEwAAAQCYAAEA84UCYnUBAgD0dREAAAEAfAEBAPOAAmls8wADYnR1OjAAAAEAAAAAAA8AEAAAAHIH+wf8B/0H/gcbEOEQrxMxFH4UfxSBFI4UjxS1FLkUAQwA8xQUAAABAAEAAYACsAAJGQABoCAAAA0DZG4BBgD0IxMAAAEAkgABAPMDAWL0CBEAAAEAkgEBAJcGAACHsEAP8AABoHAAABKnsJATUwABOAkDOjAAAAEAAAAAACQAEAAAAMEFwgXDBcQFNAc1BzMM+Az5DPoM/gz/DAAN6A/pDzwQPRBBEEIQXxBgEGEQYhBjEGQQZRCoEK0Q2RDaEI8TsxPaE3QUgBShFLIUAQMA9DkTAAABAE8BAQDzgAJjbXcBAIDFsYAUwgABQIEECA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/023324239cca.js b/pr/672/docs/search.index/023324239cca.js new file mode 100644 index 000000000..f5b4e02ac --- /dev/null +++ b/pr/672/docs/search.index/023324239cca.js @@ -0,0 +1 @@ +rn_("AQABsIAUPAABcPI4FAAAAQBDBQOgIAAAEWCgQAAAErVkZXPz+A8AAFADAQABAgA7MAAAAQAAEwABACUQEwATgYSgUAAAERCgEAAADCagIAAADPAcQAAzgAOwUBOEAAFhZm4=") \ No newline at end of file diff --git a/pr/672/docs/search.index/02e07382c03b.js b/pr/672/docs/search.index/02e07382c03b.js new file mode 100644 index 000000000..7e17c4dce --- /dev/null +++ b/pr/672/docs/search.index/02e07382c03b.js @@ -0,0 +1 @@ +rn_("AQABsPAUsAABazswAAABAAAIAAMAGRQAAB4UBgBIFAAAEwACsQAUmQABZG7y2hIAAAEAE4ACsBAMUwABZW8BBQA7MAAAAQAADQABAJwRDQABAgDzBxAAAAEAAQABAgD0AxAAAAEAAQABAAEDADswAAABAAAIAAEAuRAIAAGAAqBAAAARELDAFFwAAWVzAQEAOzAAAAEAABMAAQAlEBMAI4ACsJATxgABaW03NgAAiqAQAAAMJqBQAAAUJ7BwFAcAAaAgAAAM8JxVETswAAABAAAGAAMAVgcAAOoQAwC6EgEA84ACbHQ=") \ No newline at end of file diff --git a/pr/672/docs/search.index/035dbd7fa572.js b/pr/672/docs/search.index/035dbd7fa572.js new file mode 100644 index 000000000..d27f0dee6 --- /dev/null +++ b/pr/672/docs/search.index/035dbd7fa572.js @@ -0,0 +1 @@ +rn_("AQ0A9eATAAABAAEAeAABAAEHAPPIEwAAagABACOAAqEQAAAUGW5yAQcA9LYRAAABAAEAAQAzAASgIAAACSiw4BQOAAExZWlyOjAAAAEAAAAAAAkAEAAAAGAEYQROBwoJbAxtDG4MbwxYEVkRAQIA9AoRAAABAJIBAQABAwD0dREAAAEAfAEBAPMAAmJ1ARsQAAABAwD0CBEAAAEAkgEBAPOEAmRzAQIA8wwJAAABAFkA84ACaXQBGADzxBQAAAEAAQAjgQKwwBPeAAFjbQENAbBAFGYAAWX1KhQAAAEAAQABAAEAE4ICsAARIAABa3MBBAD0rhAAAAEAAQABAAGBAqAgAAARG7BAE3MAAWVp84ICZXQBBAGwYBLFAAFy9CEMAAABAPcDAQDzgANhZW8BCQD28BIAAAEAAAGHADQAAQATgAKwYBDIAAFkbgEGADswAAABAAAFAAIA8xADAIwTAQABAwGgcAAAEy1n9FsHAAABAAgCAQABCQDzfhQAAAEAOgABgQKwgBSOAAGgYAAAFLVkcxcBAIiFsNAUtwABoEAAABRhsGAUMQBQApAKAQQA9MwTAAABAEoAAQBTgAOwkBSwAAFlaG8BAAGgQAAAEYJlAcYPAAAjgQKwUBEtAAFrdHfsAwCOoZAAABSlsLATPQABoKAAABLvsVAUrgAB/cEPOzAAAAEAACEACACBAAAAhwADAHIFAQA3BwcAMwkHAEIMBwBTEAEAmhIAAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/03a9c483da4d.js b/pr/672/docs/search.index/03a9c483da4d.js new file mode 100644 index 000000000..af7b0528f --- /dev/null +++ b/pr/672/docs/search.index/03a9c483da4d.js @@ -0,0 +1 @@ +rn_("AQgA9PwTAAABAIkABQATAAOwkBRjAAGwUBNBAWRnbHA7MAAAAQAAEgAJAGkQAgDtEQEALhMBAFETAQCIEwEA7RMBACUUAQA0FAEAdRQBAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/0425426ec9ab.js b/pr/672/docs/search.index/0425426ec9ab.js new file mode 100644 index 000000000..4823237a1 --- /dev/null +++ b/pr/672/docs/search.index/0425426ec9ab.js @@ -0,0 +1 @@ +rn_("AQIBsEAUfAABdDswAAABAAANAAYA/hIDAGsTAQDbEwEA8hMBAHEUAQCXFAEAE4ACsDATMgFJaXABBQDzsBMAAAEAAQATgAKgMAAAFAtleVMCA7BwFBcAAWJlcwFAEAAAEwACoCAAAAWkZm31ggAAAOMGAQDVAQEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/05aae8a6fc63.js b/pr/672/docs/search.index/05aae8a6fc63.js new file mode 100644 index 000000000..544449006 --- /dev/null +++ b/pr/672/docs/search.index/05aae8a6fc63.js @@ -0,0 +1 @@ +rn_("AQEBoEAAABRlZfKHFAAAAQABgAOgMAAAEqGw4BRAAAGwkBSJAAFhY22HAQCAhbAQES0AAbAQEUwAAaCgAAAUczEJAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/05b8ba3e24ea.js b/pr/672/docs/search.index/05b8ba3e24ea.js new file mode 100644 index 000000000..c507c3ca9 --- /dev/null +++ b/pr/672/docs/search.index/05b8ba3e24ea.js @@ -0,0 +1 @@ +rn_("AQQA884RAAABAAEAAYICsEASxwABsEASyQABNDYBCwDzyBMAAGoAAQABBgD0sxQAAAEACgABAPMCAXIBERQAAPMCAWU7MAAAAQAABQACAH8QAwAwEQEAE4ACoFAAABL9ZHTzAIQRAQH37xAAAAEAbgAdAAEAAQA8AQ==") \ No newline at end of file diff --git a/pr/672/docs/search.index/05ba80c533eb.js b/pr/672/docs/search.index/05ba80c533eb.js new file mode 100644 index 000000000..f95eb14c0 --- /dev/null +++ b/pr/672/docs/search.index/05ba80c533eb.js @@ -0,0 +1 @@ +rn_("AQMBsDATXgABY/OGEQAAAQABAAEDAYATgAAQ+HM7MAAAAQAABQABAGATBQAjggKgkAAAFGJpcwcBAIDFsFATJwABoKAAABPLsHATkAABoNAAABRugAEYCPOAAmRy") \ No newline at end of file diff --git a/pr/672/docs/search.index/0613632b906a.js b/pr/672/docs/search.index/0613632b906a.js new file mode 100644 index 000000000..669029641 --- /dev/null +++ b/pr/672/docs/search.index/0613632b906a.js @@ -0,0 +1 @@ +rn_("AQDKoRAAABR6gAj/ABrdoEAAABEVsGAUagABoBAAAAyDsFAUAwABsDARdwABoAAAAAxBsSAUjAABsEAREwIjQE6wCTowAAABAAAAAAAUABAAAAAVCRYJKQkqCQgMCQwKDAsMEQwWDN0MoRLNEs4SzxKKE4sToBOhExIUExQ=") \ No newline at end of file diff --git a/pr/672/docs/search.index/06c693dd9ca3.js b/pr/672/docs/search.index/06c693dd9ca3.js new file mode 100644 index 000000000..4256a2d94 --- /dev/null +++ b/pr/672/docs/search.index/06c693dd9ca3.js @@ -0,0 +1 @@ +rn_("AYECoCAAABSWsEAP/AABYnABBQD0ShMAAAEAmAABAPMDAWMBsBIAAAEMAbAAFIIAAXM7MAAAAQAABQABAJsUBQDzgAJudgGAArAwDFcAAaAwAAAMWWlvAQMBoAAAABRicwH4EQAAAQQA9BIMAAABAAEAAQDXAwCAhrAABXQAAZFIAQ==") \ No newline at end of file diff --git a/pr/672/docs/search.index/06f31413a772.js b/pr/672/docs/search.index/06f31413a772.js new file mode 100644 index 000000000..b7794ba79 --- /dev/null +++ b/pr/672/docs/search.index/06f31413a772.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAYAAQBkEQYAAQAA9x8JAAABAF0HAQBzAAEAFQEBggKgIAAAESegMAAAEY1pdPOAA2FncwEBAaBAAAAUOmXyaBQAAAEAI4ACsFAT7wABZnPzgAJpbw==") \ No newline at end of file diff --git a/pr/672/docs/search.index/06fa28d2800a.js b/pr/672/docs/search.index/06fa28d2800a.js new file mode 100644 index 000000000..e560a0bd1 --- /dev/null +++ b/pr/672/docs/search.index/06fa28d2800a.js @@ -0,0 +1 @@ +rn_("AYQCsAARfgABsAARgAABNDbzAAFh8nUMAAABACOAArCQEYQBImx0AYACsHATmQABsIAT0gABY3MBAAA6MAAAAQAAAAAACQAQAAAA3hHfEfoS+xLrE+wTQBRBFLoUuxTzgAJkZScBAICFoNAAABQRsBAEwQABoDAAABB4AggL") \ No newline at end of file diff --git a/pr/672/docs/search.index/072d10679fb0.js b/pr/672/docs/search.index/072d10679fb0.js new file mode 100644 index 000000000..af963d5de --- /dev/null +++ b/pr/672/docs/search.index/072d10679fb0.js @@ -0,0 +1 @@ +rn_("AQEAOjAAAAEAAAAAAAkAEAAAAN8P4A/WEJIR8BLxEvETeBSsFK0UAYACsOAT6wABsDAUAwABZHDDgISxQBS+AAGgkAAAE68CgAk=") \ No newline at end of file diff --git a/pr/672/docs/search.index/0867d8c9f05d.js b/pr/672/docs/search.index/0867d8c9f05d.js new file mode 100644 index 000000000..99d831a68 --- /dev/null +++ b/pr/672/docs/search.index/0867d8c9f05d.js @@ -0,0 +1 @@ +rn_("AYYCoAAAABNXoAAAABNYZHIBgAKgcAAAE+qgMAAAERhmdAELAaBAAAAUOmXyaBQAAAEAI4ECsFAQ7wABY3YBAAGwgBQ8AAFw8jgUAAABAEMGA6AgAAARYKBAAAAStWRlc/P4DwAAUAMBAPMABGlyc3Y7MAAAAQAADwAFAPQGBQDcEgUACRMBAIMTAABzFAAA") \ No newline at end of file diff --git a/pr/672/docs/search.index/09a203b4464b.js b/pr/672/docs/search.index/09a203b4464b.js new file mode 100644 index 000000000..e6d3b7de2 --- /dev/null +++ b/pr/672/docs/search.index/09a203b4464b.js @@ -0,0 +1 @@ +rn_("AQAA9K4QAAABAAEAAQDzAgFz9UYJAAABAAEAAQCKBgEGAPQDEgAAAQABAAEAAQEAOzAAAAEAAAYAAQDAFAYA8wYBZfRvFAAAAQAyAAEAEwYDsEAUmQABsGAThwCJYXJ183sQAAB5AgEA84ACYWUBAwD0thEAAAEAAQABAAECAPTIDwAAAQBRBAEAAYICoBAAABNdsEAS+gABYXMBgAKgAAAADBewcBCMAAFyc+cHAACHoHAAABICkQVBOjAAAAEAAAAAAAwAEAAAAOcB6AHpAeoBeAfhB+IHTwlVDAcQCBAJEBwR") \ No newline at end of file diff --git a/pr/672/docs/search.index/0a2e52a6ccac.js b/pr/672/docs/search.index/0a2e52a6ccac.js new file mode 100644 index 000000000..4d4a55e1d --- /dev/null +++ b/pr/672/docs/search.index/0a2e52a6ccac.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwDLEQIAfhMBABQUAgABBADzzhEAAAEAAQAjAAKgYAAAEv1hcvPvEAAAAQDJAQGAAqEQAAAUobCAFJcAAWl1NwQAgYexUBS+AAGgAAAAEM+wcBPeAAGhMAAAFLIKQBsBAAA7MAAAAQAAjAABAAgOjAABAgD2ohMAAAEAqgBXABgAAQAjgQKgIAAAEQ9ydvOAAmVvI4ACoCAAABKaZHQBAAA7MAAAAQAA8AAEAMYFAAABCO0AgwwAAHERAADzgANkbnY=") \ No newline at end of file diff --git a/pr/672/docs/search.index/0a99e2bd71de.js b/pr/672/docs/search.index/0a99e2bd71de.js new file mode 100644 index 000000000..5cdee67e1 --- /dev/null +++ b/pr/672/docs/search.index/0a99e2bd71de.js @@ -0,0 +1 @@ +rn_("AQMBoEAAABQ6ZfJoFAAAAQABAAD0DhMAAAEAAQABAPMEAWT0sRIAAAEAAQABAKcAAAKFsKAUVgABsEATDAABsHAT/wABFYgAASgUAAABAAGwgBQ8AAFw8jgUAAABAEMAA6AgAAARYKBAAAAStWRlc/P4DwAAUAMBAPOAAmVp") \ No newline at end of file diff --git a/pr/672/docs/search.index/0b73e38f9a07.js b/pr/672/docs/search.index/0b73e38f9a07.js new file mode 100644 index 000000000..3da1f8a91 --- /dev/null +++ b/pr/672/docs/search.index/0b73e38f9a07.js @@ -0,0 +1 @@ +rn_("AYACsHARdwABsCAUXAABYWQBBQD0ShMAAAEAmAABAPMAAWMBsBIAAEOAA7AwE6AAAbCgFD4AAWFzeQEGAPQWEQAAAQC+AAEAk4CEoAAAABFesSAUogABCSAB84ACZWk=") \ No newline at end of file diff --git a/pr/672/docs/search.index/0d39a6ae8014.js b/pr/672/docs/search.index/0d39a6ae8014.js new file mode 100644 index 000000000..b4c03898c --- /dev/null +++ b/pr/672/docs/search.index/0d39a6ae8014.js @@ -0,0 +1 @@ +rn_("AQkA834UAAABADoAAYACoEAAABNdsCAUGgABaGkBAQDz+Q8AAAEAAQATgAKwwBS3AAF0dQGBArCAFI4AAaBgAAAUtWRzNwMAgIagQAAAFGGwYBQxAFACkA4BAAGwUBENAAFj9F0HAAABAAEACALDgISgEAAADHegUAAAEqIMQAQ=") \ No newline at end of file diff --git a/pr/672/docs/search.index/0d3f98434995.js b/pr/672/docs/search.index/0d3f98434995.js new file mode 100644 index 000000000..96f32ff6d --- /dev/null +++ b/pr/672/docs/search.index/0d3f98434995.js @@ -0,0 +1 @@ +rn_("AQ8A9CUUAAABAE8AAQABBAD0gxAAAAEAtwABAAECADowAAABAAAAAAAJABAAAADeEd8R+hL7EusT7BNAFEEUuhS7FPMAAW/ycwkAAAEAU4ADsQAUiQABaG9ww4CEoBAAABNdsIAToAABBQAG") \ No newline at end of file diff --git a/pr/672/docs/search.index/0d9568b14335.js b/pr/672/docs/search.index/0d9568b14335.js new file mode 100644 index 000000000..3542f55f7 --- /dev/null +++ b/pr/672/docs/search.index/0d9568b14335.js @@ -0,0 +1 @@ +rn_("AQIA9asRAAABAAEAAQABAAGAArBgExYAAaAgAAARAG95AQABoMAAABQ7afRvEwAACAAcABsAAQMA9JQTAAABAAEAAQABBgD0phQAAAEAAQABAAECAPT+EAAAAQCaAAEAAQIBoEAAABNGcjswAAABAAAHAAIA/BABAJMRBQDzgAJhb2cHAACHsIATRQBasKATvAABDDAHOzAAAAEAAA4ABACVBAUAHAwAAMYRAwCsEgMApwAAgIWg8AAAE8+wUBBPAAGgkAAAEYqUgQA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/0e6582e80826.js b/pr/672/docs/search.index/0e6582e80826.js new file mode 100644 index 000000000..c3342fa44 --- /dev/null +++ b/pr/672/docs/search.index/0e6582e80826.js @@ -0,0 +1 @@ +rn_("AQsA9KQTAAABAA8AAQABBAGwQBNZAAFlAaMSAADzAAF0AWEUAAABDQDzeBQAADQAAQABEADzpBQAABgAAQDzgQNrbXMBAAA7MAAAAQAABgACAI4TAAD2EwUAI4YCsEAUbAABZXMBBAD0xBAAAAEAAQABAPOAA2NydDOAA7BgEOQAAWFscg==") \ No newline at end of file diff --git a/pr/672/docs/search.index/1089838db3bf.js b/pr/672/docs/search.index/1089838db3bf.js new file mode 100644 index 000000000..6f5e061a1 --- /dev/null +++ b/pr/672/docs/search.index/1089838db3bf.js @@ -0,0 +1 @@ +rn_("AQQBsEAUZgABZfUqFAAAAQABAAEAAQABAgA7MAAAAQAACwACANQTBQCQFAUA8wEBbTswAAABAAANAAQA0AwAAG0RAADpEgUAThQFAPMBAWXyABIAAAEAM4CEsBAQjAABoCAAAAwXFEAE") \ No newline at end of file diff --git a/pr/672/docs/search.index/1105a51fe78c.js b/pr/672/docs/search.index/1105a51fe78c.js new file mode 100644 index 000000000..4ace11eb6 --- /dev/null +++ b/pr/672/docs/search.index/1105a51fe78c.js @@ -0,0 +1 @@ +rn_("AQkA834UAAABADoAAYADoEAAABNdsCAUGgABoCAAAA/TaGl5AQkBoIAAABS2ZgGYEwAAAQMBsAAUMQBQc/KtEAAALQMjgAKgIAAAD89hcgECAPXcEAAAAQABAF8CAQDzAANpdHU6MAAAAQAAAAAAEAAQAAAAfQcsCfMMORC2EO8SKRMqE94T3xPlEw4UDxQvFDAUghSDFAGAAqAwAAAUYbCwE7QAAWFsAQEA8/kPAAABAAEAE4ACsMAUtwABdHUBAwDzwBIAAAEAAQABAQD1PxEAAAEAAQABAAEAI4ACsJAUBwABYXQBBAGgQAAAFGVl8ocUAAABAAECADswAAABAAALAAUAWRAFANASAQABFAEAWBQAAHcUAAATggKw8BS6AAFmZwGBArCAFI4AAaBgAAAUtWRzAQgA9UIUAAABAAEAAQABAPMCAWb1mhAAAAEAAQABAAEAI4ADoCAAAA/NsKAUjAABZHJ0d+8BAI2wEBQ8AAGw4BQ+AAGX1Q47MAAAAQAAOgETAIcEAQC0BAIApgUCALwGBADuBwEAKQwDAFcMAQDkDAEAnQ4EAfAPAQC3EAEAyhACAPgQAQCLEQAAvhEDAOgRAQDiEgAAphMHAJsUBQA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/140824e73115.js b/pr/672/docs/search.index/140824e73115.js new file mode 100644 index 000000000..f32a566c5 --- /dev/null +++ b/pr/672/docs/search.index/140824e73115.js @@ -0,0 +1 @@ +rn_("AYMCsQAUvAABsNAUrAABa20BCwDzARQAAAEAdQAzAYSgYAAAEpWhAAAAFHkEgAM7MAAAAQAANAACAE8HAACgBzMA") \ No newline at end of file diff --git a/pr/672/docs/search.index/148ca916a394.js b/pr/672/docs/search.index/148ca916a394.js new file mode 100644 index 000000000..a4ab5ce83 --- /dev/null +++ b/pr/672/docs/search.index/148ca916a394.js @@ -0,0 +1 @@ +rn_("AQIBsEAUfAABdDswAAABAAANAAYA/hIDAGsTAQDbEwEA8hMBAHEUAQCXFAEAE4ACsDATMgFJaXABBQDzsBMAAAEAAQATgAKgMAAAFAtleVMAA7BwFBcAAWJlcwFAEAAA") \ No newline at end of file diff --git a/pr/672/docs/search.index/1573b4768191.js b/pr/672/docs/search.index/1573b4768191.js new file mode 100644 index 000000000..02d471795 --- /dev/null +++ b/pr/672/docs/search.index/1573b4768191.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAI0AAgBzBwAAdQmMAAGEArBwFDgAAaBAAAARGGlzAQcA8/EQAAABABUBI4ACoLAAABLWYXQBAAOwMAkhAAGwMBOZAAGgIAAAB3RhZG8BFwIAADcDAICGoMAAABQwsAAUAwABAJgV") \ No newline at end of file diff --git a/pr/672/docs/search.index/161c195e5cd2.js b/pr/672/docs/search.index/161c195e5cd2.js new file mode 100644 index 000000000..0af46befa --- /dev/null +++ b/pr/672/docs/search.index/161c195e5cd2.js @@ -0,0 +1 @@ +rn_("AYQCsAARfgABsAARgAABNDbzAgFh8nUMAAABAAEAAPbFEgAAAQBzAAEATwEBAPMAAWTz1BIAAAEArQBjgAOgIAAACPlhZW8=") \ No newline at end of file diff --git a/pr/672/docs/search.index/16a23e4d7f90.js b/pr/672/docs/search.index/16a23e4d7f90.js new file mode 100644 index 000000000..db5a58d22 --- /dev/null +++ b/pr/672/docs/search.index/16a23e4d7f90.js @@ -0,0 +1 @@ +rn_("AQIBoKAAABNvZPLyBgAAAQABgQKgMAAAFHOgUAAAE/5jcwEEA7AgEUwAAaBQAAAR+KBAAAAUO2xzdvI2FAAAAQDzAANhZW87MAAAAQAACAADAIsEAQAEDAAAEA0FAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/1727786f31fa.js b/pr/672/docs/search.index/1727786f31fa.js new file mode 100644 index 000000000..8ede5a34d --- /dev/null +++ b/pr/672/docs/search.index/1727786f31fa.js @@ -0,0 +1 @@ +rn_("AQEA9fQHAAABAAEAAQABAAEAAqAwAAARHKCQAAATkzBo9QISAACZAQEAAQABABOAAqAwAAAPzGRyIwKEsBAI9wNAsCAMAwEMoDAAAA/0EgAY9ZwFAAAtAAEAAQABADOAhLCgEtAAAbBAD9YAARBJAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/1736d97117b1.js b/pr/672/docs/search.index/1736d97117b1.js new file mode 100644 index 000000000..c97a28bcd --- /dev/null +++ b/pr/672/docs/search.index/1736d97117b1.js @@ -0,0 +1 @@ +rn_("AQEAOjAAAAEAAAAAAAgAEAAAAAkJdxAiESMRJBH8E/0ThhSLFAEBAqBAAAAUbqAwAAARKGVo9QEJAAAuAC8DAQCBBQECAPTXEQAAAQABAAEAE4ECsCAR2wABYXPzgAJubwECAPSyEAAAAQABAAEAEwACoNAAABRzY3PyIxAAAAEA84ADZWlv") \ No newline at end of file diff --git a/pr/672/docs/search.index/175082f63529.js b/pr/672/docs/search.index/175082f63529.js new file mode 100644 index 000000000..064a47be3 --- /dev/null +++ b/pr/672/docs/search.index/175082f63529.js @@ -0,0 +1 @@ +rn_("AYkCsCAUcQABoDAAABR7aXATAAOgIAAAEy2ggAAAE0JlaXX1EAwAADoAAQABAAEAAYYCsJAUlwABoAAAABK5YmQBgAKwMBMhAAGgMAAAEPhydtcAAACFoDAAABBsoDAAABN5EAETOjAAAAEAAAAAAAkAEAAAAPsI/Aj9CC0JLgmHEI4QSBFJEf0S") \ No newline at end of file diff --git a/pr/672/docs/search.index/18d8732f90bc.js b/pr/672/docs/search.index/18d8732f90bc.js new file mode 100644 index 000000000..628cbc732 --- /dev/null +++ b/pr/672/docs/search.index/18d8732f90bc.js @@ -0,0 +1 @@ +rn_("AQQA9HQUAAAMACEAEQABAgGwQBK+AAFlAcsPAAABgAKgkAAAFDagkAAAFDcyM5cCAADGoCAAAAx0oGAAABFEsGANCAABAWQAAzswAAABAAAKAAMAGQcFAEAMAAB6EwMA") \ No newline at end of file diff --git a/pr/672/docs/search.index/191d6e7f041f.js b/pr/672/docs/search.index/191d6e7f041f.js new file mode 100644 index 000000000..a0e2613ac --- /dev/null +++ b/pr/672/docs/search.index/191d6e7f041f.js @@ -0,0 +1 @@ +rn_("AQMA9gENAADrAloAAQDRAgEAAQQAOzAAAAEAAAoABACPEAUA0hIBADMTAQBZFAAAAYYCoAAAABNXoAAAABNYZHLzAgF2AYMTAADzgAJlbycBAADHoDAAAA/roHAAABKioIAAABQosNATyQABoLAAABQoQEUICTswAAABAAAJAAMAvgUCAB8HBABaDAEAI4ACJA95bWzNZHIBAAA6MAAAAQAAAAAACgAQAAAAUQlSCSIQcRNyE6IToxNNFKQUvBS9FBOAAqAwAAAQpWx0w4AEoAAAABCVoAAAABCWNDZhZQ==") \ No newline at end of file diff --git a/pr/672/docs/search.index/195d6fe9d0c7.js b/pr/672/docs/search.index/195d6fe9d0c7.js new file mode 100644 index 000000000..2fa924de9 --- /dev/null +++ b/pr/672/docs/search.index/195d6fe9d0c7.js @@ -0,0 +1 @@ +rn_("AQwA9MITAAABAAEAAQABBgDzchEAAAEAAQABggKwYBNIAAGw0BRoAAFpchOAArAQDOkAAWVpFwMAg4ixMBSHAAGgYAAAEXGgUAAAEQWwkBMYAAGwQBEBAAG0EAfzAAFh9D0JAAABAAgIAQA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/19fb50c323e0.js b/pr/672/docs/search.index/19fb50c323e0.js new file mode 100644 index 000000000..4ac74d136 --- /dev/null +++ b/pr/672/docs/search.index/19fb50c323e0.js @@ -0,0 +1 @@ +rn_("AQUA9JQTAAABAAEAAQABAAD1egwAAAEAAQABAAEA84ACZ2sTgAKwsBPvAAFhbwGAAqAgAAAL+qBAAAAMM2V0AYUCsHAUFwABsDATawABZXMTAAOwQA/UAAGgYAAAEYxiZXM7MAAAAQAAEgAEAKsFAQDGBgoAURABAKkQAwABAwD0bRAAAAEABAABAAEKAaBAAAAUZWXyhxQAAAEAE4ACsGASugABY2nzgAJkcvMAAmVvOzAAAAEAAAkAAgCaBwUA3QcDAAECAbAwEVgAAW47MAAAAQAACQABAPALCQDzgAJvdAEDADswAAABAADuAAIAdgcAAAIL7QABAgGgcAAAEf9pOzAAAAEAAAABAgB1BwAAAgr/ABOAAqBAAAAMMnJ1AQQA9EoTAAABAJgAAQABAwDz1BIAAAEArQDzgAJhb9cHAACIsEAM/AABsTAUhAABNIEWOzAAAAEAAB4ADQC/BAEAggUBAAUMAAA4DAcAXAwBAOYPAQDXEAEAYxEAALARAwD0EQEAsBIAABITAQCqFAEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/1aeec3eb1f17.js b/pr/672/docs/search.index/1aeec3eb1f17.js new file mode 100644 index 000000000..b0d4d6a62 --- /dev/null +++ b/pr/672/docs/search.index/1aeec3eb1f17.js @@ -0,0 +1 @@ +rn_("AQUA9MsSAAABAKkAAQABCAD0ihMAAAEAhwABADMBA7BAE1kAAWFiZfX0DAAAAQBFA2kC1QABAAA7MAAAAQAABgACAC0MAQC4DwQA84ACc3UjgQKwgBOEAAFydAGAArBwEwkAAaAQAAAM1G56AQYA9IUQAAABALcAAQDzAANiaXT1egUAAAEA5g5WAAEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/1b0a08e9b744.js b/pr/672/docs/search.index/1b0a08e9b744.js new file mode 100644 index 000000000..b01016095 --- /dev/null +++ b/pr/672/docs/search.index/1b0a08e9b744.js @@ -0,0 +1 @@ +rn_("AYACsAARfgABsAARgAABNDYBCwA7MAAAAQAABgABAB4UBgAjgAKggAAAFEhlbyMCA6BgAAAT3bBAFLcAAWVucAEjDAAA8wACYXYB3gYAAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/1b5ac0a0d3eb.js b/pr/672/docs/search.index/1b5ac0a0d3eb.js new file mode 100644 index 000000000..6651ab407 --- /dev/null +++ b/pr/672/docs/search.index/1b5ac0a0d3eb.js @@ -0,0 +1 @@ +rn_("AQABsKAS0gABZTswAAABAAAJAAIA4wcHABQTAQABAAGgIAAADOttAYQFAAABgAKwoBLQAAGwQA/WAAFsbwEAAPQOEwAAAQABAAEA8wQBZPSxEgAAAQABAAEAIwKEsKAUVgABsEATDAABsHAT/wABFYAAASgUAAABAAGwgBQ8AAFw8jgUAAABAEMAA6AgAAARYKBAAAAStWRlc/P4DwAAUAMBAPOBAmVp1wIAAIawIBKWAAGw0BRAAAEXAQL0owQAAAEAAQDHAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/1c80fc1385ea.js b/pr/672/docs/search.index/1c80fc1385ea.js new file mode 100644 index 000000000..2121e863b --- /dev/null +++ b/pr/672/docs/search.index/1c80fc1385ea.js @@ -0,0 +1 @@ +rn_("AQAA9iARAAABAJIDAQAKAAEAAQAAOzAAAAEAAAYAAgCOEwAA9hMFACOIArBAFGwAAWVzAQ0BsEAUZgABZfUqFAAAAQABAAEAAQABBQA7MAAAAQAACAADAPwRAgACEwQAKRQAANcBAACFBbqAxTPrFIQC88sMAAABAAEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/1d72d41a89e5.js b/pr/672/docs/search.index/1d72d41a89e5.js new file mode 100644 index 000000000..6bd01291e --- /dev/null +++ b/pr/672/docs/search.index/1d72d41a89e5.js @@ -0,0 +1 @@ +rn_("AQYA9BITAAABAJcBAQATAgKwYBMUAAFmbfLiDAAAAQAjggKwQBNDAAFlbAEFAPQFFAAAAQBOAAEA8wMBY/IHEwAAAQABgQKgAAAACSygMAAAD9NzdBOAAqAQAAAHTm5404CEsHAUugABkEAB") \ No newline at end of file diff --git a/pr/672/docs/search.index/1d79643796e7.js b/pr/672/docs/search.index/1d79643796e7.js new file mode 100644 index 000000000..7fa25f6e9 --- /dev/null +++ b/pr/672/docs/search.index/1d79643796e7.js @@ -0,0 +1 @@ +rn_("AQAA9AoRAAABAJIBAQAjgQKgYAAAE0JiaQEAAPTyDwAAAQD1AAcBI4ECsFATVQABZXMTgAKAB1Ee8AFhcgGJArAgFHEAAaAwAAAUe2lwAQIA9CUTAAABAJIAAQABAwD0WxMAAAEAmAABAPOFAmJ1AQIA9HURAAABAHwBAQDzgAJpbLcBAACFoCAAABMtEgEMOjAAAAEAAAAAABYAEAAAAHIH+wf8B/0H/gcQDEoMSwxMDE0MGxDhEHMTdBOvEzEUfhR/FIEUjhSPFLUUuRQ=") \ No newline at end of file diff --git a/pr/672/docs/search.index/1dbb8572c4de.js b/pr/672/docs/search.index/1dbb8572c4de.js new file mode 100644 index 000000000..2fe0f305d --- /dev/null +++ b/pr/672/docs/search.index/1dbb8572c4de.js @@ -0,0 +1 @@ +rn_("AQcA9JQTAAABAAEAAQDzBQF0OzAAAAEAAAgAAgAFDAAAOAwHAAEFAPOwEwAAAQABADcAAICFsDAQTQABoKAAABPOoDAAABQLEEFI") \ No newline at end of file diff --git a/pr/672/docs/search.index/1ed8c6ed4cd9.js b/pr/672/docs/search.index/1ed8c6ed4cd9.js new file mode 100644 index 000000000..5e36d441d --- /dev/null +++ b/pr/672/docs/search.index/1ed8c6ed4cd9.js @@ -0,0 +1 @@ +rn_("AQYA9BYRAAABAL4AAQATgQOgAAAAEV6xIBSiAAFhZG4BBQD0iBAAAAEAAQABAAEUAPXCFAAAAQABAAEAAQABBQGwgBRfAAF2OzAAAAEAAAcAAgDREQMAwhMDACOAArBwEfYAAWJkVwIAAYawwBOMAAGgoAAAE6+gkAAAE3gF4AI7MAAAAQAABQACABUHAwABEAEAAQYA89QSAAABAK0AAQABoGAAAA/tcgGQBAAA1wEAgMWgMAAADA0AAIoK") \ No newline at end of file diff --git a/pr/672/docs/search.index/1fae25a0d497.js b/pr/672/docs/search.index/1fae25a0d497.js new file mode 100644 index 000000000..2d6af6f16 --- /dev/null +++ b/pr/672/docs/search.index/1fae25a0d497.js @@ -0,0 +1 @@ +rn_("AQoBoIAAABS2ZgGYEwAAAQgA9UIUAAABAAEAAQABAPMDAWb1mhAAAAEAAQABAAEA84MCYXMBAgKgIAAAEd2gMAAAEqhiaAHKDwAAI4ACoIAAABNMYXDzAAJobQGmBAAA") \ No newline at end of file diff --git a/pr/672/docs/search.index/1fee3e0237a8.js b/pr/672/docs/search.index/1fee3e0237a8.js new file mode 100644 index 000000000..436d39989 --- /dev/null +++ b/pr/672/docs/search.index/1fee3e0237a8.js @@ -0,0 +1 @@ +rn_("AQAA9HoTAAABAAEAAQATAgKwgBNOAAFzdvRtCQAAAQCRCgEA8wABYwFlDAAAAQICoLAAABRYoLAAABRZY3Pz0BAAAAEAXgABgQKwUBHyAAGgwAAAFCdoafOCAmVp84ACZW8=") \ No newline at end of file diff --git a/pr/672/docs/search.index/206a22bb12ba.js b/pr/672/docs/search.index/206a22bb12ba.js new file mode 100644 index 000000000..6e7a76598 --- /dev/null +++ b/pr/672/docs/search.index/206a22bb12ba.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAYAAQDAFAYA8wYBZfRvFAAAAQAyAAEAEwCEoCAAAAlbsEAUmQABsGAThwCJAQEJ83sQAAB5AgEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/2084af11f5a7.js b/pr/672/docs/search.index/2084af11f5a7.js new file mode 100644 index 000000000..7e04f5c31 --- /dev/null +++ b/pr/672/docs/search.index/2084af11f5a7.js @@ -0,0 +1 @@ +rn_("AYACsRAUmQABsBAMUwABZW8BBQA7MAAAAQAADQABAJwRDQABAgDzBxAAAAEAAQABAgD0AxAAAAEAAQABAAEDADswAAABAAAIAAEAuRAIAIcHAACHsKATxgABsNAUXAABoFAAABQnjBQROzAAAAEAAAYAAwBWBwAA6hADALoSAQDzgQJsdPMAAWk7MAAAAQAAjgADAJwEAAD3EAAACBKMAAEFAPN+FAAAAQA6AAEAAaBAAAAQ+2XzBQkAAAEAoQkTgAKgMAAAD95kcvMAAWXz7AYAAAEAAQDzgAJodAEBADswAAABAACNAAIA9xAAAAgSjAAjgAOwABAYAx+gUAAAECJnaXQBCAD0/BMAAAEAiQAFABMAhLDwFIAAMrCQFGMAAbBQE0EBZECJADswAAABAAASAAkAaRACAO0RAQAuEwEAURMBAIgTAQDtEwEAJRQBADQUAQB1FAEA1wEAAIawgBG0AAGgQAAACSgIMCY6MAAAAQAAAAAADQAQAAAAkQCSAGAHagkDDA8NChELEZ0SnhIlEyYTuBO5Ew==") \ No newline at end of file diff --git a/pr/672/docs/search.index/20b60bb6dc02.js b/pr/672/docs/search.index/20b60bb6dc02.js new file mode 100644 index 000000000..7f07884e7 --- /dev/null +++ b/pr/672/docs/search.index/20b60bb6dc02.js @@ -0,0 +1 @@ +rn_("AQUA9EkUAAABAAEAAQABBQD0OxMAAAEAlAABAPMAAmN19VYQAAABAMYAAQABAAEJAPN+FAAAAQA6AAGBArCAFI4AAaBgAAAUtWRzFwEAhIWw0BS3AAGgQAAAFGGwYBQxAFACkAojgAKgcAAAEV9kcgELAaBAAAAUZWXyhxQAAAEAE4ACsDAQcgABZHLzAgFl9N0HAAABAAEAAQAjgAKwIAwGAAFleQEKAPPAEgAAAQABAAELADswAAABAAAHAAEAphMHAAECAPQFFAAAAQBOAAEAtwAAgIWwcBL2AAGw0BSqAAFgIAYBBgDzzRIAAAEAAQABAwGwQBR8AAF08tsTAAABACOFAqBAAAAUC2JzIwIDsEARAwABoFAAABFwYWJm9wcJAAABAEgAeAcBAGIAAQDzgAJpcAEEAPNvEAAAAQABAPMAAXA7MAAAAQAADAABAN8GDAAjgAOgMAAADBawcBO6AAFhZWnzgANkbW4BAAA6MAAAAQAAAAAABwAQAAAAEglmDLYStxIOEw8TEBMRE/MCAWQ7MAAAAQAACQADAOIRAwCxEgMA5RIBACOAAqCQAAAUWGlvAYADsDATbQABgBOwABABsFARIAABY3N48wABZQHMBAAA5xUAgImgAAAADHegQAAADNGwQAzSAAGQSF0=") \ No newline at end of file diff --git a/pr/672/docs/search.index/228447d4f222.js b/pr/672/docs/search.index/228447d4f222.js new file mode 100644 index 000000000..06e0dce17 --- /dev/null +++ b/pr/672/docs/search.index/228447d4f222.js @@ -0,0 +1 @@ +rn_("AQAA9HoTAAABAAEAAQDzBgFz8v8TAAABAAEBAbBQEvgAAWX3EwkAAFQDAQARBAEA3AMBAAEDAPS+EQAAAQABAAEAAQMA9LoRAAABAAEAAQDzgwJsc/OAAmN0U4ADoBAAAAzKYW9y") \ No newline at end of file diff --git a/pr/672/docs/search.index/22b744f970a4.js b/pr/672/docs/search.index/22b744f970a4.js new file mode 100644 index 000000000..e334d4f88 --- /dev/null +++ b/pr/672/docs/search.index/22b744f970a4.js @@ -0,0 +1 @@ +rn_("AQQA9n4UAAABAA8AAQAmAAQAAQMA88ASAAABAAEAI4ECsSAUugABY2sTgAKgMAAAFDpuclMAA6BAAAAMH2VvcvQoEQAAgAHAAQEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/22eb75e289be.js b/pr/672/docs/search.index/22eb75e289be.js new file mode 100644 index 000000000..014fa7b62 --- /dev/null +++ b/pr/672/docs/search.index/22eb75e289be.js @@ -0,0 +1 @@ +rn_("AYACsFARBgABoLAAABP+Y3MBBADzwA8AAAEAAQABgAKgUAAAENagYAAAEUVpcgGAAqAgAAAMdKBgAAARRGVoAQEA83AMAAABAAEAE4ECoBAAAAxzYWMBgAKwEBPJAAGwMA0WAAFlcyMAAqBAAAAUZWV38ocUAAABAAEDADswAAABAAAFAAIAbAwDAFgRAQABBgD0EhMAAAEAlwEBABMAArBgExQAAWZt8uIMAAABAAEBAPQREQAAAQCwAAEAI4ACoLAAABPlZGcBggKgoAAAE0egQAAAEHRvdvOAA2ducgGAAqBAAAATC6AwAAAPvWhsV38AAIugcAAAEzewQA++AAE8YFc7MAAAAQAAGgAEAAsCAABsBA4A8AsJAM8MAAA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/233f5ec5c226.js b/pr/672/docs/search.index/233f5ec5c226.js new file mode 100644 index 000000000..0f41dca93 --- /dev/null +++ b/pr/672/docs/search.index/233f5ec5c226.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwDLEQIAfhMBABQUAgABBwD2ohMAAAEAqgBXABgAAQABBADzzhEAAAEAAQAjAAKgYAAAEv1hcvPvEAAAAQDJAQGAAqEQAAAUobCAFJcAAWl1dwgAAIixUBS+AAGgAAAAEM+wcBPeAAGhMAAAFLIKQRvyWwkAAEoHAYECoFAAABEUoKAAABNQa3ABAwA7MAAAAQAABgECAOQMAQCdDgQB84ACbHTzgAJhZQ==") \ No newline at end of file diff --git a/pr/672/docs/search.index/23a40eca4762.js b/pr/672/docs/search.index/23a40eca4762.js new file mode 100644 index 000000000..916463b93 --- /dev/null +++ b/pr/672/docs/search.index/23a40eca4762.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAkAAQBOEQkAAQIAOzAAAAEAABMAAQAlEBMAE4GEoFAAABEQoBAAAAwmoCAAAAzwHEAAE4ACsFAThAABYW4zgAOwMA/IAAFpc3Q=") \ No newline at end of file diff --git a/pr/672/docs/search.index/240f796d6ccd.js b/pr/672/docs/search.index/240f796d6ccd.js new file mode 100644 index 000000000..0579e52e7 --- /dev/null +++ b/pr/672/docs/search.index/240f796d6ccd.js @@ -0,0 +1 @@ +rn_("AYICsEASxwABsEASyQABNDYBCwDzyBMAAGoAAQABBgD0sxQAAAEACgABAPMCAXIBERQAAPMDAWU7MAAAAQAABQACAH8QAwAwEQEA84ADYWVp") \ No newline at end of file diff --git a/pr/672/docs/search.index/2424bb274ca9.js b/pr/672/docs/search.index/2424bb274ca9.js new file mode 100644 index 000000000..8d7706aaf --- /dev/null +++ b/pr/672/docs/search.index/2424bb274ca9.js @@ -0,0 +1 @@ +rn_("AYICsCARRgABsDARxAABZXABBQD05g8AAAEA8AABAFOAA7CQEfQAAWFpcgGBArCgFBoAAbCgFBwAAWNyxwAAAIWwUAz2AAGgIAAACW+gIAAADB88AAE6MAAAAQAAAAAACgAQAAAAtQG2AXsE+gYfCSAJfRB+EPEQ8hAHEg==") \ No newline at end of file diff --git a/pr/672/docs/search.index/2618e545b18c.js b/pr/672/docs/search.index/2618e545b18c.js new file mode 100644 index 000000000..554e0dd9f --- /dev/null +++ b/pr/672/docs/search.index/2618e545b18c.js @@ -0,0 +1 @@ +rn_("AQEAOjAAAAEAAAAAAAcAEAAAAFETUhPtE+4TJRQmFHUUdhTzBgFpAXATAABDgAOwABSuAAGwUBNFAFpkZm4BAAGwQBMcAAFlOzAAAAEAAA0AAgDRBgwAEBEAACOAAqAwAAAMEWFlNwAAgMWwUBOZAAGwEBQ+AAGwYBOgAAEAApgI") \ No newline at end of file diff --git a/pr/672/docs/search.index/289f0a680251.js b/pr/672/docs/search.index/289f0a680251.js new file mode 100644 index 000000000..7fd62540e --- /dev/null +++ b/pr/672/docs/search.index/289f0a680251.js @@ -0,0 +1 @@ +rn_("AQMBoEAAABH5ZfMFDQAAFAQBAAEGAPQWEQAAAQC+AAEAE4EDoAAAABFesSAUogABYWRuAQUA9IgQAAABAAEAAQABFAD1whQAAAEAAQABAAEAAQUBsIAUXwABdjswAAABAAAHAAIA0REDAMITAwAjgAKwcBH2AAFiZFcCAAGGsMATjAABoKAAABOvoJAAABN4BeACOzAAAAEAAAUAAgAVBwMAARABAAEGAPPUEgAAAQCtAPOAA3J0dgEDADswAAABAAAHAAIAvhMDAKYUAwDzAAFzAWIUAADzAAFzAfgRAADHAgCAh6CAAAATOKAwAAAMQbAAE3MAAaAQAAAM6xNADg==") \ No newline at end of file diff --git a/pr/672/docs/search.index/28cab8635ca9.js b/pr/672/docs/search.index/28cab8635ca9.js new file mode 100644 index 000000000..7db296d89 --- /dev/null +++ b/pr/672/docs/search.index/28cab8635ca9.js @@ -0,0 +1 @@ +rn_("AQACoCAAABOSsDAR6gABaXTy9hIAAAEAAQMAOzAAAAEAAAYAAQBkEQYAAQgA9PwTAAABAIkABQATAQOwkBRjAAGwUBNBAWRnbHA7MAAAAQAAEgAJAGkQAgDtEQEALhMBAFETAQCIEwEA7RMBACUUAQA0FAEAdRQBAAGAArAwEeYAAbAwEegAAXJ2AQQBsAAStgABZDswAAABAAAFAAIA4hEDAOUSAQC3BQABh7BQErgBF7BgEz8AAZQJAzowAAABAAAAAAALABAAAADfDOAM4QybDpwODBGOEY8RkBGRETUTcBMjgAKgEAAADN5ubwEDAPNmEAAAAQABACOAAqAAAAAMDmRyYwCEoFAAABGLoDAAABCkFAEQ8pgHAAABAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/29212c997b44.js b/pr/672/docs/search.index/29212c997b44.js new file mode 100644 index 000000000..d9630d35e --- /dev/null +++ b/pr/672/docs/search.index/29212c997b44.js @@ -0,0 +1 @@ +rn_("AQEA9GcHAAB6COEAAQABBQD0SRQAAAEAAQABAAEFAPQ7EwAAAQCUAAEA8wICY3X1VhAAAAEAxgABAAEA84ACbnM=") \ No newline at end of file diff --git a/pr/672/docs/search.index/2923606a2790.js b/pr/672/docs/search.index/2923606a2790.js new file mode 100644 index 000000000..1a702708b --- /dev/null +++ b/pr/672/docs/search.index/2923606a2790.js @@ -0,0 +1 @@ +rn_("AQAEoAAAABCZsCARTAABoFAAABH4oEAAABQ7ZGxzdjswAAABAAATAAYArgUAADIMAADoDAAAChANAHARAAA2FAEAAQIA9NcRAAABAAEAAQAThAKwIBHbAAFhc/OAAmFl") \ No newline at end of file diff --git a/pr/672/docs/search.index/29667f8ae9a2.js b/pr/672/docs/search.index/29667f8ae9a2.js new file mode 100644 index 000000000..16c681d25 --- /dev/null +++ b/pr/672/docs/search.index/29667f8ae9a2.js @@ -0,0 +1 @@ +rn_("AQAA9CkTAAABAFgBAQDzBAFzOzAAAAEAAA0AAgCmEwcAmxQFAEcAAAGGoIAAABOGsQAUqgABsEAS4wABsIAUugABsFATKwABFQAW9JYOAAABAAEAAQABBQDz/g8AAAEAAQBjgAOwQBCMAAFtbm8=") \ No newline at end of file diff --git a/pr/672/docs/search.index/297772620241.js b/pr/672/docs/search.index/297772620241.js new file mode 100644 index 000000000..f4a8e6c82 --- /dev/null +++ b/pr/672/docs/search.index/297772620241.js @@ -0,0 +1 @@ +rn_("AYQCsCARhAEioJAAABQpbXABgAKgMAAAERCgUAAAE85jchcBAACGsCATmQABoGAAABM3sMAUXAABoGAAABHgEEAPOzAAAAEAABIABgDNBAgA3hEBAPoSAQDrEwEAQBQBALoUAQA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/297c2deef0ac.js b/pr/672/docs/search.index/297c2deef0ac.js new file mode 100644 index 000000000..2637d0312 --- /dev/null +++ b/pr/672/docs/search.index/297c2deef0ac.js @@ -0,0 +1 @@ +rn_("AYICoFAAABLioQAAABSWY3QjAAKwQA/RAAFldTswAAABAAA3ABYAxAYBANUMAQD2DAEA1g8BAFkQBQCPEAUADREBAGsRAQD8EQIA0BIDAAITBAAzEwEAOxMBAEoTAQDQEwEA4xMBAAEUAQAFFAEAKRQAAFQUAQBYFAEAdxQAAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/298fec6e5a30.js b/pr/672/docs/search.index/298fec6e5a30.js new file mode 100644 index 000000000..0de48fea1 --- /dev/null +++ b/pr/672/docs/search.index/298fec6e5a30.js @@ -0,0 +1 @@ +rn_("AQMA9gENAADrAloAAQDRAgEAAQQAOzAAAAEAAAoABACPEAUA0hIBADMTAQBZFAAAAYYCoAAAABNXoAAAABNYZHLzAgF2AYMTAADzgAJlbycBAADHoDAAAA/roHAAABKioIAAABQosNATyQABoLAAABQoQEUICTswAAABAAAJAAMAvgUCAB8HBABaDAEAI4ACJA95bWzNZHIBAAA6MAAAAQAAAAAACgAQAAAAUQlSCSIQcRNyE6IToxNNFKQUvBS9FBOAAqAwAAAQpWx084ACYWU=") \ No newline at end of file diff --git a/pr/672/docs/search.index/29a80323f87b.js b/pr/672/docs/search.index/29a80323f87b.js new file mode 100644 index 000000000..8abba6fc6 --- /dev/null +++ b/pr/672/docs/search.index/29a80323f87b.js @@ -0,0 +1 @@ +rn_("AQUA9UMQAAABAAEAXwIBAAEEAaAAAAAQ4XP1/gwAAAEAAQBBAwEAAQIA9asRAAABAAEAAQABAAGAArBgExYAAaAgAAARAG95AQABoMAAABQ7afRvEwAACAAcABsAAQYA9KYUAAABAAEAAQABAgD0/hAAAAEAmgABAAECAaBAAAATRnI7MAAAAQAABwACAPwQAQCTEQUA84ACYW9nAwAAhrCAE0UAWrCgE7wAAQwwBjswAAABAAAIAAMAHAwAAMYRAwCsEgMA84ICZWlHAQAAhbBwE8wAAaCAAAAUtqBAAAAP97BABPT6CAAA6AYBALUDAYECoCAAABD6sFASvAABYW8BBAGgAAAAE+VzAfkQAABjBAOwUBFrAAFjZXP2AgkAAAEAAQAsAAEAcQkBAAA7MAAAAQAAJAAIAEIHBwBDEAIAnBENAMsRAgCkEgEAKxMBAH4TAQAUFAIAAQUA8x4TAAABAAEAAQEAOzAAAAEAAAUAAgD/CwMA9hEBAFMAA7AwFFYAAWVwcvTkDwAAAQDvAAEAAQADsEAUHAABoUAAABSLoFAAABHsYmNmAQgHAADjgISxQBSzAAECMBABAYSgsAAAE9qwQBC3AAGwwBQJAAGgYAAAEZIUIAT27wYAAGAJAQA6AS4BFwEBAwA7MAAAAQAABQABANwSBQABAAGgoAAAFEdy9O4PAAABAPcAAQDzgQJncwEHAPVmEwAAAQABAAEAAQABgAKgQAAAEWOwUBFKAAFjafOAAmFvAQABoCAAABGDcvM0DAAAgQNwAROAAoAFcgAdJ3J4AQMCsEATIQABoDAAABN5ZXfyjhAAALsAE4MCoHAAABOzZ28BAgD1ABMAAAEA8gB+AAEAE4ACsDATMgFJaXABBgDzsBMAAAEAAQDzBQJicwFAEAAAAQIA9B4QAAABAAEAAQAjgAKgYAAAEqBiY/dfAQDNsGAQ3wABoJAAABKhwE5yFTowAAABAAAAAAAYABAAAAAaAhsChASFBIYEEgdNB34HfweAB4EHggeDB/oLdAwEDTsQTRBOEBsRmxGZFJoUrhSvFA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/29d2100cf07e.js b/pr/672/docs/search.index/29d2100cf07e.js new file mode 100644 index 000000000..90044f0a5 --- /dev/null +++ b/pr/672/docs/search.index/29d2100cf07e.js @@ -0,0 +1 @@ +rn_("AYACsHAR3gABsCAP6gDxaHkBBQD0Bg0AAAEAQQMBAAEAAPRgDAAAAQABAG8AAQIAOjAAAAEAAAAAAAkAEAAAAN8P4A/WEJIR8BLxEvETeBSsFK0UAQMA8xsJAAABAAEA1wMAAIagQAAAEfkYQAc6MAAAAQAAAAAAFwAQAAAAaAdpB/MHKAlcCV0JBQ3ID8kP7Q+YEJ8QoBChEKIQ4hDjEBkRGhGVEu8T8BMaFBsU") \ No newline at end of file diff --git a/pr/672/docs/search.index/29f7585ca898.js b/pr/672/docs/search.index/29f7585ca898.js new file mode 100644 index 000000000..32232b73f --- /dev/null +++ b/pr/672/docs/search.index/29f7585ca898.js @@ -0,0 +1 @@ +rn_("AYADsAAT5gABsAAT6AABsHAUXwABNDZhAQQA9LMUAAABAAoAAQAjgQKgQAAAEV5tcAECADswAAABAAAOAAQAagcBAAgNAQBOEQkAjBEAAAEBADswAAABAAAIAAMA1hIAAEwTAADAFAYAAQEAOzAAAAEAAAoAAwByEQIAvhMDAKYUAwAjgAKgMAAAEFhkZ9cDAADGoIAAABKwQEaACDswAAABAAAYAAkA9Q8BABYRAQBKEQEAWhEDANERBQDCEwMAERQAAG8UAQCiFAEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/2b576bc01ad5.js b/pr/672/docs/search.index/2b576bc01ad5.js new file mode 100644 index 000000000..3036ced2a --- /dev/null +++ b/pr/672/docs/search.index/2b576bc01ad5.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAAkABABcCQEAnxADAOIQAQDvEwEAAQIBsEAUfAABdDswAAABAAANAAYA/hIDAGsTAQDbEwEA8hMBAHEUAQCXFAEAE4ACsDATMgFJaXABiAKwABPmAAGwABPoAAE0NhOBAqAwAAARhWRzAYACoCAAAAx3sGAUFwABbngBBQDzsBMAAAEAAQATgAKgMAAAFAtlefMAhBIAA/JAEAAA5gDzgAJydA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/2b9e99969beb.js b/pr/672/docs/search.index/2b9e99969beb.js new file mode 100644 index 000000000..ddf9a4c0d --- /dev/null +++ b/pr/672/docs/search.index/2b9e99969beb.js @@ -0,0 +1 @@ +rn_("AQQA9goNAAABAD8DAQAuAQEAAQoA89QSAAABAK0AAQEA9x8JAAABAF0HAQBzAAEAFQEjgAKwYBPvAAFpbwECAPTIDAAAAQBNBgEAAQUA9AUUAAABAE4AAQD3CAAAiLCQE7wAAaAgAAAMyrCQEfoAAQTcAzowAAABAAAAAAAIABAAAACVBRAJEQljDGQMARECEQcTCBM=") \ No newline at end of file diff --git a/pr/672/docs/search.index/2ce5f6d932f6.js b/pr/672/docs/search.index/2ce5f6d932f6.js new file mode 100644 index 000000000..965ce4401 --- /dev/null +++ b/pr/672/docs/search.index/2ce5f6d932f6.js @@ -0,0 +1 @@ +rn_("AQACoCAAABOSsDAR6gABaXTy9hIAAAEAAQMAOzAAAAEAAAYAAQBkEQYAAQgA9PwTAAABAIkABQATAQOwkBRjAAGwUBNBAWRnbHA7MAAAAQAAEgAJAGkQAgDtEQEALhMBAFETAQCIEwEA7RMBACUUAQA0FAEAdRQBAAGAArAwEeYAAbAwEegAAXJ2AQQBsAAStgABZDswAAABAAAFAAIA4hEDAOUSAQC3BQABh7BQErgBF7BgEz8AAZQJAzowAAABAAAAAAALABAAAADfDOAM4QybDpwODBGOEY8RkBGRETUTcBMjgAKgEAAADN5ubwEDAPNmEAAAAQABACOAAqAAAAAMDmRyYwGEoFAAABGLoDAAABCkFAEQ8pgHAAABABOAArBgEtoAAWNnE4ADoLAAABQZoEAAAA6aZW9z") \ No newline at end of file diff --git a/pr/672/docs/search.index/2d9a6dddfba0.js b/pr/672/docs/search.index/2d9a6dddfba0.js new file mode 100644 index 000000000..5445c6f66 --- /dev/null +++ b/pr/672/docs/search.index/2d9a6dddfba0.js @@ -0,0 +1 @@ +rn_("AQIA9L4RAAABAAEAAQDzAAFl9MQQAAABAAEAAQABAwD0uhEAAAEAAQABAMcAAACFsKATHAABsCAMJAABsIAS+gABCAwG8wUHAAABAAEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/2da0a77b4fbc.js b/pr/672/docs/search.index/2da0a77b4fbc.js new file mode 100644 index 000000000..c77832b05 --- /dev/null +++ b/pr/672/docs/search.index/2da0a77b4fbc.js @@ -0,0 +1 @@ +rn_("AQAA9AoRAAABAJIBAQAjgQKgYAAAE0JiaQEAAPTyDwAAAQD1AAcBI4ICsFATVQABZXMBiQKwIBRxAAGgMAAAFHtpcAECAPQlEwAAAQCSAAEAAQMA9FsTAAABAJgAAQDzhQJidQECAPR1EQAAAQB8AQEA84ACaWy3AQAAhaAgAAATLRIBDDowAAABAAAAAAAUABAAAAByB/sH/Af9B/4HEAxKDEsMTAxNDBsQ4RCvEzEUfhR/FIEUjhSPFLUUuRQBBQDzFBQAAAEAAQBDhgOwkBSXAAGgAAAAErliZHcBgAKwAAkZAAGwIA0DA2lkbgEDAPPwDwAAAQAHAQECAPYaDAAAAQCOAwEA5gMBAAEAAPQ3CQAAAQABAAEAAQAAOzAAAAEAAAYAAgCOEwAA9hMFACOBArBAFGwAAWVz84ADZXh5AQAA9DMJAAABAAEAAQAjgAKwIBMhAAFyeAcNAADIoJAAABNwsFAQHAABsMATugABoBAAABFxsEAM5gABAEeACzowAAABAAAAAAAJABAAAABwBXEF7AztDAEN7A9GEEcQGBMZEwEGAPQjEwAAAQCSAAEA8wMBYvQIEQAAAQCSAQEAtwwAAImgcAAAEqewkBNTAAGwYBA+AAGgMAAAE3k4iRM6MAAAAQAAAAAALwAQAAAAwQXCBcMFxAU0BzUH+wj8CP0ILQkuCR8MMwz4DPkM+gz+DP8MAA3oD+kPPBA9EEEQQhBfEGAQYRBiEGMQZBBlEIcQjhCoEK0Q2RDaEEgRSRH9Eo8TsxPaE3QUgBShFLIU") \ No newline at end of file diff --git a/pr/672/docs/search.index/2dd3fa22e8d4.js b/pr/672/docs/search.index/2dd3fa22e8d4.js new file mode 100644 index 000000000..9c015e1d8 --- /dev/null +++ b/pr/672/docs/search.index/2dd3fa22e8d4.js @@ -0,0 +1 @@ +rn_("AYACsCATRQBaoFAAABEybXNDgAOw4BQHAAEpfC3u8KxkZ3IBAwDz1xIAAAEAAQAjgAKwMBQMAAFjaTOAA7AADQMDaWFpbw==") \ No newline at end of file diff --git a/pr/672/docs/search.index/2eede6b213b7.js b/pr/672/docs/search.index/2eede6b213b7.js new file mode 100644 index 000000000..00b414f24 --- /dev/null +++ b/pr/672/docs/search.index/2eede6b213b7.js @@ -0,0 +1 @@ +rn_("AQQBoFAAABRecwGgEgAAAYICsEASxwABsEASyQABNDYBCwDzyBMAAGoAAQABBgD0sxQAAAEACgABAPMCAXIBERQAAPMDAWU7MAAAAQAABQACAH8QAwAwEQEA84QDYWVpY4ADoFAAABSWY2V0") \ No newline at end of file diff --git a/pr/672/docs/search.index/3067b7ed1d8a.js b/pr/672/docs/search.index/3067b7ed1d8a.js new file mode 100644 index 000000000..e5b0a2e90 --- /dev/null +++ b/pr/672/docs/search.index/3067b7ed1d8a.js @@ -0,0 +1 @@ +rn_("AQcA9eATAAABAAEAeAABAAEHAPPKEAAAAQABAAGAAqAAAAAHWqAQAAAJCmR48wEBZfIcEAAAAQDnAAAAhbBgDOwAAbBADFwAATIIAfTLBAAAhQLdCQEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/30dacb34bdb2.js b/pr/672/docs/search.index/30dacb34bdb2.js new file mode 100644 index 000000000..5c5fe1d8a --- /dev/null +++ b/pr/672/docs/search.index/30dacb34bdb2.js @@ -0,0 +1 @@ +rn_("AQIAOjAAAAEAAAAAAA4AEAAAAA0NDg0VEfkR+hH7EbUSvhK/EsMSxBIcEx0TVRNWEwEJAPN7EQAAAQABAAEKAPPNEgAAAQABAAEGAPU/EQAAAQABAAEAAQDzgAJkbnMAhLEgFD4AAQARA/InEwAAAQABDQD2ohMAAAEAqgBXABgAAQABgAKwoBO6AAGgEAAADDZjdQEBAPTMEwAAAQBKAAEA8wABZfJxCQAAAQD3AACAxbAwELcAAQACqAg=") \ No newline at end of file diff --git a/pr/672/docs/search.index/31541a51e8db.js b/pr/672/docs/search.index/31541a51e8db.js new file mode 100644 index 000000000..55530a374 --- /dev/null +++ b/pr/672/docs/search.index/31541a51e8db.js @@ -0,0 +1 @@ +rn_("AQEA9T8RAAABAAEAAQABACOBArCQFAcAAWF0Q4ADsRAUhwABsIATyQABY290EwACsAAUXAABbnL2DQIAAAEAewIBAKwCXAw=") \ No newline at end of file diff --git a/pr/672/docs/search.index/31eb7fecbe0b.js b/pr/672/docs/search.index/31eb7fecbe0b.js new file mode 100644 index 000000000..168daad6f --- /dev/null +++ b/pr/672/docs/search.index/31eb7fecbe0b.js @@ -0,0 +1 @@ +rn_("AQcA9AMSAAABAAEAAQAjAAKgcAAAEgJhcgF4BwAAAQAA9iARAAABAJIDAQAKAAEAAQAAOzAAAAEAAAYAAgCOEwAA9hMFACOIArBAFGwAAWVzAQ0BsEAUZgABZfUqFAAAAQABAAEAAQABBQA7MAAAAQAACAADAPwRAgACEwQAKRQAANcDAACGBbqAxTPrFIQG9cEEAAABAAkIAQABAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/32222f9e344c.js b/pr/672/docs/search.index/32222f9e344c.js new file mode 100644 index 000000000..a0d067b6f --- /dev/null +++ b/pr/672/docs/search.index/32222f9e344c.js @@ -0,0 +1 @@ +rn_("AQIA9CUTAAABAJIAAQABAgD0/hAAAAEAmgABAAEDAPRbEwAAAQCYAAEAAQIBoEAAABNGcjswAAABAAAHAAIA/BABAJMRBQDzAIQDQAg6MAAAAQAAAAAAFQAQAAAASgdLB0wH2A/ZD4UQhhCmEKcQPRE+EfAR8REjEyQTMBMxE0ITbRNuE7YTtxM=") \ No newline at end of file diff --git a/pr/672/docs/search.index/336c95938b04.js b/pr/672/docs/search.index/336c95938b04.js new file mode 100644 index 000000000..0141a16db --- /dev/null +++ b/pr/672/docs/search.index/336c95938b04.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwDLEQIAfhMBABQUAgABBADzzhEAAAEAAQAjAAKgYAAAEv1hcvPvEAAAAQDJAQGAAqEQAAAUobCAFJcAAWl1NwQAgoexUBS+AAGgAAAAEM+wcBPeAAGhMAAAFLIKQBsTgAKwEBMJAAFpcg==") \ No newline at end of file diff --git a/pr/672/docs/search.index/34d17abef16f.js b/pr/672/docs/search.index/34d17abef16f.js new file mode 100644 index 000000000..a9313f8c0 --- /dev/null +++ b/pr/672/docs/search.index/34d17abef16f.js @@ -0,0 +1 @@ +rn_("AYECoBAAABNHoHAAABOPYWMBhwKwABPmAAGwABPoAAE0NlMAhKCQAAATb7CwFAwAAYEACfUkBwAANgA/CREBIAA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/3545cf7f836f.js b/pr/672/docs/search.index/3545cf7f836f.js new file mode 100644 index 000000000..344bd9683 --- /dev/null +++ b/pr/672/docs/search.index/3545cf7f836f.js @@ -0,0 +1 @@ +rn_("AQUA8x4TAAABAAEAAQAAOzAAAAEAAAUAAgD/CwMA9hEBAPMAAWT0/hAAAAEAmgABAFMAA7AwFFYAAWVwcvTkDwAAAQDvAAEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/357d49476acc.js b/pr/672/docs/search.index/357d49476acc.js new file mode 100644 index 000000000..3ac860d5d --- /dev/null +++ b/pr/672/docs/search.index/357d49476acc.js @@ -0,0 +1 @@ +rn_("AQYA9I4RAAABAAEAAQABAgA7MAAAAQAACgAEAI8QBQDSEgEAMxMBAFkUAADzgAJmbgEBADswAAABAAAIAAIAog8GAF4TAQDzgAJkbgEBAPMuBwAAAQABAAGGAqAAAAATV6AAAAATWGRy8wEBdgGDEwAAQwADsIAS5wABsLAUiQABa2xyOzAAAAEAAAwAAwAlBwgABg0BAEgQAQABAgD19AcAAAEAAQABAAEA8wCEFEEAAYAAAAA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/358b5f8021f2.js b/pr/672/docs/search.index/358b5f8021f2.js new file mode 100644 index 000000000..bdc66b3c7 --- /dev/null +++ b/pr/672/docs/search.index/358b5f8021f2.js @@ -0,0 +1 @@ +rn_("AQIA9L4RAAABAAEAAQATAAKgMAAAEYlldPTEEAAAAQABAAEAAQMA9LoRAAABAAEAAQABgAKgAAAAEJWgAAAAEJY0NvMDAXYBAg0AAJcCAACIsKATHAABsCAMJAABsGATFgABsIAS+gABoCAAABEACUxGOjAAAAEAAAAAAAkAEAAAAAUHBgcHBzUM9w9fEakSqhKrEl4U") \ No newline at end of file diff --git a/pr/672/docs/search.index/358dcb9f0de5.js b/pr/672/docs/search.index/358dcb9f0de5.js new file mode 100644 index 000000000..7afd3ff68 --- /dev/null +++ b/pr/672/docs/search.index/358dcb9f0de5.js @@ -0,0 +1 @@ +rn_("AQQAOjAAAAEAAAAAAAcAEAAAANgP2Q+mEKcQIxMkE7YTtxMBBAD02g8AAAEAAQABAAEAAqAQAAAMg7BQFAMAAWlv8ikJAAABAPOAA2Vpbw==") \ No newline at end of file diff --git a/pr/672/docs/search.index/35eb33e7a6e8.js b/pr/672/docs/search.index/35eb33e7a6e8.js new file mode 100644 index 000000000..d4d33881c --- /dev/null +++ b/pr/672/docs/search.index/35eb33e7a6e8.js @@ -0,0 +1 @@ +rn_("AQIBsEAUfAABdDswAAABAAANAAYA/hIDAGsTAQDbEwEA8hMBAHEUAQCXFAEAE4ACsDATMgFJaXABBQDzsBMAAAEAAQATgAKgMAAAFAtleVMBA7BwFBcAAWJlcwFAEAAAE4ADsDAUjAABsCAG+wABZWxz") \ No newline at end of file diff --git a/pr/672/docs/search.index/36ce02943eb1.js b/pr/672/docs/search.index/36ce02943eb1.js new file mode 100644 index 000000000..594b1868a --- /dev/null +++ b/pr/672/docs/search.index/36ce02943eb1.js @@ -0,0 +1 @@ +rn_("AQEBoEAAABGJc/U1DAAAKgVKAQEAAQATggKgIAAADQxlcgECAPTuDwAAAQD3AAEAI4ECoBAAAAw0ZWkTgAKg8AAAFChucjMAA7AwCSYAAWVvdAHdEQAA") \ No newline at end of file diff --git a/pr/672/docs/search.index/36ce51aa446c.js b/pr/672/docs/search.index/36ce51aa446c.js new file mode 100644 index 000000000..4bbfade70 --- /dev/null +++ b/pr/672/docs/search.index/36ce51aa446c.js @@ -0,0 +1 @@ +rn_("AQUBsEASwwABZTswAAABAAAFAAIA5xIBAEkUAwABggKgkAAAFDagkAAAFDcyMwEAhKAgAAASn6AgAAAI+LCAFK4AAbAwDgYAAQCRATswAAABAAAMAAYAngQAANYEAwBpCQAAsQ8DAAsUAAAcFAEA8wABcwEyEQAAAQUA9JQTAAABAAEAAQABAAD1egwAAAEAAQABAAEA84ACZ2tzgISwsBPvAAERSAABBQGgQAAAEfll8wUNAAAUBAEAE4ACoDAAAAwzZXIBgAOwUBQvAAGgEAAAC/qgcAAAE4NnbnKjAISgUAAAFGKgoAAAE/4UAQQ7MAAAAQAAEQAEAAECAQC5EAgAthEDAMASAgABhQKwcBQXAAGwMBNrAAFlcxMAA7BAD9QAAaBgAAARjGJlczswAAABAAASAAQAqwUBAMYGCgBREAEAqRADAAEDAPRtEAAAAQAEAAEAAQoBoEAAABRlZfKHFAAAAQATgAKwYBK6AAFjafOAAmRy8wACZW87MAAAAQAACQACAJoHBQDdBwMAAQIBsDARWAABbjswAAABAAAJAAEA8AsJAAEAAPSuEAAAAQABAAEAAQEAOzAAAAEAAO4AAgDGBQAAAQjtAGMAA7AQEycAAWVpczowAAABAAAAAAAOABAAAAALCUYJRwlICUkJGgwbDKkPqg/TD+oP2xAAEZATkRPjgISwIAdYAAERQAQBgYSwMAwGAAGgQAAADNGggAAAEV+wQAzSAAEAAB0BBQD14BMAAAEAAQB4AAEAE4ACoAAAAAb6YWcBAwD0mBIAAAEAbgEBAPOAAmlvAQEAOzAAAAEAAO4AAgB2BwAAAgvtAAEIAbBAFGYAAWX1KhQAAAEAAQABAAEA84ECZ3QBAQGgcAAAEf9pOzAAAAEAAAABAgB1BwAAAgr/AAEFAPPKEAAAAQABAPOAAmFvE4ACoEAAAAwycnUBgAKgAAAAB1qgEAAACQpkeBMAArAgDPwAAWVy8hwQAAABABOAArCwFLAAAXJ4AQoA9JsTAAABAAEAAQATgAKwEBEUAjxhZQEEAPSpEAAAAQABAAEAAQQAOjAAAAEAAAAAAAcAEAAAADsTPBNKE0sT0BPRE+MT5BMBAwDz1BIAAAEArQCXAQCAhbBAEvwAPLBAEQYAAYFIAce/AwCPoIAAABLWsGAM7AABsEAMXAABsTAUhAABP6kfOzAAAAEAAEsAIgB6AAAAfwAAAL8EAQDLBAAAggUBAEAHAQBQBwAAVAcBAD8JAABTCQcAawkBAAUMAAAdDAEAJAwBADgMBwBcDAEAeAwBAMgMAQDmDwEAVhABANcQAQDkEAEA+xAAAB0RAgAtEQEAPxEEAGMRAACwEQMA9BEBALASAAASEwEAFhMBAEYTAACqFAEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/378b1aab08c9.js b/pr/672/docs/search.index/378b1aab08c9.js new file mode 100644 index 000000000..b18d85209 --- /dev/null +++ b/pr/672/docs/search.index/378b1aab08c9.js @@ -0,0 +1 @@ +rn_("AQMBoEAAABGCZQHGDwAAAQIA8y8MAAABAAEAIwACsCAMHQABa3L28AYAAAEAFArtAAEANALzAAJyePLvCAAAAQA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/37cb471d121c.js b/pr/672/docs/search.index/37cb471d121c.js new file mode 100644 index 000000000..1e9118e4a --- /dev/null +++ b/pr/672/docs/search.index/37cb471d121c.js @@ -0,0 +1 @@ +rn_("AQoA9KQTAAABAA8AAQABgwKgIAAAEEygMAAAEOlpdAEAAPQOEwAAAQABAAEA8wQBZPSxEgAAAQABAAEAI4aEsKAUVgABsEATDAABsHAT/wABFYAAAQACsDAQPgABsBATvAABc3TzlwUAAIEGAQABgAKwwBSEAAGgAAAAB1B0efOAhRHBAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/37eaa116de94.js b/pr/672/docs/search.index/37eaa116de94.js new file mode 100644 index 000000000..07862a0d0 --- /dev/null +++ b/pr/672/docs/search.index/37eaa116de94.js @@ -0,0 +1 @@ +rn_("AQMBgBOAABD4czswAAABAAAFAAEAYBMFACOCAqCQAAAUYmlzAQEA9CUTAAABAJIAAQAjgAKwoBQMAAFjbgGEArCgFBoAAbCgFBwAAWNyAQIAOzAAAAEAAAYAAQBfEAYAE4ICoCAAABP+ZXUBAgDzzhEAAAEAAQABgAKwIBQ+AAGgYAAAE49vdmOBA6AQAAATR2FjcwEDADswAAABAAAFAAEA1wwFAAEBAPMbCQAAAQABAAEBAPT+EAAAAQCaAAEAU4ADoMAAABRubnB1AYACsDAQHAABoWAAABSlYWUBgQKwQBLHAAGwQBLJAAE0NgEFAaBQAAAUJ2jyuhIAAAEAAQUA9HUQAAABALMAAQBzgISwMBNzAAEBoAQBCADzbxMAAAgAHAATgAKwQBHyAAFhZQEKAPPIEwAAagABAAEAAaAgAAAQl3IBYBEAAAEAAPTMEwAAAQBKAAEApwEAgIWgMAAAEPugEAAAERsJICEBCQGgQAAAFGVl8ocUAAABAEOAhKAgAAAUc6CQAAATy6EwAAAUhhBJAAECAPRbEwAAAQCYAAEAE4ACsEATJwABaXkBAQKgMAAAEv2gQAAAE0ZncjswAAABAAAHAAIA/BABAJMRBQABgAKwABPmAAGwABPoAAE0NgEGAPSzFAAAAQAKAAEA8wICcnYBERQAAPMCAWU7MAAAAQAABQACAH8QAwAwEQEA84ACZHTn1x8AEaAwAAARHLDwFLAAAbBwE5AAATBhYmNlaGlrbG5vcHJzdHV2OzAAAAEAABgBRADDBAAAqQUBALEFAACzBQEAwQYAACQHAABKBwIAWgcAAGwHBQBzBwAA1AcAAPkHAQD/CAEABQkBABIJAABxCQEAdQmMAP8LAwAODAAAEgwDACAMAABZDAAAZgwAAK0PAQDYDwUAWBAAAIUQAQCZEAAAphABAO8QAQDzEAMABhEBAD0RAQBeEQAAexECAKoRAADKEQAA7BEAAPARAQD2EQEAAhIEAKcSAAC2EgEAuRIAAMUSAQDXEgIADhMDACMTAQAwEwEAORMBAD8TAQBCEwAARRMAAFcTAABtEwEAjBMBAJsTBAC2EwEAyRMBAMwTAQDSEwEA3RMAABcUAgAeFAYAPBQBAEgUAABfFAEAiRQBAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/380eb6ff2905.js b/pr/672/docs/search.index/380eb6ff2905.js new file mode 100644 index 000000000..e155755db --- /dev/null +++ b/pr/672/docs/search.index/380eb6ff2905.js @@ -0,0 +1 @@ +rn_("AQUA9AUUAAABAE4AAQDzAwFj8gcTAAABACOAArBwFLoAAWhvAYICsAAS4wABsAAR6gABZXNDAAOgoAAAFFmgIAAAE5JlaXTy9hIAAAEAAQIAOzAAAAEAAAYAAQBkEQYAAQIBoEAAABQ6ZfJoFAAAAQDzgAJpbwEIAPT8EwAAAQCJAAUAEwEDsJAUYwABsFATQQFkZ2xwOzAAAAEAABIACQBpEAIA7REBAC4TAQBREwEAiBMBAO0TAQAlFAEANBQBAHUUAQABgAKwQBK4ARegIAAAEShhaQEDAPPXEgAAAQABACOBArAwFAwAAWNpAQAA9CkTAAABAFgBAQDzAwFzOzAAAAEAAA8AAwDoEQEAphMHAJsUBQABAwD15hEAAAEAJQEBAGEB84ACcnYBAAA7MAAAAQAABQACALYSAQAOEwMA8wIBZDswAAABAAAJAAMA4hEDALESAwDlEgEAI4ACoJAAABRYaW8TgAKw8BSqAAFsbwEFAPR0FAAADAAhABEAI4ACoGAAABOGZ3MjgAKwkBRWAAFibPfHAACNsGAUjAABsHAT/wABsGATPwABsFATKwABtckXOjAAAAEAAAAAACAAEAAAALUFAQkvCV4MXwzfDOAM4QyWDpcOmA6ZDpsOnA78D/0P/g//DwAQ0BDREAwRLxGOEY8RkBGREeARNRNOE08TcBMoFA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/3840c747afc6.js b/pr/672/docs/search.index/3840c747afc6.js new file mode 100644 index 000000000..fb1f58fee --- /dev/null +++ b/pr/672/docs/search.index/3840c747afc6.js @@ -0,0 +1 @@ +rn_("AQAA9HoTAAABAAEAAQABBADzwA8AAAEAAQABgAKgUAAAENagYAAAEUVpcgGAAqAgAAAMdKBgAAARRGVoAQEA83AMAAABAAEAE4ECoBAAAAxzYWMBAwA7MAAAAQAABQACAGwMAwBYEQEAAQYA9BITAAABAJcBAQATAAKwYBMUAAFmbfLiDAAAAQAjgAKgwAAAE+VucqcPAACIoEAAAA+9sEAPvgABNCAXOzAAAAEAABkAAwALAgAAbAQOAPALCQABgAOwcBOZAAGwMBNDAAGwgBPSAAFjcnMBAAGwIBOZAAFvOjAAAAEAAAAAAAkAEAAAAN4R3xH6EvsS6xPsE0AUQRS6FLsUAYACsCAJIQABsDATTgABcnT3BAAAyKAgAAAHdKBgAAAP7bCAE04AAUAGkgn2FwIAAHkC3QQBAJEKAQA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/38b51321ef64.js b/pr/672/docs/search.index/38b51321ef64.js new file mode 100644 index 000000000..62de8b389 --- /dev/null +++ b/pr/672/docs/search.index/38b51321ef64.js @@ -0,0 +1 @@ +rn_("AQMA9H8MAAABAAEAAQABAAGwUBQ+AAFyAcMEAABTAYSwQBK+AAGwQBR8AAGYAAQ6MAAAAQAAAAAAKAAQAAAAjQSOBI8EmAUMCQ0JMglmCdQMyw8+ED8QTBB8ENwQ3RDeECcRnxL+Ev8SABMBEy0TPRM+E2sTbBPbE9wT4BPhE+IT8hPzE1oUWxRxFHIUlxSYFA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/3a1ad02b1851.js b/pr/672/docs/search.index/3a1ad02b1851.js new file mode 100644 index 000000000..4242c21cf --- /dev/null +++ b/pr/672/docs/search.index/3a1ad02b1851.js @@ -0,0 +1 @@ +rn_("AQAA9DsHAAABAAEAAQABhQKwcBQXAAGwMBNrAAFlcxMAA7BAD9QAAaBgAAARjGJlczswAAABAAASAAQAqwUBAMYGCgBREAEAqRADAAECAPO1BQAARwoBAAEDAPXMDwAAoQABAAQAAQABCgGgQAAAFGVl8ocUAAABACMABKAwAAARHKCQAAATk7BgEroAATBjaGn1AhIAAJkBAQABAAEA84ACZHLnBAAAh7AQCPcDQLAgDAMBDKAwAAAP9BJBODswAAABAAASAAYAjAACAJwFAADJBQMAmgcFAN0HAwANDAAA") \ No newline at end of file diff --git a/pr/672/docs/search.index/3a8f25986add.js b/pr/672/docs/search.index/3a8f25986add.js new file mode 100644 index 000000000..8aba7e1e3 --- /dev/null +++ b/pr/672/docs/search.index/3a8f25986add.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAEQAAgAeCQAAhAxDAAEFAPTEEAAAAQABAAEAE4ACsLATpAABYWwBBAD0sQ8AAAEAAQABAHMABKAQAAAQeGJwcnY7MAAAAQAACgACAGQRBgC6EQMAAYACsIAREwIjsOAUDAABaXIBAwGwQBFhAAFlAZUOAAABBQA7MAAAAQAABgABAF8QBgABAwA7MAAAAQAABQABANcMBQDzAAJwczowAAABAAAAAAANABAAAACpBaoFwQYSCVkMZgwGEQcRthK3Eg4TDxMQExETVwAAAIWwUBC2AjmwUA/QANOgQAAAEYxIgAM7MAAAAQAADwAGAKIEAABoBQMAvQ8AAOIRAwCxEgMA5RIBANMAhKBgAAAM6xAIBvOvAQAAAQAaCw==") \ No newline at end of file diff --git a/pr/672/docs/search.index/3b45dd23db88.js b/pr/672/docs/search.index/3b45dd23db88.js new file mode 100644 index 000000000..bcaa11e49 --- /dev/null +++ b/pr/672/docs/search.index/3b45dd23db88.js @@ -0,0 +1 @@ +rn_("AQAA9HUQAAABALMAAQABhAKwABF+AAGwABGAAAE0NvMCAWHydQwAAAEAAQEA9sUSAAABAHMAAQBPAQEA84ACZW/zgAJoaw==") \ No newline at end of file diff --git a/pr/672/docs/search.index/3be8e84e40bd.js b/pr/672/docs/search.index/3be8e84e40bd.js new file mode 100644 index 000000000..2f02ad5d5 --- /dev/null +++ b/pr/672/docs/search.index/3be8e84e40bd.js @@ -0,0 +1 @@ +rn_("AQIA9eYRAAABACUBAQBhAQEBAPciEQAAAQABANgCAQCJAAUAAQIA9LIQAAABAAEAAQATgAKg0AAAFHNjc/OAAmVvI4ACsAAHUgABbHTzAAJhcvKrDwAAAQA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/3c4d6a050636.js b/pr/672/docs/search.index/3c4d6a050636.js new file mode 100644 index 000000000..19633e0e1 --- /dev/null +++ b/pr/672/docs/search.index/3c4d6a050636.js @@ -0,0 +1 @@ +rn_("AQMA82YQAAABAAEAAYACoLAAABQnsDATcwABbnRDAAOgIAAAERuwUBHyAAFlaGk7MAAAAQAABgADAA4MAAADEgMA0hMBAPOAAmRy") \ No newline at end of file diff --git a/pr/672/docs/search.index/3fae53857d69.js b/pr/672/docs/search.index/3fae53857d69.js new file mode 100644 index 000000000..c8988efc7 --- /dev/null +++ b/pr/672/docs/search.index/3fae53857d69.js @@ -0,0 +1 @@ +rn_("AQMBsAAM8wNGc/QpDAAAAQABAAEAAQIA9CcMAAABADkFAQAjAAKgQAAADO9lcvK4BAAAAQABgAKgQAAAE92wMBL4AAFhcwECAaBAAAAUZWXyhxQAAAEA84AEbG5ydg==") \ No newline at end of file diff --git a/pr/672/docs/search.index/40fbc4e3aee2.js b/pr/672/docs/search.index/40fbc4e3aee2.js new file mode 100644 index 000000000..878137268 --- /dev/null +++ b/pr/672/docs/search.index/40fbc4e3aee2.js @@ -0,0 +1 @@ +rn_("AQIBoQAAABSAZPJgBwAACgIBBgGgMAAAEe90Aa8TAAABAwGwUBL4AAFl9RMJAABUAwEAEQQBAAEBAaEAAAAUdGTysgUAAD4CFwcAAIewoBL2AAGg4AAAFEewEAWzAAEegArzYgQAAAEAAQA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/4129fde217fe.js b/pr/672/docs/search.index/4129fde217fe.js new file mode 100644 index 000000000..314b8d3c1 --- /dev/null +++ b/pr/672/docs/search.index/4129fde217fe.js @@ -0,0 +1 @@ +rn_("AYACsAAQIwABsCAMXgABZWkBiQKwIBRxAAGgMAAAFHtpcBMAA6AgAAATLaCAAAATQmVpdfUQDAAAOgABAAEAAQABhgKwkBSXAAGgAAAAErliZAGAArAwEyEAAaAwAAAQ+HJ21wAAAIWgMAAAEGygMAAAE3kQARM6MAAAAQAAAAAACgAQAAAA+wj8CP0ILQkuCR8MhxCOEEgRSRH9EmOAA7AQCO8AAWRtdA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/4143e8d28ee3.js b/pr/672/docs/search.index/4143e8d28ee3.js new file mode 100644 index 000000000..b31e62681 --- /dev/null +++ b/pr/672/docs/search.index/4143e8d28ee3.js @@ -0,0 +1 @@ +rn_("AQABsIAT6wABdAEUCQAAAQkA880SAAABAAEA84ACY2cBgwKgkAAAFDagkAAAFDcyMwGAAqBQAAAUYqCAAAATg2Nl8wACc3TyQAcAAAEAAQABoLAAABPOYvNYEwAALAEBACOAAqAAAAATV2RyI4ACoFAAABQoY2XzgAJpeQEGAPU/EQAAAQABAAEAAQABAQGwQBRmAAFl9SoUAAABAAEAAQABAPcAAICFoBAAAAwPKWAA") \ No newline at end of file diff --git a/pr/672/docs/search.index/4177e23c85bd.js b/pr/672/docs/search.index/4177e23c85bd.js new file mode 100644 index 000000000..228db7e24 --- /dev/null +++ b/pr/672/docs/search.index/4177e23c85bd.js @@ -0,0 +1 @@ +rn_("AYACsHAS2gABoLAAABQZZW8jAQOwcBIAAAGAE4AAEPhtbnM7MAAAAQAAGgAGAGoHAQDxCAUACA0BAE4RCQCMEQAAYBMFAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/4239d228a482.js b/pr/672/docs/search.index/4239d228a482.js new file mode 100644 index 000000000..73cc6b65c --- /dev/null +++ b/pr/672/docs/search.index/4239d228a482.js @@ -0,0 +1 @@ +rn_("AYACoCAAAA/PoDAAABPaYXLzAAF0AX0HAAABBAGgQAAAFGVl8ocUAAABAAEFADswAAABAAALAAUAWRAFANASAQABFAEAWBQAAHcUAADjAISgMAAAD82FAAI7MAAAAQAACwAFAKYFAgC8BgQAVwwBAIsRAADiEgAA") \ No newline at end of file diff --git a/pr/672/docs/search.index/426c45f94d9c.js b/pr/672/docs/search.index/426c45f94d9c.js new file mode 100644 index 000000000..d65d5c4d2 --- /dev/null +++ b/pr/672/docs/search.index/426c45f94d9c.js @@ -0,0 +1 @@ +rn_("AYkCsCAUcQABoDAAABR7aXATAAOgIAAAEy2ggAAAE0JlaXX1EAwAADoAAQABAAEAAYYCsJAUlwABoAAAABK5YmQBgAKwMBMhAAGgMAAAEPhydtcAAAKFoDAAABBsoDAAABN5EAETOjAAAAEAAAAAAAkAEAAAAPsI/Aj9CC0JLgmHEI4QSBFJEf0SAQMBsDATPQABc/PKEAAAAQABAAEAADswAAABAAAKAAMAchECAL4TAwCmFAMAlwEAAIWgIAAABbGwIAf/AAFQGAH1egwAAAEAAQABAAEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/4372412479ef.js b/pr/672/docs/search.index/4372412479ef.js new file mode 100644 index 000000000..d9c6079f3 --- /dev/null +++ b/pr/672/docs/search.index/4372412479ef.js @@ -0,0 +1 @@ +rn_("AQcA9qITAAABAKoAVwAYAAEA8wEBaQFbCQAAAQAAOzAAAAEAAAUAAQBeCQUApwAAgMVWC9Eo7+ywYBQHAAGgEAAACTIAAiCK") \ No newline at end of file diff --git a/pr/672/docs/search.index/43819ce322e0.js b/pr/672/docs/search.index/43819ce322e0.js new file mode 100644 index 000000000..6b63db2e2 --- /dev/null +++ b/pr/672/docs/search.index/43819ce322e0.js @@ -0,0 +1 @@ +rn_("AYQCoCAAABEnoDAAABGNaXQBAQA7MAAAAQAAjQACAHMHAAB1CYwAYwADsAACGgABZGVsOjAAAAEAAAAAAAcAEAAAALcEhQWGBYcFdwdEEZkTmhM=") \ No newline at end of file diff --git a/pr/672/docs/search.index/4471911fea22.js b/pr/672/docs/search.index/4471911fea22.js new file mode 100644 index 000000000..172aa1c1a --- /dev/null +++ b/pr/672/docs/search.index/4471911fea22.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAYAAQBfEAYAAQMAOzAAAAEAAAUAAQDXDAUAYwADoJAAABN3aHBzOjAAAAEAAAAAAA8AEAAAAKkFqgXBBhIJWQxmDAYRBxG2ErcSDhMPExATERM/E0AT") \ No newline at end of file diff --git a/pr/672/docs/search.index/45ccc7ad5291.js b/pr/672/docs/search.index/45ccc7ad5291.js new file mode 100644 index 000000000..595cb8328 --- /dev/null +++ b/pr/672/docs/search.index/45ccc7ad5291.js @@ -0,0 +1 @@ +rn_("AQABoCAAAAzrbQGEBQAAAQAA9A4TAAABAAEAAQDzBAFk9LESAAABAAEAAQAjhISwoBRWAAGwQBMMAAGwcBP/AAEVgAAzgAOw0BRAAAFhZWk=") \ No newline at end of file diff --git a/pr/672/docs/search.index/45d9dd3f98fe.js b/pr/672/docs/search.index/45d9dd3f98fe.js new file mode 100644 index 000000000..6df7f5498 --- /dev/null +++ b/pr/672/docs/search.index/45d9dd3f98fe.js @@ -0,0 +1 @@ +rn_("AQcAOzAAAAEAAI0AAgD3EAAACBKMAAEDADswAAABAACMAAEACA6MAAECADswAAABAAAHAAEAQgwHAPMAA2JpdDswAAABAACNAAIAoQQAANsEjAA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/46364023d7e3.js b/pr/672/docs/search.index/46364023d7e3.js new file mode 100644 index 000000000..01eaf28df --- /dev/null +++ b/pr/672/docs/search.index/46364023d7e3.js @@ -0,0 +1 @@ +rn_("AYACsHASmAABsCAUsAABaW8BAAGw8BSwAAFrOzAAAAEAAAgAAwAZFAAAHhQGAEgUAADzBQFk8toSAAABAAECAPPcEAAAAQABAPMAAWk6MAAAAQAAAAAABwAQAAAAthDvEt4T3xMOFA8ULxQwFCODArCwFIwAAWFzAYECoKAAABNHoEAAABB0b3bzgIQBUAE=") \ No newline at end of file diff --git a/pr/672/docs/search.index/482faea55872.js b/pr/672/docs/search.index/482faea55872.js new file mode 100644 index 000000000..93e875f8a --- /dev/null +++ b/pr/672/docs/search.index/482faea55872.js @@ -0,0 +1 @@ +rn_("AQQBoEAAABGDZQE7EAAAAQgA9MYRAAABAAEAAQABAwGgQAAAEYJlAcYPAAABAgDzLwwAAAEAAQAjAAKwIAwdAAFrcvbwBgAAAQAUCu0AAQA0AvOAAnJ4AQMBsIAUXwABdjswAAABAAALAAMAFhEBANERBQDCEwMAI4ACsGAQhwDBYXIBgAKgsAAAE+qwMA/EAAFlaAEDAaBAAAARFWXyFQkAAAEA1xMAAIugIAAAB2awsBM5AAGgkAAAEvyggAAAEcqgAAAAE0afGEw6MAAAAQAAAAAADAAQAAAADAJ8BH0EfgQGDAcMcAxxDHIMBg4HDkcTXRM=") \ No newline at end of file diff --git a/pr/672/docs/search.index/487bb0c57643.js b/pr/672/docs/search.index/487bb0c57643.js new file mode 100644 index 000000000..f7950ea6b --- /dev/null +++ b/pr/672/docs/search.index/487bb0c57643.js @@ -0,0 +1 @@ +rn_("AQYA9OsTAAABAFQAAQAjgAKhEAAAFKVjchOAAqAgAAAJC2h5AQEBoGAAABSWc/LbEQAAAQAjgAKwIAlxAAFldQGAArAgECMAAaCQAAAUR2FlAQcBsEAUZgABZfUqFAAAAQABAAEAAQABAAD0KRMAAAEAWAEBAPMEAXM7MAAAAQAADQACAKYTBwCbFAUARwAABIaggAAAE4axABSqAAGwQBLjAAGwgBS6AAGwUBMrAAEVABb0lg4AAAEAAQABAIMAhLBAFLAAAbAwExoAAbAwE7oAAcAAAzowAAABAAAAAAAJABAAAAC6BBEREhG0EbURwhHDEQkUChR6FAEDAbBAFHwAAXTy2xMAAAEAI4QCoEAAABQLYnMTgAKwwBSuAAF1eQGAAqAAAAAJK7AgEQMAAWdwAQEA9NcRAAABAAEAAQDzgAJkbLfoAACMoNAAABOSoFAAABFwoJAAABOusFATCQABoDAAAAxrt5gHOzAAAAEAAGgAFQADAgEACQIBAGgEAwDFBQAAwgYBAGEHAQB0BwAABwkBABQJAAAeCQAAUAkAAIQMQwDeDAAA7g8BABgQAADIEAEA5hABACsRAQCrEQQAvBIBADcTAAA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/48dd45a6efaf.js b/pr/672/docs/search.index/48dd45a6efaf.js new file mode 100644 index 000000000..0ff833614 --- /dev/null +++ b/pr/672/docs/search.index/48dd45a6efaf.js @@ -0,0 +1 @@ +rn_("AQIA9LARAAABAAEAAQABAQDzzhEAAAEAAQATgAJm0DF3AghlcwEKAPTCEwAAAQABAAEAAYACsCAUPgABoGAAABOPb3YBgAKwMBLFAAGgAAAAE0djZPOAhQUQBg==") \ No newline at end of file diff --git a/pr/672/docs/search.index/48ff0a8ecdcf.js b/pr/672/docs/search.index/48ff0a8ecdcf.js new file mode 100644 index 000000000..58d64ebf7 --- /dev/null +++ b/pr/672/docs/search.index/48ff0a8ecdcf.js @@ -0,0 +1 @@ +rn_("AQEA8/kPAAABAAEAAYmEoIAAABS1oEAAABRhoGAAABSBoJAAABS5AoAKE4EDoKAAABPxsNAUWgABY2l1M4ADsMAUtwABYXR1") \ No newline at end of file diff --git a/pr/672/docs/search.index/4920da678e00.js b/pr/672/docs/search.index/4920da678e00.js new file mode 100644 index 000000000..e90ae7a9a --- /dev/null +++ b/pr/672/docs/search.index/4920da678e00.js @@ -0,0 +1 @@ +rn_("AQgA9OsTAAABAFQAAQABAgGgYAAAFJZz8tsRAAABAAGAArAgECMAAaCQAAAUR2FlAQcBsEAUZgABZfUqFAAAAQABAAEAAQABAwGwQBR8AAF08tsTAAABACOEAqBAAAAUC2JzE4ACsMAUrgABdXkBAQD01xEAAAEAAQABABOAArAwEQMAAWRsN3QAAIuwQBO6AAGgUAAAEXCgkAAAE66wUBMJAAGgMAAADGuzmAc7MAAAAQAAZQATAAkCAQBoBAMAxQUAAMIGAQBhBwEABwkBABQJAAAeCQAAUAkAAIQMQwDeDAAA7g8BABgQAADIEAEA5hABACsRAQCrEQQAvBIBADcTAAA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/4986f6b50c9a.js b/pr/672/docs/search.index/4986f6b50c9a.js new file mode 100644 index 000000000..37629fec4 --- /dev/null +++ b/pr/672/docs/search.index/4986f6b50c9a.js @@ -0,0 +1 @@ +rn_("AQABsGAUDgABZQFHFAAAAQYA9DkTAAABAE8BAQABBAD0UxMAAAEADwEBAPMAA2VyczowAAABAAAAAAAJABAAAAAjCSQJJQlFEYQRmBKZEqYSBxQIFA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/4a3a9fa5310b.js b/pr/672/docs/search.index/4a3a9fa5310b.js new file mode 100644 index 000000000..042e5251a --- /dev/null +++ b/pr/672/docs/search.index/4a3a9fa5310b.js @@ -0,0 +1 @@ +rn_("AYACoAAAABCVoAAAABCWNDYBgAOwABPmAAGwABPoAAGwcBRfAAE0NmEBBAD0sxQAAAEACgABACOBAqBAAAARXm1wAQEAOzAAAAEAAAgAAwDWEgAATBMAAMAUBgDHAQACxaBAAAAQWKCAAAASsEAGgAg7MAAAAQAAFgAIAPUPAQAWEQEAWhEDANERBQDCEwMAERQAAG8UAQCiFAEA8wACZXY6MAAAAQAAAAAAFQAQAAAAEwIUAhUCsAUCDa8PsA/ND+sPfxCAEIEQghAwETERxxLIEskSyhLIEzIUMxQBgQKwcBFaAAGwcBFcAAE0NiOAArAQB2EAAW5wAQMCsEATIQABoDAAABN5ZXf0hxAAAAcAugABABOBAqBwAAATs2dvAQQAOzAAAAEAAAYAAgAEDAAAEA0FAGOAA7BQD/UAAWRzdFcBAACFoBAAABBYsSAUbwABESEBOzAAAAEAAAoAAwB+AAAAlAAFANcRAwA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/4ba0a8bbc176.js b/pr/672/docs/search.index/4ba0a8bbc176.js new file mode 100644 index 000000000..afbe3a8af --- /dev/null +++ b/pr/672/docs/search.index/4ba0a8bbc176.js @@ -0,0 +1 @@ +rn_("AQIAOjAAAAEAAAAAAAgAEAAAABoMGwypD6oP6g/bEAARkBOREwEAAPQ3CQAAAQABAAEAAQUBsDATGgABaPMJFAAAAQBwABODArAwEM0AAWNwAQMA9OgPAAABAPAAAQABgQKgAAAAEYmgEAAAD8dldfOAAmFyAQAA9L4RAAABAAEAAQABgAKgkAAAE/OwUBKkAAFid/OAAmR0E4ACsJATMwABbnLzAAJldPTEEAAAAQABAAEAAQEA9XoMAAABAAEAAQABABOAArCwE+8AAWFvIwACsEAM/AABZXQ7MAAAAQAACQAEAL8EAQCCBQEAXAwBALARAwABAgA7MAAAAQAACQACAOMHBwAUEwEAAYACsJATfgABoKAAABPyZXYTgAKxgBTCAAFjbQGCArBAFBwAAaBQAAAR7GJmE4ACoEAAABRHZHMjgAKg0AAAFJZjbvOAhBEBQAEFAbBAE1kAAWUBoxIAAEOCA6EAAAAUpKDQAAAUeGttcxOAArBgEOQAAWFyAQUAOzAAAAEAAI0AAgD3EAAACBKMAAGCA7AgExYAAbAgEH0AAbBAEXkAAWtsc1OAA7AgFHwAAWt0dQGJhKCAAAAUtaBAAAAUYaBgAAAUgaCQAAAUuQKAChOCA6CgAAAT8bDQFFoAAWNpdQEBAPS6EQAAAQABAAEAQ4ADsXAUwAABoNAAABQwY29zI4ACsGAQ4gABaW8BAAD0JAwAAAEAUwABACOBAqDgAAAUTWV5AQ0AOjAAAAEAAAAAAAcAEAAAAFETUhPtE+4TJRQmFHUUdhQBgQKwQBNxAAGgwAAAFC9lbwECADowAAABAAAAAAAIABAAAADfD+APkhHwEvES8RN4FKwUrRTzAQFvOzAAAAEAAAkABABcCQEAnxADAOIQAQDvEwEA84ADbW5yAQAAOzAAAAEAAAYAAgCOEwAA9hMFAEOAA7BAFGwAAaAgAAAM+2VycyMAArAwE9IAAWNy8rwTAAABAAGBArCAExwAAaEAAAAUd2NmAQsA9HoTAAABAAEAAQABgAKgAAAAEJWgAAAAEJY0NvMCAXYBAg0AAPMAAmRsARcJAADX/jkAkrCgFBoAAbBwEd4AAbCAEvoAAaCwAAATNZ3dfzswAAABAAAXAAsAqwEBALsEAwB/BQIA+wYBAAUHAgA1DAAA9w8AAF8RAADOEQIAqRICAF4UAAA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/4c3658ed6e5f.js b/pr/672/docs/search.index/4c3658ed6e5f.js new file mode 100644 index 000000000..b1aef39d0 --- /dev/null +++ b/pr/672/docs/search.index/4c3658ed6e5f.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAUAAQCQFAUA8w0BbTswAAABAAAFAAEAThQFACMDA7BAEsMAAbAAE4cAiWVmczswAAABAAAYAAYAMxEHAOcSAQBmEwQAQhQEAEkUAwC2FAAA") \ No newline at end of file diff --git a/pr/672/docs/search.index/4c4e5cf71a95.js b/pr/672/docs/search.index/4c4e5cf71a95.js new file mode 100644 index 000000000..c60e882da --- /dev/null +++ b/pr/672/docs/search.index/4c4e5cf71a95.js @@ -0,0 +1 @@ +rn_("AQgA834UAAABADoAI4ACsAAQ3wABbW4BgAOgQAAAE12wIBQaAAGgIAAAD9NoaXkBCQGggAAAFLZmAZgTAAABAwGwABQxAFBz8q0QAAAtA0OAA7AgD88BK7BQErwAAWFvcgECAPXcEAAAAQABAF8CAQDzAANpdHU6MAAAAQAAAAAAEQAQAAAAfQcsCfMMORC2EOER7xIpEyoT3hPfE+UTDhQPFC8UMBSCFIMUAQYA9BITAAABAJcBAQATAgKwYBMUAAFmbfLiDAAAAQAjggKwQBNDAAFlbAGAAqAwAAAUYbCwE7QAAWFsAQYA8+EPAADhAAEAAQUA9AUUAAABAE4AAQDzAgFj8gcTAAABAAECAaAwAAAQd2EBVgwAAAGAArBQESsAAbAgD6sAAWNu84ACbnQBBQA7MAAAAQAADgACAOgMAAAKEA0AAQIAOzAAAAEAAAcAAQBTCQcA5wUAAAmgMAAADFWgsAAAFC+ggAAAEaqw0BQ+AAEwYWZpa21ydXY7MAAAAQAAagIFAAACAABtA+0AdgcAAAIL7QAIDowAAQEA8/kPAAABAAEAE4ACsMAUtwABdHUBAwDzwBIAAAEAAQABAQD1PxEAAAEAAQABAAEAAQEBsDAP6gDxdPZoBwAAAQCKAPoHqwD9AWOAA7CQFAcAAWFtdAEDAaBAAAAUZWXyhxQAAAEAE4ACsGAUugABZXIBAwGgAAAAE+VzAfkQAAABAgA6MAAAAQAAAAAACgAQAAAAUQlSCSIQcRNyE6IToxNNFKQUvBS9FIMABKCAAAASmrBwEW4AAbAgEQ8A8GZucnYB8gcAAAGBAqAAAAAJLKAwAAAP03N0xwEAgMawABQ8AAGwkBKYAAGgEAAAB04AEogoAQIAOzAAAAEAAA0ABgBZEAUAaxEBANASAQABFAEAWBQAAHcUAAATggKw8BS6AAFmZwGAArCAFI4AAaBgAAAUtWRzE4ACsMATzAABaW8BCAD1QhQAAAEAAQABAAEA8wIBZvWaEAAAAQABAAEAAQAjgAOgIAAAD82woBSMAAFkcnT3+wcAj6AwAAAPw5fXDzswAAABAABSAR4AhwQBALQEAgB8BQIApgUCAMcFAQC8BgQAPwcAAFcHAADuBwEAAgkCADAJAQApDAMAVwwBAH8MAwDkDAEAnQ4EAfAPAQBvEAIAtxABAMoQAgD4EAEAixEAAL4RAwDoEQEAohIAAOISAAAaEwEAhBMBAKYTBwCbFAUA") \ No newline at end of file diff --git a/pr/672/docs/search.index/4c6ccd5ef6f9.js b/pr/672/docs/search.index/4c6ccd5ef6f9.js new file mode 100644 index 000000000..54cc71c63 --- /dev/null +++ b/pr/672/docs/search.index/4c6ccd5ef6f9.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAAUAAQDcEgUAAQQBoFAAABQnaPK6EgAAAQABAAKwQBO6AAGgoAAAFEdlcjowAAABAAAAAAAHABAAAADCBsMGYQdiB+4P7w/mEOcQ84ADZ2lz") \ No newline at end of file diff --git a/pr/672/docs/search.index/4ce1093f624a.js b/pr/672/docs/search.index/4ce1093f624a.js new file mode 100644 index 000000000..79a301c4c --- /dev/null +++ b/pr/672/docs/search.index/4ce1093f624a.js @@ -0,0 +1 @@ +rn_("AQAA9CkTAAABAFgBAQDzBAFzOzAAAAEAAA0AAgCmEwcAmxQFAEcAAAWGoIAAABOGsQAUqgABsEAS4wABsIAUugABsFATKwABFQAW9JYOAAABAAEAAQBDAISgIAAACSug0AAAE5KgMAAACQsVAATzAwIAAAEAcAU=") \ No newline at end of file diff --git a/pr/672/docs/search.index/4d1a3a5d7a12.js b/pr/672/docs/search.index/4d1a3a5d7a12.js new file mode 100644 index 000000000..3df37a87b --- /dev/null +++ b/pr/672/docs/search.index/4d1a3a5d7a12.js @@ -0,0 +1 @@ +rn_("AQACoDAAABEcoJAAABOTMGj1AhIAAJkBAQABAAEAE4ACoDAAAA/MZHIjAISwEAj3A0CwIAwDAQygMAAAD/QSABj1nAUAAC0AAQABAAEAAQIA9LMUAAABAAoAAQABAgDzbxAAAAEAAQDzgAJpcgEBArBAEWEAAaBQAAATeGVz85UOAADiAgEAAQsA9qITAAABAKoAVwAYAAEA84ACbHTTgISgkAAAE1ABRAQ=") \ No newline at end of file diff --git a/pr/672/docs/search.index/4db7cfff3d56.js b/pr/672/docs/search.index/4db7cfff3d56.js new file mode 100644 index 000000000..cad2b8da7 --- /dev/null +++ b/pr/672/docs/search.index/4db7cfff3d56.js @@ -0,0 +1 @@ +rn_("AQADoKAAABRZoCAAABOSsDAR6gABZWl08vYSAAABAAEDADswAAABAAAGAAEAZBEGAAEIAPT8EwAAAQCJAAUAEwEDsJAUYwABsFATQQFkZ2xwOzAAAAEAABIACQBpEAIA7REBAC4TAQBREwEAiBMBAO0TAQAlFAEANBQBAHUUAQABgAKwMBHmAAGwMBHoAAFydgECAbAAErYAAWQ7MAAAAQAABQACAOIRAwDlEgEAI4ECoJAAABRYaW8BBwD0dBQAAAwAIQARAHcLAAGIsFASuAEXsGATPwABlQkDOjAAAAEAAAAAAA4AEAAAAN8M4AzhDJsOnA7QENEQDBEvEY4RjxGQEZERNRNwEyOAAqAQAAAM3m5v") \ No newline at end of file diff --git a/pr/672/docs/search.index/4dd22e56451b.js b/pr/672/docs/search.index/4dd22e56451b.js new file mode 100644 index 000000000..14c67a960 --- /dev/null +++ b/pr/672/docs/search.index/4dd22e56451b.js @@ -0,0 +1 @@ +rn_("AQIBsIAT6wABdAEUCQAAAYECoBAAABNHoHAAABOPYWPzAQFy8qoRAAAgAPOAAmVpAQAA9BIMAAABAAEAAQDzgAJkcvMAAWX0jwAAAGEMTgcBAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/4ede297cd13d.js b/pr/672/docs/search.index/4ede297cd13d.js new file mode 100644 index 000000000..3f5d58e82 --- /dev/null +++ b/pr/672/docs/search.index/4ede297cd13d.js @@ -0,0 +1 @@ +rn_("AQABoJAAABN3aPI/EwAAAQDzAgFk8hMHAAABAAECADswAAABAAAFAAEAkBQFAPMLAW07MAAAAQAABQABAE4UBQAjgAKwIAzsAAFhbwECAPP+CAAAUAMBAAGKArAAE+YAAbAAE+gAATQ2I4ECoDAAAAx3ZXIBgAKwQBH0AAFO8FYRjlhjdbcGAACHsBAQUwABsFAUagABEVEJ9Z0EAAAKAMYAAQCeBw==") \ No newline at end of file diff --git a/pr/672/docs/search.index/4ef056118e58.js b/pr/672/docs/search.index/4ef056118e58.js new file mode 100644 index 000000000..b37c171ce --- /dev/null +++ b/pr/672/docs/search.index/4ef056118e58.js @@ -0,0 +1 @@ +rn_("AYMCsQAUvAABsNAUrAABa20BCwDzARQAAAEAdQAzAoSgYAAAEpWhAAAAFHkEgAM7MAAAAQAANAACAE8HAACgBzMA") \ No newline at end of file diff --git a/pr/672/docs/search.index/4f020f79d021.js b/pr/672/docs/search.index/4f020f79d021.js new file mode 100644 index 000000000..27976a6a5 --- /dev/null +++ b/pr/672/docs/search.index/4f020f79d021.js @@ -0,0 +1 @@ +rn_("AQACoDAAABEcoJAAABOTMGj1AhIAAJkBAQABAAEAE4ACoDAAAA/MZHIjAYSwEAj3A0CwIAwDAQygMAAAD/QSABj1nAUAAC0AAQABAAEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/4f7d52082fe6.js b/pr/672/docs/search.index/4f7d52082fe6.js new file mode 100644 index 000000000..3fb1c806b --- /dev/null +++ b/pr/672/docs/search.index/4f7d52082fe6.js @@ -0,0 +1 @@ +rn_("AQEA9UoJAAABAAEAAQABAPMAAWnysBQAAAEAAQQCoCAAABHdoDAAABKoYmgByg8AAKcAAIDFoBAAAAzusBAUmQABoDAAABB8BBAoAQ==") \ No newline at end of file diff --git a/pr/672/docs/search.index/504ca52d3ae9.js b/pr/672/docs/search.index/504ca52d3ae9.js new file mode 100644 index 000000000..d45566c4f --- /dev/null +++ b/pr/672/docs/search.index/504ca52d3ae9.js @@ -0,0 +1 @@ +rn_("AQcA9AMSAAABAAEAAQAjAQKgcAAAEgJhcgF4BwAAAQMAOzAAAAEAAAgAAgCiDwYAXhMBAPMAAmN0OzAAAAEAAFcAJwCoBAsAbwUAAA4JAQAnDAEALwwCAO8MAAD7DAIADQ0BAMwPAADRDwEA1A8BAN4PAABmEAIAbRABAHIQAQCXEAAADxEAABURAABhEQEAbhEBAIIRAQCGEQIA5hEBAPkRAgD/EQAAtRIAAL4SAQDDEgEADBMBABwTAQAhEwEAQxMBAFMTAwBZEwEAuhMBAM4TAAA6FAAAYxQEAGwUAgA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/5106fdea4a6c.js b/pr/672/docs/search.index/5106fdea4a6c.js new file mode 100644 index 000000000..0c5af044d --- /dev/null +++ b/pr/672/docs/search.index/5106fdea4a6c.js @@ -0,0 +1 @@ +rn_("AQUA9AUUAAABAE4AAQDzBAFj8gcTAAABAAEFAPPXEgAAAQABAPOCAmZ0AQABsAAUXwABZPT7CwAAAQABAAEA84ACZWkTgAKw4BRqAAF0dg==") \ No newline at end of file diff --git a/pr/672/docs/search.index/5168b1c9fd9a.js b/pr/672/docs/search.index/5168b1c9fd9a.js new file mode 100644 index 000000000..72cf073d3 --- /dev/null +++ b/pr/672/docs/search.index/5168b1c9fd9a.js @@ -0,0 +1 @@ +rn_("AQEA9yIRAAABAAEA2AIBAIkABQABAgD0shAAAAEAAQABABOAAqDQAAAUc2Nz84ACZW8jgQKwAAdSAAFsdCOAA7AABbMAAbCgFAkAAWRydA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/51a383abcfdb.js b/pr/672/docs/search.index/51a383abcfdb.js new file mode 100644 index 000000000..a126387c5 --- /dev/null +++ b/pr/672/docs/search.index/51a383abcfdb.js @@ -0,0 +1 @@ +rn_("AQEAOjAAAAEAAAAAAAgAEAAAAAkJdxAiESMRJBH8E/0ThhSLFAECAPSyEAAAAQABAAEAE4ACoNAAABRzY3NTgAOgcAAAFG5laW+DgISwIBOgAAGgIAAAD+uwAAdSAAEKCAQ=") \ No newline at end of file diff --git a/pr/672/docs/search.index/51d0879dcf6d.js b/pr/672/docs/search.index/51d0879dcf6d.js new file mode 100644 index 000000000..fded95d74 --- /dev/null +++ b/pr/672/docs/search.index/51d0879dcf6d.js @@ -0,0 +1 @@ +rn_("AYACsFAUFwABoAAAAAkKY3QBAQD0lBMAAAEAAQABAAEDAPTvDAAA3waeAAEAAYACoBAAAAx3oFAAABKiY2QBAgDz/ggAAFADAQDzAAFt86cEAADGAAEAAYECoBAAABNHoHAAABOPYWPzAAFy81oHAABQCiAA5wcAgIewMA/QANMpICM=") \ No newline at end of file diff --git a/pr/672/docs/search.index/5219aed485bd.js b/pr/672/docs/search.index/5219aed485bd.js new file mode 100644 index 000000000..a830de700 --- /dev/null +++ b/pr/672/docs/search.index/5219aed485bd.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgC2EQMAwBICAAEKAbBAFGYAAWX1KhQAAAEAAQABAAEA8wACaXP0JAwAAAEAUwABACOBAqDgAAAUTWV5") \ No newline at end of file diff --git a/pr/672/docs/search.index/52ddf1446ab6.js b/pr/672/docs/search.index/52ddf1446ab6.js new file mode 100644 index 000000000..e82176981 --- /dev/null +++ b/pr/672/docs/search.index/52ddf1446ab6.js @@ -0,0 +1 @@ +rn_("AQUA9MsSAAABAKkAAQABCAD0ihMAAAEAhwABADMAA7BAE1kAAWFiZTowAAABAAAAAAAMABAAAAAxBzIHMwf0DPUMxw86EIgQiRCKEIsQoxJ4Ew==") \ No newline at end of file diff --git a/pr/672/docs/search.index/536af0dd1eb6.js b/pr/672/docs/search.index/536af0dd1eb6.js new file mode 100644 index 000000000..019da9b99 --- /dev/null +++ b/pr/672/docs/search.index/536af0dd1eb6.js @@ -0,0 +1 @@ +rn_("AYACsAAT5gABsAAT6AABNDYBBgD0sxQAAAEACgABAPMCAnJ28/UPAAABABsE8wIBZTswAAABAAAFAAIAfxADADARAQA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/53a370bed26e.js b/pr/672/docs/search.index/53a370bed26e.js new file mode 100644 index 000000000..dbe01db6c --- /dev/null +++ b/pr/672/docs/search.index/53a370bed26e.js @@ -0,0 +1 @@ +rn_("AQIA9asRAAABAAEAAQABAAGAArBgExYAAaAgAAARAG95AQABoMAAABQ7afRvEwAACAAcABsAAQYA9KYUAAABAAEAAQABAgD0/hAAAAEAmgABAAECAaBAAAATRnI7MAAAAQAABwACAPwQAQCTEQUA84ACYW9nAwAAhrCAE0UAWrCgE7wAAQwwBjswAAABAAAIAAMAHAwAAMYRAwCsEgMA84ACZWk=") \ No newline at end of file diff --git a/pr/672/docs/search.index/53a9362a0d4b.js b/pr/672/docs/search.index/53a9362a0d4b.js new file mode 100644 index 000000000..6697bff59 --- /dev/null +++ b/pr/672/docs/search.index/53a9362a0d4b.js @@ -0,0 +1 @@ +rn_("AQIBoDAAABB3YQFWDAAAAYECoCAAABD6sFASvAABYW8BAwGgAAAAE+VzAfkQAAAjgAKgAAAAB/JydmMAA7BQEWsAAWNlczswAAABAAAIAAQAAgkCADAJAQBvEAIAohIAAAGAArDQFK4AAaCQAAATrmJoJwEAgIWwUBErAAGw8BPGAAGwIA+rAAFEIQTzgAJudA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/53ffd91bdfe9.js b/pr/672/docs/search.index/53ffd91bdfe9.js new file mode 100644 index 000000000..4561b5d6d --- /dev/null +++ b/pr/672/docs/search.index/53ffd91bdfe9.js @@ -0,0 +1 @@ +rn_("AQMA9BAJAAABAFIDAQABAwDzYAwAAAEAAQDzgAJpbwEBAbBQEQ0AAWP0XQcAAAEAAQAIAiOBAqBQAAAQz2NuAQQAOzAAAAEAAAcAAwDLEQIAfhMBABQUAgABAQA6MAAAAQAAAAAABwAQAAAA7RHuES4TLxOIE4kTNBQ1FPMJAWkBDBEAANcCAACHsMATcwABoFAAAA+1oRAAABReFAEXOzAAAAEAACMAAQC5ASMA") \ No newline at end of file diff --git a/pr/672/docs/search.index/5450ecaeec3d.js b/pr/672/docs/search.index/5450ecaeec3d.js new file mode 100644 index 000000000..f7807e0d2 --- /dev/null +++ b/pr/672/docs/search.index/5450ecaeec3d.js @@ -0,0 +1 @@ +rn_("AQMBgBOAABD4czswAAABAAAFAAEAYBMFACOCAqCQAAAUYmlzBwEAhMWwUBMnAAGgoAAAE8uwcBOQAAGg0AAAFG6AARgI") \ No newline at end of file diff --git a/pr/672/docs/search.index/5525682f4073.js b/pr/672/docs/search.index/5525682f4073.js new file mode 100644 index 000000000..e064d1550 --- /dev/null +++ b/pr/672/docs/search.index/5525682f4073.js @@ -0,0 +1 @@ +rn_("AQEA9GcHAAB6COEAAQABBQD0SRQAAAEAAQABAAEFAPQ7EwAAAQCUAAEA8wICY3X1VhAAAAEAxgABAAEAYwAEoDAAAAw2oHAAABHhZW5zdvN0EAAAnwAjAg==") \ No newline at end of file diff --git a/pr/672/docs/search.index/5540c5ab7cb3.js b/pr/672/docs/search.index/5540c5ab7cb3.js new file mode 100644 index 000000000..9d85d2059 --- /dev/null +++ b/pr/672/docs/search.index/5540c5ab7cb3.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAABYACQBZEAUAaxEBAPwRAgDQEgEAAhMEAAEUAQApFAAAWBQAAHcUAAATgAKw8BS6AAFmZyOAArBQE7wAAW1uAQUA8wEUAAABAHUA8wQBYwGLEQAAAQEBsHAUXAABcDswAAABAAAJAAIA1QcHANIQAQABAgA7MAAAAQAACwACANQTBQCQFAUA8wEBbTswAAABAAANAAQA0AwAAG0RAADpEgUAThQFABMAAqAwAAAM1GVz8gASAAABAAGCArBwFDgAAaBAAAARGGlz9wEAgIawkBQaAAGQSQg=") \ No newline at end of file diff --git a/pr/672/docs/search.index/5560d878572f.js b/pr/672/docs/search.index/5560d878572f.js new file mode 100644 index 000000000..91f368f67 --- /dev/null +++ b/pr/672/docs/search.index/5560d878572f.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAO4AAgAMDAAAGA3tAPMAAW07MAAAAQAA7gACAKMFAADNBe0AAQMAOzAAAAEAAAgAAgCiDwYAXhMBAPMAAWM7MAAAAQAAVwAnAKgECwBvBQAADgkBACcMAQAvDAIA7wwAAPsMAgANDQEAzA8AANEPAQDUDwEA3g8AAGYQAgBtEAEAchABAJcQAAAPEQAAFREAAGERAQBuEQEAghEBAIYRAgDmEQEA+RECAP8RAAC1EgAAvhIBAMMSAQAMEwEAHBMBACETAQBDEwEAUxMDAFkTAQC6EwEAzhMAADoUAABjFAQAbBQCAPOAAnJ3AQkA834UAAABADoAAYACoEAAABNdsCAUGgABaGkBAQDz+Q8AAAEAAQATgAKwwBS3AAF0dQGBArCAFI4AAaBgAAAUtWRzNwMAgYagQAAAFGGwYBQxAFACkA4BiAKwABPmAAGwABPoAAE0NjOAA6AwAAARhWRuc2MAA6AQAAAEy2FlbwGGAAAA") \ No newline at end of file diff --git a/pr/672/docs/search.index/560bd128efec.js b/pr/672/docs/search.index/560bd128efec.js new file mode 100644 index 000000000..7c67f1e71 --- /dev/null +++ b/pr/672/docs/search.index/560bd128efec.js @@ -0,0 +1 @@ +rn_("AQIA9LARAAABAAEAAQABCgD0whMAAAEAAQABAPOBAm10AQYA83IRAAABAAEAAYICsGATSAABsNAUaAABaXITgAKwEAzpAAFlaQEFAPQFFAAAAQBOAAEA8wQBY/IHEwAAAQABBQDz1xIAAAEAAQDzggJmdAEAAbAAFF8AAWT0+wsAAAEAAQABAPOBAmVpNwYAAImxMBSHAAGgYAAAEXGgUAAAEQWwkBMYAAGwQBEBAAG1EAcBOxQAAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/5687f616548f.js b/pr/672/docs/search.index/5687f616548f.js new file mode 100644 index 000000000..f45875eea --- /dev/null +++ b/pr/672/docs/search.index/5687f616548f.js @@ -0,0 +1 @@ +rn_("AQEBsFAP1gABY/SjBAAAAQABAMcAAQABoIAAABS2ZgGYEwAA8wYBc/IEDQAAlwQzAAOgYAAAFJZjaXPy2xEAAAEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/57030fb90cf6.js b/pr/672/docs/search.index/57030fb90cf6.js new file mode 100644 index 000000000..3be85cda5 --- /dev/null +++ b/pr/672/docs/search.index/57030fb90cf6.js @@ -0,0 +1 @@ +rn_("AQEA9DsHAAABAAEAAQABAQGgQAAAEYlz9TUMAAAqBUoBAQABABOCAqAgAAANDGVyI4ADoEAAAAw0sDAJJgABZW90AQAA9DcHAAABAAEAAQABAgD0pBMAAAEADwABABOAArBAFFwAAWJ284ACb3gBCgD14BMAAAEAAQB4AAEAAYECoCAAABD6sFASvAABYW8BBAGgAAAAE+VzAfkQAABjAwOwUBFrAAFjZXP2AgkAAAEAAQAsAAEAcQkBAQA7MAAAAQAABQABANcMBQAjgAKwMBNBAWRydPMAhgHAB/LxAQAA/AU=") \ No newline at end of file diff --git a/pr/672/docs/search.index/5718d5014774.js b/pr/672/docs/search.index/5718d5014774.js new file mode 100644 index 000000000..1faad6f1b --- /dev/null +++ b/pr/672/docs/search.index/5718d5014774.js @@ -0,0 +1 @@ +rn_("AQEA9qEFAAABACkNAQCpAAEAAQAAOzAAAAEAAAcAAwARAgEAaQwBAB4QAwABgwKwcBOZAAGwgBPSAAFjcwEFAPRJFAAAAQABAAEAAQUA9DsTAAABAJQAAQDzCAJjdfVWEAAAAQDGAAEAAQABAgD0vhEAAAEAAQABABMAAqAwAAARiWV09MQQAAABAAEAAQABAwD0uhEAAAEAAQABAAGAAqAAAAAQlaAAAAAQljQ28wMBdgECDQAAlwEAAIawoBMcAAGwIAwkAAGwgBL6AAEJDAY6MAAAAQAAAAAACQAQAAAABQcGBwcHNQz3D18RqRKqEqsSXhQBAgD0JRMAAAEAkgABAAEDAPRbEwAAAQCYAAEA8wACYnU6MAAAAQAAAAAAFQAQAAAASgdLB0wH2A/ZD4UQhhCmEKcQPRE+EfAR8REjEyQTMBMxE0ITbRNuE7YTtxMBAQD0nQUAAAEAUwcBALcAAICFsQAUQAABsAAHVAABDAhEAQgAOzAAAAEAAAUAAQDcEgUA1w0AAIiwQBEtAAGgIAAADQy5AAs7MAAAAQAADwAFAHwAAQD9BgcAYwcBAHUQAQApEQEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/5853623e6846.js b/pr/672/docs/search.index/5853623e6846.js new file mode 100644 index 000000000..c7c3a7bea --- /dev/null +++ b/pr/672/docs/search.index/5853623e6846.js @@ -0,0 +1 @@ +rn_("AQUA9IgQAAABAAEAAQABEwD1whQAAAEAAQABAAEAE4ACoMAAABNMYW8BAgKgsAAAFFigsAAAFFljc/PQEAAAAQBeAAGBArBQEfIAAaDAAAAUJ2hp84ACZWkjgAKwsBOMAAFpdAEFAbCAFF8AAXY7MAAAAQAABwACANERAwDCEwMAI4ACsHAR9gABYmR3AgAAhqCgAAATr6CQAAATeAXgAjswAAABAAAFAAIAFQcDAAEQAQA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/58beb8724380.js b/pr/672/docs/search.index/58beb8724380.js new file mode 100644 index 000000000..5124c57c3 --- /dev/null +++ b/pr/672/docs/search.index/58beb8724380.js @@ -0,0 +1 @@ +rn_("AQEBoGAAABSWc/LbEQAAAQABCAD1QhQAAAEAAQABAAEAowCEsCAJcQABoIAAABLvMEAIOzAAAAEAAAUAAgDyAQAAmhAEAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/58cbe3793786.js b/pr/672/docs/search.index/58cbe3793786.js new file mode 100644 index 000000000..fcc5143b3 --- /dev/null +++ b/pr/672/docs/search.index/58cbe3793786.js @@ -0,0 +1 @@ +rn_("AQACoCAAABHdoDAAABKoYmgByg8AAAETAbAAFIIAAXM7MAAAAQAABQABAJsUBQABBwDzqRIAAAEAAQDzgANkcnQ=") \ No newline at end of file diff --git a/pr/672/docs/search.index/59718cd9c46f.js b/pr/672/docs/search.index/59718cd9c46f.js new file mode 100644 index 000000000..e14b0f428 --- /dev/null +++ b/pr/672/docs/search.index/59718cd9c46f.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAYBAgDkDAEAnQ4EAQEBAaAAAAATTXP1xgQAAAgL+AMBAAQAAQECoGAAABPOoOAAABQpZXQ7MAAAAQAACAABAM0ECAABgAOwIBKWAAGwsBLQAAGwsBLSAAFiY3PzgARjbHR2") \ No newline at end of file diff --git a/pr/672/docs/search.index/59d3892a5c9c.js b/pr/672/docs/search.index/59d3892a5c9c.js new file mode 100644 index 000000000..ada6e4a9c --- /dev/null +++ b/pr/672/docs/search.index/59d3892a5c9c.js @@ -0,0 +1 @@ +rn_("AQQA9goNAAABAD8DAQAuAQEAAQoA89QSAAABAK0AAQEA9x8JAAABAF0HAQBzAAEAFQEjgAKwYBPvAAFpbwECAPTIDAAAAQBNBgEAAQUA9AUUAAABAE4AAQD3BAAAh6AgAAAMyrCQEfoAAQScAzowAAABAAAAAAAIABAAAACVBRAJEQljDGQMARECEQcTCBM=") \ No newline at end of file diff --git a/pr/672/docs/search.index/5b3910277d15.js b/pr/672/docs/search.index/5b3910277d15.js new file mode 100644 index 000000000..0078ce25f --- /dev/null +++ b/pr/672/docs/search.index/5b3910277d15.js @@ -0,0 +1 @@ +rn_("AQQA9K4QAAABAAEAAQABAAA6MAAAAQAAAAAACgAQAAAA5g/nD9cQ2BBjEfQR9RESExMTqhSrFPMBAXnyDBQAAAEAAQADoKAAABRZoCAAABOSsDAR6gABZWl08vYSAAABAAEDADswAAABAAAGAAEAZBEGAAEIAPT8EwAAAQCJAAUAEwEDsJAUYwABsFATQQFkZ2xwOzAAAAEAABIACQBpEAIA7REBAC4TAQBREwEAiBMBAO0TAQAlFAEANBQBAHUUAQABgAKwMBHmAAGwMBHoAAFydgECAbAAErYAAWQ7MAAAAQAABQACAOIRAwDlEgEAI4ECoJAAABRYaW8BBwD0dBQAAAwAIQARAHcWAAGJsFAUDAABsFASuAEXsGATPwABtQkDOjAAAAEAAAAAAA4AEAAAAN8M4AzhDJsOnA7QENEQDBEvEY4RjxGQEZERNRNwE0OAA6AgAAAM0aAQAAAM3m1ubwEDAPRmEAAAAQABABoBAYACoLAAABQnsDATcwABbnRDAAOgIAAAERuwUBHyAAFlaGk7MAAAAQAABgADAA4MAAADEgMA0hMBAPOAAmRyhwsAAAqAAfgAEAGAAfsAEAGgUAAAEYugUAAAFF6gMAAAEJmgMAAAEKQwMWNlaW9zdHV39ZgHAAABAC0I2gJeAQ==") \ No newline at end of file diff --git a/pr/672/docs/search.index/5bbc672548ec.js b/pr/672/docs/search.index/5bbc672548ec.js new file mode 100644 index 000000000..691bae88f --- /dev/null +++ b/pr/672/docs/search.index/5bbc672548ec.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAYAAQDAFAYA8wYBZfRvFAAAAQAyAAEAEwMDsEAUmQABsGAThwCJYXJ183sQAAB5AgEAI4ACgBF7ABABZHI=") \ No newline at end of file diff --git a/pr/672/docs/search.index/5c06b39c26ac.js b/pr/672/docs/search.index/5c06b39c26ac.js new file mode 100644 index 000000000..00b6f59b7 --- /dev/null +++ b/pr/672/docs/search.index/5c06b39c26ac.js @@ -0,0 +1 @@ +rn_("AQACoKAAABM3oDAAAAzOYWkBeRMAAAEAAbAwEUoAAW7yWAcAAAEAAQEA89QHAADZBwEAEwADsCAQGAMfoFAAABCoZWlz9VsEAAABAAEAAQABAAGEArCgFBoAAbCgFBwAAWNy8wEBdPTsEQAA6wABAAEAAQMBsGAUagABZjswAAABAAAHAAMACAwDAIoTAQASFAEA84ACYWXzgIQAKAU=") \ No newline at end of file diff --git a/pr/672/docs/search.index/5c9fb1c311dd.js b/pr/672/docs/search.index/5c9fb1c311dd.js new file mode 100644 index 000000000..9287059fd --- /dev/null +++ b/pr/672/docs/search.index/5c9fb1c311dd.js @@ -0,0 +1 @@ +rn_("AYMCsQAUvAABsNAUrAABa20BCwA7MAAAAQAABgABAB4UBgAjgAKggAAAFEhlbyMEA6BgAAAT3bBAFLcAAWVucAEjDAAAAQsA8wEUAAABAHUAdwAAAIWgYAAAEpWhAAAAFHkEgQM7MAAAAQAAOwAEALgFBQBPBwAAoAczAIURAABDgAOwgBGEASKgAAAAAhhka3Q=") \ No newline at end of file diff --git a/pr/672/docs/search.index/5e777cfd64d1.js b/pr/672/docs/search.index/5e777cfd64d1.js new file mode 100644 index 000000000..645f08bf0 --- /dev/null +++ b/pr/672/docs/search.index/5e777cfd64d1.js @@ -0,0 +1 @@ +rn_("AQAA9HoTAAABAAEAAQATAAKwgBNOAAFzdvRtCQAAAQCRCgEAE4ACsDARDQABbG4BgAKgAAAAEJWgAAAAEJY0NvMDAXYBAg0AABMAAqBAAAARiWFz9zUMAADCA2gBSgEBAAEAswEBAgD0JRMAAAEAkgABAAEDAPRbEwAAAQCYAAEA8wACYnU6MAAAAQAAAAAAEwAQAAAASgdLB0wH2A/ZD4UQhhCmEKcQPRE+EfAR8REjEyQTMBMxE0ITthO3E/OAAmR0E4ACoCAAAA0MZXIBBwGgQAAAFDpl8mgUAAABAAEBADswAAABAAAFAAEAXgkFAEcAAACFsDAPrwABoFAAABB4sHARBgABsUAUwAABgSkA8nYFAAABAPOAAmx0lwEAgIaxMBQ8AAGxEBRqAAGgsAAAE10RRQg=") \ No newline at end of file diff --git a/pr/672/docs/search.index/5ed95ff36a5f.js b/pr/672/docs/search.index/5ed95ff36a5f.js new file mode 100644 index 000000000..8485ef624 --- /dev/null +++ b/pr/672/docs/search.index/5ed95ff36a5f.js @@ -0,0 +1 @@ +rn_("AQkBoEAAABRlZfKHFAAAAQABgAKwAAzpAAGggAAAE8tkcAECA7BgE0gAAbDQFGgAAaCAAAAT6mlycwFzFAAA1wAAgIWwMA/EAAGhMAAAFIaQSQA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/5fb5abc1fd26.js b/pr/672/docs/search.index/5fb5abc1fd26.js new file mode 100644 index 000000000..1f68e98be --- /dev/null +++ b/pr/672/docs/search.index/5fb5abc1fd26.js @@ -0,0 +1 @@ +rn_("AQUA9MsSAAABAKkAAQABCAD0ihMAAAEAhwABADMBA7BAE1kAAWFiZfX0DAAAAQBFA2kC1QABAAA7MAAAAQAABgACAC0MAQC4DwQA84ACc3UjgQKwgBOEAAFydAEGAPSFEAAAAQC3AAEAUwADsIATCQABYml09XoFAAABAOYOVgABAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/6026531291d3.js b/pr/672/docs/search.index/6026531291d3.js new file mode 100644 index 000000000..940337a28 --- /dev/null +++ b/pr/672/docs/search.index/6026531291d3.js @@ -0,0 +1 @@ +rn_("AYACsIATCQABsKAThAABaXQBgAKw4BSEAAGwQBBTAAFscgEGAPTqEAAAAQABAAEARwUAAIeg0AAAFF6wUBBRAAGgAAAAE4aw8BQ4AAEFkQPy9A8AALAA") \ No newline at end of file diff --git a/pr/672/docs/search.index/60287e11cc71.js b/pr/672/docs/search.index/60287e11cc71.js new file mode 100644 index 000000000..a94ca7392 --- /dev/null +++ b/pr/672/docs/search.index/60287e11cc71.js @@ -0,0 +1 @@ +rn_("AQIA9CUTAAABAJIAAQABAwD0WxMAAAEAmAABAPMFAmJ1OjAAAAEAAAAAAAcAEAAAAIUQhhA9ET4R8BHxETATMRMBAgD0dREAAAEAfAEBAAEFAPN7EQAAAQABAAEEAPTLEgAAAQCpAAEA84CEEQkA") \ No newline at end of file diff --git a/pr/672/docs/search.index/608a5d02477c.js b/pr/672/docs/search.index/608a5d02477c.js new file mode 100644 index 000000000..b9b6a5230 --- /dev/null +++ b/pr/672/docs/search.index/608a5d02477c.js @@ -0,0 +1 @@ +rn_("AQUA9MsSAAABAKkAAQABCAD0ihMAAAEAhwABADMBA7BAE1kAAWFiZTowAAABAAAAAAAMABAAAAAxBzIHMwf0DPUMxw86EIgQiRCKEIsQoxJ4EwEAADswAAABAAAiAA4AxAQBAIgFCgAmCQEALQwBAOYMAQCaDgAAuA8EAM8PAADyDwEA6BABAPoQAACNEQAA6hEBAO8RAAABgQKwABF+AAGwABGAAAE0NiOAArCAFIwAAW9wAQABsEATVQABZfOJEQAAWgEBALcBAICFoGAAABEmEAEL") \ No newline at end of file diff --git a/pr/672/docs/search.index/6094984aadd1.js b/pr/672/docs/search.index/6094984aadd1.js new file mode 100644 index 000000000..6a63815d1 --- /dev/null +++ b/pr/672/docs/search.index/6094984aadd1.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAUAAQCQFAUA8w0BbTswAAABAAAFAAEAThQFAEMAhLAAFF8AAbBAEsMAAbAAE4cAiTgAAjswAAABAAAcAAcA+wsDADMRBwDnEgEAZhMEAEIUBABJFAMAthQAAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/60e7eb5d335c.js b/pr/672/docs/search.index/60e7eb5d335c.js new file mode 100644 index 000000000..af259d330 --- /dev/null +++ b/pr/672/docs/search.index/60e7eb5d335c.js @@ -0,0 +1 @@ +rn_("AYADoIAAABLWsFAS/AA8sHAULwABYWNzAQABoIAAABS2ZvL6CAAAngoBBwD1ZhMAAAEAAQABAAEAI4ACoFAAABFjYW/zAAJmczswAAABAAAIAAQATQcAAH4HBQAEDQAAmxEAAPOAAmR5") \ No newline at end of file diff --git a/pr/672/docs/search.index/60e98a5178e4.js b/pr/672/docs/search.index/60e98a5178e4.js new file mode 100644 index 000000000..d217b0300 --- /dev/null +++ b/pr/672/docs/search.index/60e98a5178e4.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAkAAQBOEQkAI4ADsGAThAABsDAPyAABaXN0AQgA9UIUAAABAAEAAQABAPMBAWb1mhAAAAEAAQABAAEAY4ADoDAAABNGZWd0") \ No newline at end of file diff --git a/pr/672/docs/search.index/60ea120b017f.js b/pr/672/docs/search.index/60ea120b017f.js new file mode 100644 index 000000000..37fce7cf1 --- /dev/null +++ b/pr/672/docs/search.index/60ea120b017f.js @@ -0,0 +1 @@ +rn_("AQIA9AoRAAABAJIBAQABAAD08g8AAAEA9QAHASOCArBQE1UAAWVzAQIA9CUTAAABAJIAAQABAwD0WxMAAAEAmAABAPOFAmJ1AQIA9HURAAABAHwBAQDzgAJpbPMAA2J0dTowAAABAAAAAAAPABAAAAByB/sH/Af9B/4HGxDhEK8TMRR+FH8UgRSOFI8UtRS5FAEMAPMUFAAAAQABAAGAArAACRkAAaAgAAANA2RuAQYA9CMTAAABAJIAAQDzAwFi9AgRAAABAJIBAQCXBgAAh7BAD/AAAaBwAAASp7CQE1MAATgJAzowAAABAAAAAAAkABAAAADBBcIFwwXEBTQHNQczDPgM+Qz6DP4M/wwADegP6Q88ED0QQRBCEF8QYBBhEGIQYxBkEGUQqBCtENkQ2hCPE7MT2hN0FIAUoRSyFA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/60eabdb6a059.js b/pr/672/docs/search.index/60eabdb6a059.js new file mode 100644 index 000000000..90fd8382b --- /dev/null +++ b/pr/672/docs/search.index/60eabdb6a059.js @@ -0,0 +1 @@ +rn_("AYMCsCARhAEioJAAABQpbXABggKwoBQaAAGwoBQcAAFjcgECAaBAAAARg2UBOxAAADOAhLAgEQEAAbAgD7YAAVAUAPOAAm9y") \ No newline at end of file diff --git a/pr/672/docs/search.index/60f27d69272c.js b/pr/672/docs/search.index/60f27d69272c.js new file mode 100644 index 000000000..56f09e600 --- /dev/null +++ b/pr/672/docs/search.index/60f27d69272c.js @@ -0,0 +1 @@ +rn_("AQAA9CkTAAABAFgBAQDzBAFzOzAAAAEAAA0AAgCmEwcAmxQFAEcAAASGoIAAABOGsQAUqgABsEAS4wABsIAUugABsFATKwABFQAW9JYOAAABAAEAAQA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/62158b71bbd3.js b/pr/672/docs/search.index/62158b71bbd3.js new file mode 100644 index 000000000..bfac4969a --- /dev/null +++ b/pr/672/docs/search.index/62158b71bbd3.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAABEABQCuBQAAMgwAAOgMAAAKEA0AcBEAAAGEArDQFK4AAaCQAAATrmJoAYQCsAARfgABsAARgAABNDaXBAAAh6AgAAAR3aAwAAARRKAwAAASqLBwD+IAActACDswAAABAAARARQAuAEAAKQFAAD4CAAA/ggAABsJAgAMDAAATgwBAHUMAQDrDAAAGA3tAMoPAADfDwEA1hAAAJIRAADwEgEA1BMFAPETAAB4FAAAkBQFAKwUAQA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/624cc81e0578.js b/pr/672/docs/search.index/624cc81e0578.js new file mode 100644 index 000000000..86094d4cd --- /dev/null +++ b/pr/672/docs/search.index/624cc81e0578.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAI0AAgD3EAAACBKMAAEAAbBwE8wAAWXy4g8AAAEA8wABc/RNEAAAAQBLBAEAM4ADoGAAABGqZGlv") \ No newline at end of file diff --git a/pr/672/docs/search.index/62a274538f89.js b/pr/672/docs/search.index/62a274538f89.js new file mode 100644 index 000000000..03f4f3698 --- /dev/null +++ b/pr/672/docs/search.index/62a274538f89.js @@ -0,0 +1 @@ +rn_("AQEBsIATbQABdfJECQAAAQAjgAKwEAlCAAEzNgECAPNRBwAA7wEBAAECAPQ9CQAAAQAICAEAAQIA81AMAAABAAEAE4ACsBAJOwABaXUBAgA7MAAAAQAACwACANQTBQCQFAUA8wkBbTswAAABAAAMAAMAbREAAOkSBQBOFAUAdzAAAIqgMAAAE+qgcAAAEf+gIAAACT+wMBHEAAGwUBABAAEzhQs7MAAAAQAANAIEAP4BAAAkAjIBdQcAAAIK/wA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/62ea63f61793.js b/pr/672/docs/search.index/62ea63f61793.js new file mode 100644 index 000000000..d5944010b --- /dev/null +++ b/pr/672/docs/search.index/62ea63f61793.js @@ -0,0 +1 @@ +rn_("AQsA9HQUAAAMACEAEQABCgA6MAAAAQAAAAAABwAQAAAAURNSE+0T7hMlFCYUdRR2FPOBAm90EwCEsCATRQBasDARSgABoFAAABEyEDACOzAAAAEAAAgABACEAAEArgADAFgHAQBvCQAA") \ No newline at end of file diff --git a/pr/672/docs/search.index/63002ba13ba6.js b/pr/672/docs/search.index/63002ba13ba6.js new file mode 100644 index 000000000..41595338a --- /dev/null +++ b/pr/672/docs/search.index/63002ba13ba6.js @@ -0,0 +1 @@ +rn_("AQ0A9eATAAABAAEAeAABAAECAPQKEQAAAQCSAQEAAQMA9HURAAABAHwBAQDzAAJidQEbEAAAAQMA9AgRAAABAJIBAQDzhAJkcwECAPMMCQAAAQBZAPOAAml0ARgA88QUAAABAAEAI4ECsMAT3gABY20BBQGwYBLFAAFy9CEMAAABAPcDAQABCQD28BIAAAEAAAGHADQAAQATgAKwYBDIAAFkbgEDAaBwAAATLWf0WwcAAAEACAIBAAEJAPN+FAAAAQA6AAGBArCAFI4AAaBgAAAUtWRzFwEAiYWw0BS3AAGgQAAAFGGwYBQxAFACkApnugAAjLBwES0AAbCwEz0AAaCgAAAS77FQFK4AAbDwFA4AAW3BD/SHAAAAAQABAAEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/644655b6a37b.js b/pr/672/docs/search.index/644655b6a37b.js new file mode 100644 index 000000000..99228ec90 --- /dev/null +++ b/pr/672/docs/search.index/644655b6a37b.js @@ -0,0 +1 @@ +rn_("AQIBsEATugABZfLCBgAAAQABBADzbxAAAAEAAQBDAAOwQBMcAAGxABQBAAFlZnA7MAAAAQAAJAAGABkCAADRBgwA3wYMAKIPBgAQEQAAXhMBAGOAA7AwDBEABWFlaQ==") \ No newline at end of file diff --git a/pr/672/docs/search.index/647990943ac3.js b/pr/672/docs/search.index/647990943ac3.js new file mode 100644 index 000000000..82420c41f --- /dev/null +++ b/pr/672/docs/search.index/647990943ac3.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABNNc/TODwAA+AMBAAQAAQECsGAUPgABsEAUfAABZHQ7MAAAAQAADQAGAP4SAwBrEwEA2xMBAPITAQBxFAEAlxQBAAEBA7BwEgAAAbCAEtoAAYATgAAQ+G1uczswAAABAAALAAIA8QgFAGATBQCjgISwsBPJAAGxcBS6AAEIQIIBAIWgcAAAEp+wMBSuAAGgcAAAEQ+xABQ+AAGwABNBAWQGIQHyeAUAAAEAAQEAOzAAAAEAAAYAAQDAFAYA8wYBZfRvFAAAAQAyAAEAEwEDsEAUmQABsGAThwCJYXJ183sQAAB5AgEAdwQAAIegEAAABa2wMBMyAUmgQAAAETIRwQw7MAAAAQAABgACAI4TAAD2EwUA") \ No newline at end of file diff --git a/pr/672/docs/search.index/650b84df086a.js b/pr/672/docs/search.index/650b84df086a.js new file mode 100644 index 000000000..a51927649 --- /dev/null +++ b/pr/672/docs/search.index/650b84df086a.js @@ -0,0 +1 @@ +rn_("AYICsEAUYwABsGATOQABZXIBBQD0nxAAAAEAAQABAPOAAmZtAQYAOjAAAAEAAAAAAAgAEAAAAN8P4A+SEfAS8RLxE3gUrBStFAEAArCwFAkAAaFAAAAUpWVn9HkHAAABAAEAAQDDAISgsAAAE9qwQBC3AAEUIAT37wYAAN8FgQMBADoBLgEXAWOAA7AwEBwAAWFlaQ==") \ No newline at end of file diff --git a/pr/672/docs/search.index/651d241c3c65.js b/pr/672/docs/search.index/651d241c3c65.js new file mode 100644 index 000000000..670630c45 --- /dev/null +++ b/pr/672/docs/search.index/651d241c3c65.js @@ -0,0 +1 @@ +rn_("AQEBsIATbQABdfJECQAAAQAjgAKwEAlCAAEzNgEGAPPxEAAAAQAVAROAArAAD7YAAW91AQIA81EHAADvAQEAAQMA880SAAABAAEAI4ACsAARxAABaGkBAAA7MAAAAQAAEwABAIQHEwAjgAKgMAAADN1hZfOAAnJ0E4ACsDATvAABYWwBAAGggAAAFLZm8voIAACeCgEHAPVmEwAAAQABAAEAAQAjgAKgUAAAEWNhb/MCAmZzOzAAAAEAAAgABABNBwAAfgcFAAQNAACbEQAAE4ACoEAAAAwNYW8BAwGwQBR8AAF08tsTAAABACOFAqBAAAAUC2JzIwADsEARAwABoFAAABFwYWJm9wcJAAABAEgAeAcBAGIAAQATgQKgUAAAEf9ndAGAAqBAAAAR7KAgAAAT6mFpAQIA9D0JAAABAAgIAQABAgDzUAwAAAEAAQATgAOwEAk7AAGwMBQcAAFpdXkBAgA7MAAAAQAACwACANQTBQCQFAUA8wgBbTswAAABAAAMAAMAbREAAOkSBQBOFAUAAYECoIAAABLWoHAAABQvYXPzgAJucrfbAQCNoUAAABSLoCAAAAk/sFAQAQABN5UPOzAAAAEAADUCBQD+AQAAJAIyAQgHAAB1BwAAAgr/AA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/651ea497c196.js b/pr/672/docs/search.index/651ea497c196.js new file mode 100644 index 000000000..8531d0eef --- /dev/null +++ b/pr/672/docs/search.index/651ea497c196.js @@ -0,0 +1 @@ +rn_("AQABoLAAABPOYvNYEwAALAEBAAGAAqAgAAAPz6AwAAAT2mFy8wABdAF9BwAAAQQBoEAAABRlZfKHFAAAAQABBQA7MAAAAQAACwAFAFkQBQDQEgEAARQBAFgUAAB3FAAA4wGEoDAAAA/NhQACOzAAAAEAAAsABQCmBQIAvAYEAFcMAQCLEQAA4hIAAGOAA6AAAAATV2Rucg==") \ No newline at end of file diff --git a/pr/672/docs/search.index/65427ca2c609.js b/pr/672/docs/search.index/65427ca2c609.js new file mode 100644 index 000000000..94bf92995 --- /dev/null +++ b/pr/672/docs/search.index/65427ca2c609.js @@ -0,0 +1 @@ +rn_("AQAAOjAAAAEAAAAAAAoAEAAAAOYP5w/XENgQYxH0EfUREhMTE6oUqxQBBADz4Q8AAOEAAQDzAAFx9woNAAABAD8DAQDNAGEAAQDzAAJleTowAAABAAAAAAAOABAAAAAJCVMMVAx3EIwQjRAiESMRJBH8E/0TDBQNFIYUixQ=") \ No newline at end of file diff --git a/pr/672/docs/search.index/66c7e9777791.js b/pr/672/docs/search.index/66c7e9777791.js new file mode 100644 index 000000000..9474473c2 --- /dev/null +++ b/pr/672/docs/search.index/66c7e9777791.js @@ -0,0 +1 @@ +rn_("AQAA9LEPAAABAAEAAQDzAgFzATIRAAABAAKwgBSOAAGgYAAAFLVkc/L3CAAAQAOTgISwwBPMAAGgEAAAEd0AwUA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/66d031770208.js b/pr/672/docs/search.index/66d031770208.js new file mode 100644 index 000000000..1b201b456 --- /dev/null +++ b/pr/672/docs/search.index/66d031770208.js @@ -0,0 +1 @@ +rn_("AYADsAAT5gABsAAT6AABsHAUXwABNDZhAQQA9LMUAAABAAoAAQAjgQKgQAAAEV5tcAEBADswAAABAAAIAAMA1hIAAEwTAADAFAYAxwEAAMWgQAAAEFiggAAAErBABoAIOzAAAAEAABYACAD1DwEAFhEBAFoRAwDREQUAwhMDABEUAABvFAEAohQBAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/686d5bf1a575.js b/pr/672/docs/search.index/686d5bf1a575.js new file mode 100644 index 000000000..bba18bfe1 --- /dev/null +++ b/pr/672/docs/search.index/686d5bf1a575.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAUAAQBgEwUAg4YEsFATJwABsHATkAABoNAAABRuYm5vdhOAArAQEvgAAXByAQABoDAAABHvdAGvEwAA8wIBc/KoEAAACwMTgAKgYAAAEJlldQECAPYKDQAAAQA/AwEALgEBAAGAArAAEuMAAYAOmjUAAWVzAQEA81cHAAAtDAEAY4ADsIAUAwABYm5084ADYW9yARQAOzAAAAEAAAUAAQCbFAUAAQAAOzAAAAEAAAYAAgCOEwAA9hMFACOIArBAFGwAAWVzAQQBsDATXgABY/OGEQAAAQABAAEFADswAAABAAAIAAMA/BECAAITBAApFAAA8wIDY2Vw88sMAAABAAEA84ACb3UBAAGwYBQOAAFlAUcUAADzAgFz9iMJAAABAAEAIAg/ACIBAQgA88ASAAABAAEAQ4ADoBAAABOSoOAAABQZZG9zAQIBoFAAABGFcgEmEQAAAYACoEAAABCloHAAABRZYWUBAAA7MAAAAQAACwACAPQGBQDcEgUA84ACdHYjAAKwEAfuAAFucvYYCQAAWAATBwEAtwABAPMAhzCRDDowAAABAAAAAAAIABAAAADJBMoEAxEEEfYS9xKAE4ETeRQ=") \ No newline at end of file diff --git a/pr/672/docs/search.index/6995c7586e21.js b/pr/672/docs/search.index/6995c7586e21.js new file mode 100644 index 000000000..c31535e66 --- /dev/null +++ b/pr/672/docs/search.index/6995c7586e21.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAUAAgB/DAMAGhMBAAECAPN7EQAAAQABAAEGAPSmFAAAAQABAAEA8wYBbfSsEgAAAQABAAEA84ACbnbzgAJhaQ==") \ No newline at end of file diff --git a/pr/672/docs/search.index/6ae15219502a.js b/pr/672/docs/search.index/6ae15219502a.js new file mode 100644 index 000000000..177d0b98f --- /dev/null +++ b/pr/672/docs/search.index/6ae15219502a.js @@ -0,0 +1 @@ +rn_("AYACoFAAABFFoFAAABKVYXUBgwKxABS8AAGw0BSsAAFrbQELADswAAABAAAGAAEAHhQGACOAAqCAAAAUSGVvIwMDoGAAABPdsEAUtwABZW5wASMMAAAjgAKgQAAAENZtcAELAPMBFAAAAQB1APcAAACFoQAAABR5BIEDOzAAAAEAADsABAC4BQUATwcAAKAHMwCFEQAA") \ No newline at end of file diff --git a/pr/672/docs/search.index/6b95550a6642.js b/pr/672/docs/search.index/6b95550a6642.js new file mode 100644 index 000000000..b2c094780 --- /dev/null +++ b/pr/672/docs/search.index/6b95550a6642.js @@ -0,0 +1 @@ +rn_("AQIA9QATAAABAPIAfgABABOAArAwEzIBSWlwAQYA87ATAAABAAEA8wICYnMBQBAAAPMBAWYBZgcAAAECAPRbEwAAAQCYAAEAM4ADsEATJwABaXV5") \ No newline at end of file diff --git a/pr/672/docs/search.index/6be7775146ac.js b/pr/672/docs/search.index/6be7775146ac.js new file mode 100644 index 000000000..e5ca5bc69 --- /dev/null +++ b/pr/672/docs/search.index/6be7775146ac.js @@ -0,0 +1 @@ +rn_("AQMBsEARYQABZQGVDgAAAQUAOzAAAAEAAAYAAQBfEAYAAQMAOzAAAAEAAAUAAQDXDAUA8wACcHM6MAAAAQAAAAAADQAQAAAAqQWqBcEGEglZDGYMBhEHEbYStxIOEw8TEBMREzMAA7BQD9AA02RwcjswAAABAAAPAAYAogQAAGgFAwC9DwAA4hEDALESAwDlEgEA") \ No newline at end of file diff --git a/pr/672/docs/search.index/6c223b5d689a.js b/pr/672/docs/search.index/6c223b5d689a.js new file mode 100644 index 000000000..36f449874 --- /dev/null +++ b/pr/672/docs/search.index/6c223b5d689a.js @@ -0,0 +1 @@ +rn_("AYACsEATcQABoMAAABQvZW8BAgD01xEAAAEAAQABABMAA7AgEdsAAbCAFEAAAWFzdAHeDAAAAQQA8/4PAAABAAEAs4CEsEAThAABQQAG") \ No newline at end of file diff --git a/pr/672/docs/search.index/6c39ad1a7a87.js b/pr/672/docs/search.index/6c39ad1a7a87.js new file mode 100644 index 000000000..116471508 --- /dev/null +++ b/pr/672/docs/search.index/6c39ad1a7a87.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAcAAQBTCQcAAQIA90wQAADbALkCAQABAHgAAQATgAKwMBDpAKRpdPMAAXPyjBQAAAEAM4ADsRAT/wABZG5w") \ No newline at end of file diff --git a/pr/672/docs/search.index/6ca18fe2a088.js b/pr/672/docs/search.index/6ca18fe2a088.js new file mode 100644 index 000000000..bd10ebee5 --- /dev/null +++ b/pr/672/docs/search.index/6ca18fe2a088.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABNNc/TODwAA+AMBAAQAAQECsGAUPgABsEAUfAABZHQ7MAAAAQAADQAGAP4SAwBrEwEA2xMBAPITAQBxFAEAlxQBAAEBA7BwEgAAAbCAEtoAAYATgAAQ+G1uczswAAABAAALAAIA8QgFAGATBQCjgISwsBPJAAGxcBS6AAEIQIIBgAKwMBSuAAGxABQ+AAFjbgEBADswAAABAAAGAAEAwBQGAPMGAWX0bxQAAAEAMgABABMBA7BAFJkAAbBgE4cAiWFydfN7EAAAeQIBAHcBAACFsDATMgFJEYEIOzAAAAEAAAYAAgCOEwAA9hMFAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/6cec104bb6e3.js b/pr/672/docs/search.index/6cec104bb6e3.js new file mode 100644 index 000000000..8e6140b61 --- /dev/null +++ b/pr/672/docs/search.index/6cec104bb6e3.js @@ -0,0 +1 @@ +rn_("AYMCoJAAABQ2oJAAABQ3MjMBgAKgUAAAFGKggAAAE4NjZfMAAnN08kAHAAABAAEAAaCwAAATzmLzWBMAACwBAQAjgAKgAAAAE1dkcgEAAbCAFDwAAXDyOBQAAAEAQwADoCAAABFgoEAAABK1ZGVz8/gPAABQAwEAE4ICoCAAABQoZWnzgAJjZQEEADswAAABAAAJAAQAXAkBAJ8QAwDiEAEA7xMBAPOAA2VpeQ==") \ No newline at end of file diff --git a/pr/672/docs/search.index/6d172713b950.js b/pr/672/docs/search.index/6d172713b950.js new file mode 100644 index 000000000..08cf09140 --- /dev/null +++ b/pr/672/docs/search.index/6d172713b950.js @@ -0,0 +1 @@ +rn_("AQIA9CUTAAABAJIAAQABAwD0WxMAAAEAmAABAPMCAmJ1OjAAAAEAAAAAABMAEAAAAEoHSwdMB9gP2Q+FEIYQphCnED0RPhHwEfERIxMkEzATMRNCE7YTtxM=") \ No newline at end of file diff --git a/pr/672/docs/search.index/6d22a2adcab1.js b/pr/672/docs/search.index/6d22a2adcab1.js new file mode 100644 index 000000000..9676c22b5 --- /dev/null +++ b/pr/672/docs/search.index/6d22a2adcab1.js @@ -0,0 +1 @@ +rn_("AYMCoFAAABEUoKAAABNQa3ABAAGwQBKYAAFs9GsJAAABAHgHAQABCAD0rBIAAAEAAQABAAEEAaAAAAAQ7nMBVRAAAAECAaBQAAATC2MBzwwAALcAAACGsEANDQABoJAAABM4oDAAAAwPlRABOzAAAAEAAAgABADIBAAAmQUCANIMAQDADwIAo4CEsDAJKQABoBAAABOGkQBAARABsAAUggABczswAAABAAAFAAEAmxQFAAEDAPRrCQAAAQB4BwEAY4ADoAAAAAwPbnJ1AQACsNATzAABoDAAAA/DYmr2fAUAAAEAAQBJAAEAdwEBAAGwgBPrAAF0OzAAAAEAAEYABADFBQAAFAkAAB4JAACEDEMAAQkA880SAAABAAEA84ADY2d0AYMCoJAAABQ2oJAAABQ3MjMBgAKgUAAAFGKggAAAE4NjZfMAAnN08kAHAAABAAEAAaCwAAATzmLzWBMAACwBAQAjgAKgAAAAE1dkciOAAqBQAAAUKGNl84ACaXkBBAD1PxEAAAEAAQABAAEA8wEBbgE2BwAAAQEAOzAAAAEAAAkAAgDjBwcAFBMBAAEGAPS+EwAAAQABAAEAAQ8A9KYUAAABAAEAAQDzgQJobQEAAbBAFGYAAWX1KhQAAAEAAQABAAEAU4ADoCAAABHhbG50120AgMugIAAAFJawYBDSAAGwQA/8AAHAGzgK") \ No newline at end of file diff --git a/pr/672/docs/search.index/6dc7b167965d.js b/pr/672/docs/search.index/6dc7b167965d.js new file mode 100644 index 000000000..72b5e77d9 --- /dev/null +++ b/pr/672/docs/search.index/6dc7b167965d.js @@ -0,0 +1 @@ +rn_("AQAAOjAAAAEAAAAAAAoAEAAAAOYP5w/XENgQYxH0EfUREhMTE6oUqxTzAQF58gwUAAABACMAA6BAAAARgqBQAAAUXmVvc/LGDwAA2gI=") \ No newline at end of file diff --git a/pr/672/docs/search.index/6e88e59bfb82.js b/pr/672/docs/search.index/6e88e59bfb82.js new file mode 100644 index 000000000..1d8080291 --- /dev/null +++ b/pr/672/docs/search.index/6e88e59bfb82.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwDLEQIAfhMBABQUAgABBADzzhEAAAEAAQAjAAKgYAAAEv1hcvPvEAAAAQDJAQGAAqEQAAAUobCAFJcAAWl1NwQAgYexUBS+AAGgAAAAEM+wcBPeAAGhMAAAFLIKQBsjgAKgMAAAEppudg==") \ No newline at end of file diff --git a/pr/672/docs/search.index/6eb9e85c5b88.js b/pr/672/docs/search.index/6eb9e85c5b88.js new file mode 100644 index 000000000..2ad2a479e --- /dev/null +++ b/pr/672/docs/search.index/6eb9e85c5b88.js @@ -0,0 +1 @@ +rn_("AQYA9L4TAAABAAEAAQABDwD0phQAAAEAAQABAPOHAmhtAQAA9A4TAAABAAEAAQDzBAFk9LESAAABAAEAAQAjh4SwoBRWAAGwQBMMAAGwcBP/AAEVgAABAAGgQAAAEJdlOzAAAAEAAAcAAgBsBwUA+QcBABOAArBQEE0AAWRyAQkA9eATAAABAAEAeAABALcCAICGsCASvAABVFDsruw9EUkB") \ No newline at end of file diff --git a/pr/672/docs/search.index/6f8ed6ef61e6.js b/pr/672/docs/search.index/6f8ed6ef61e6.js new file mode 100644 index 000000000..7cce145a3 --- /dev/null +++ b/pr/672/docs/search.index/6f8ed6ef61e6.js @@ -0,0 +1 @@ +rn_("AQQAOjAAAAEAAAAAAAcAEAAAANgP2Q+mEKcQIxMkE7YTtxMBBAD02g8AAAEAAQABAAEAiKEQAAAUeoAI/wAa3aBAAAARFbBgFGoAAaAQAAAMg7BQFAMAAbAwEXcAAbEgFIwAATnBAjowAAABAAAAAAARABAAAAAVCRYJKQkqCQgMCQwKDAsMEQwWDN0MoRKKE4sToBOhExIUExTzgANlaW8=") \ No newline at end of file diff --git a/pr/672/docs/search.index/70531bfc34d4.js b/pr/672/docs/search.index/70531bfc34d4.js new file mode 100644 index 000000000..87e448e49 --- /dev/null +++ b/pr/672/docs/search.index/70531bfc34d4.js @@ -0,0 +1 @@ +rn_("AYACsHAUlwABoAAAAASmZmcBAQA7MAAAAQAABwABAEIMBwABAQGgcAAAEqBhARIHAAAjgAKhAAAAFKFkbnOAhKBgAAASnwBBSA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/7063c86102c3.js b/pr/672/docs/search.index/7063c86102c3.js new file mode 100644 index 000000000..fcf598627 --- /dev/null +++ b/pr/672/docs/search.index/7063c86102c3.js @@ -0,0 +1 @@ +rn_("AQMA88ASAAABAAEAI4ECsSAUugABY2sBBAGgQAAAEfll8wUNAAAUBAEAAQAAOzAAAAEAAAUAAgDzEAMAjBMBAHOAhKAwAAAUOggwAQ==") \ No newline at end of file diff --git a/pr/672/docs/search.index/7093873e356a.js b/pr/672/docs/search.index/7093873e356a.js new file mode 100644 index 000000000..84d2d60b9 --- /dev/null +++ b/pr/672/docs/search.index/7093873e356a.js @@ -0,0 +1 @@ +rn_("AQkA834UAAABADoAAYACoEAAABNdsCAUGgABaGkBAQDz+Q8AAAEAAQATgAKwwBS3AAF0dQGBArCAFI4AAaBgAAAUtWRzNwMAg4agQAAAFGGwYBQxAFACkA4BEgGwABSCAAFzOzAAAAEAAAUAAQCbFAUAAQDGoFAAABLioAAAABCZsCARTAABoFAAABH4oQAAABSWoEAAABQ7AAMCCzswAAABAAAVAAcArgUAADIMAADoDAAAChANAHARAAD4EgEANhQBAAEBAbCwFIkAAWw7MAAAAQAADAADACUHCAAGDQEASBABAAEBAaAwAAARKGjzAQkAAC4AsQgBAgD01xEAAAEAAQABABOBArAgEdsAAWFz84ICbm/3AQCAh6AgAAAFrbBADBgAARUBBw==") \ No newline at end of file diff --git a/pr/672/docs/search.index/712b58a1cc15.js b/pr/672/docs/search.index/712b58a1cc15.js new file mode 100644 index 000000000..1a451bbc3 --- /dev/null +++ b/pr/672/docs/search.index/712b58a1cc15.js @@ -0,0 +1 @@ +rn_("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") \ No newline at end of file diff --git a/pr/672/docs/search.index/715b0a6426a1.js b/pr/672/docs/search.index/715b0a6426a1.js new file mode 100644 index 000000000..75056b1c7 --- /dev/null +++ b/pr/672/docs/search.index/715b0a6426a1.js @@ -0,0 +1 @@ +rn_("AQUA9AUUAAABAE4AAQDzAwFj8gcTAAABACOAArBwFLoAAWhvAYICsAAS4wABsAAR6gABZXNDAAOgoAAAFFmgIAAAE5JlaXT19hIAAAEAiQABAPgAAYACsHAS2gABoLAAABQZZW8BAgA7MAAAAQAABgABAGQRBgABAgGgQAAAFDpl8mgUAAABAPOAAmlvAQgA9PwTAAABAIkABQATAQOwkBRjAAGwUBNBAWRnbHA7MAAAAQAAEgAJAGkQAgDtEQEALhMBAFETAQCIEwEA7RMBACUUAQA0FAEAdRQBAAGAArBAErgBF6AgAAARKGFpAQMA89cSAAABAAEAI4ECsDAUDAABY2kBAAD0KRMAAAEAWAEBAPMDAXM7MAAAAQAADwADAOgRAQCmEwcAmxQFAAEDAPXmEQAAAQAlAQEAYQHzgAJydgEAADswAAABAAAFAAIAthIBAA4TAwDzAgFkOzAAAAEAAAkAAwDiEQMAsRIDAOUSAQAjgAKgkAAAFFhpbxOAArDwFKoAAWxvAQUA9HQUAAAMACEAEQAjgAKgYAAAE4ZncyOAArCQFFYAAWJs9xcDAI+wcBIAAAGwYBSMAAGwcBP/AAGwYBM/AAGwUBMrAAG1+Rc7MAAAAQAAPAAYALUFAABqBwEA8QgFAAEJAAAvCQAADwwAAF4MAQDfDAIACA0BAJYOAwCbDgEA/A8EANAQAQAMEQAALxEAAE4RCQCMEQAAjhEDAOARAAA1EwAAThMBAGATBQBwEwAAKBQAAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/719b4a1ba67f.js b/pr/672/docs/search.index/719b4a1ba67f.js new file mode 100644 index 000000000..cbf6b7cc8 --- /dev/null +++ b/pr/672/docs/search.index/719b4a1ba67f.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABPlcwH5EAAAAQMA9IgQAAABAAEAAQATgAKwUBCMAAFhaQGAArBwFK4AAbBwFDwAAWVyAYACoLAAABRzoDAAABElbG6XAgAAhqAgAAASn6AgAAAI+LAwDgYAARCRBTowAAABAAAAAAARABAAAACeBNYE1wTYBNkEaQmxD7IPsw+0D00ThxMLFBAUHBQdFDgUORQBAgA6MAAAAQAAAAAACgAQAAAAUQlSCSIQcRNyE6IToxNNFKQUvBS9FAEAADswAAABAAAGAAIAjhMAAPYTBQABAgGwMBNeAAFjOjAAAAEAAAAAABMAEAAAAPsMzA9mEGcQaBBtEG4QchBzEA8RghGGEYcRiBH/EVMTVBM6FGMUZBQBAwDzyBMAAGoAAQATgQKwQBFuAAFhZROAAqCwAAAUemFlAQEBsEAUZgABZfUqFAAAAQABAAEAAQATgAKgEAAADA9hb4cPAADIsDAJCQdusEAUbAABoIAAABKaQEyICfPyBwAAJQQ/AAGAAqCAAAATeKCQAAATUGFrAQMA88ASAAABAAEAI4CEsSAUugABoAAAAAksoDAAAA/TBAQGAQgA9cIUAAABAAEAAQABABOKArBAFJkAAWFyk4CEoMAAABNMoQAAABQZAUAGAQMBoEAAABH5ZfMFDQAAFAQBAAEFAPS+EwAAAQABAAEAo4CEoIAAABM4sAATcwABEQAGAQYA9KYUAAABAAEAAQDzCAFt9KwSAAABAAEAAQABAgA7MAAAAQAABQABAJAUBQDzDQFtOzAAAAEAAAUAAQBOFAUAAQQA9w0NAAABAKcFDgABAJEAAQABAwGAE4AAEPhzOzAAAAEAAAUAAQBgEwUAI4ICoJAAABRiaXMBBQGgUAAAFCdo8roSAAABABOAArAwE3MAAW50AYACoIAAABOTsEAR8gABYWUBgAKgEAAAERugAAAAEWBucgEJAaBAAAAUZWXyhxQAAAEAI4ACoJAAABPLaWzHIwAACqAwAAARHLBQEycAAbDwFLAAAbBwE5AAAaDQAAAUbjBiY2VoaWtub3Y7MAAAAQAAHgALAA4MAADzEAMAAhIEAIwTAQCbEwMA0hMBABkUAAAeFAYAPBQBAEgUAABfFAEAAQICoLAAABRYoLAAABRZY3Pz0BAAAAEAXgABAQKwUBHyAAGgwAAAFCdoafLSEwAAAQDzgAJlaQEBAbCQE4wAAXP0shAAAAEAAQABAAEDAaAAAAAQ7nMBVRAAAPOAA2hpdAEBAaBQAAATC2MBzwwAAAEFAbCAFF8AAXY7MAAAAQAABwACANERAwDCEwMAAQEA9HUQAAABALMAAQDjgISwcBH2AAEOCAD3tQcA0LCQEpgAAaBQAAARFKCgAAATr6AQAAAHTkA/vSk7MAAAAQAAPQAYAMYEAADIBAAAmQUCABUHAwD7CwMA0gwBAMAPAgDODwAA+A8AAAEQAQAjEAEAMhEIAIkRAADaEgEA4xIBAOcSAQBIEwEAZhMEALwTAQDGEwEAyxMAAEIUBABJFAMAthQAAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/73a9ad245c25.js b/pr/672/docs/search.index/73a9ad245c25.js new file mode 100644 index 000000000..19fd01f3c --- /dev/null +++ b/pr/672/docs/search.index/73a9ad245c25.js @@ -0,0 +1 @@ +rn_("AQACoCAAABOSsDAR6gABaXTy9hIAAAEAAQMAOzAAAAEAAAYAAQBkEQYAAQgA9PwTAAABAIkABQATAQOwkBRjAAGwUBNBAWRnbHA7MAAAAQAAEgAJAGkQAgDtEQEALhMBAFETAQCIEwEA7RMBACUUAQA0FAEAdRQBAAGAArAwEeYAAbAwEegAAXJ2AQQBsAAStgABZDswAAABAAAFAAIA4hEDAOUSAQC3BQABh7BQErgBF7BgEz8AAZQJAzowAAABAAAAAAALABAAAADfDOAM4QybDpwODBGOEY8RkBGRETUTcBMjgAKgEAAADN5ubwEDAPNmEAAAAQABACOAAqAAAAAMDmRyYwGEoFAAABGLoDAAABCkFAEQ8pgHAAABABOAA7BgEtoAAbAAD6sAAWNnchOAA6CwAAAUGaBAAAAOmmVvcw==") \ No newline at end of file diff --git a/pr/672/docs/search.index/74503773b7e7.js b/pr/672/docs/search.index/74503773b7e7.js new file mode 100644 index 000000000..ba8dd6fea --- /dev/null +++ b/pr/672/docs/search.index/74503773b7e7.js @@ -0,0 +1 @@ +rn_("AYACsRAUmQABsBAMUwABZW8BBQA7MAAAAQAADQABAJwRDQABAgDzBxAAAAEAAQABAgD0AxAAAAEAAQABAAEDADswAAABAAAIAAEAuRAIAIcHAACHsKATxgABsNAUXAABoFAAABQnjBQROzAAAAEAAAYAAwBWBwAA6hADALoSAQDzgAJsdBOAA6AgAAAMzrCgFEAAAWFkbg==") \ No newline at end of file diff --git a/pr/672/docs/search.index/747b64cfeb14.js b/pr/672/docs/search.index/747b64cfeb14.js new file mode 100644 index 000000000..4a9e01aa8 --- /dev/null +++ b/pr/672/docs/search.index/747b64cfeb14.js @@ -0,0 +1 @@ +rn_("AQAA9CkTAAABAFgBAQDzBAFzOzAAAAEAAA0AAgCmEwcAmxQFAEcAAAGGoIAAABOGsQAUqgABsEAS4wABsIAUugABsFATKwABFQAW9JYOAAABAAEAAQABBQDz/g8AAAEAAQBjgISwQBCMAAGgAAAABt4A8AA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/758d161430bc.js b/pr/672/docs/search.index/758d161430bc.js new file mode 100644 index 000000000..4407f7996 --- /dev/null +++ b/pr/672/docs/search.index/758d161430bc.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgCOEwAA9hMFACOIArBAFGwAAWVzAQQBsDATXgABY/OGEQAAAQABAAEFADswAAABAAAIAAMA/BECAAITBAApFAAA8wEDY2Vw88sMAAABAAEAAQIA9hgJAABYABMHAQC3AAEA84ACbnI=") \ No newline at end of file diff --git a/pr/672/docs/search.index/75c77b7919db.js b/pr/672/docs/search.index/75c77b7919db.js new file mode 100644 index 000000000..31475b96c --- /dev/null +++ b/pr/672/docs/search.index/75c77b7919db.js @@ -0,0 +1 @@ +rn_("AQgA9UIUAAABAAEAAQABAPMAAWY7MAAAAQAABQACAPIBAACaEAQAAQIAOzAAAAEAAAsAAgDUEwUAkBQFAPMGAW07MAAAAQAADQAEANAMAABtEQAA6RIFAE4UBQBjgAOwAAkhAAFlbXM=") \ No newline at end of file diff --git a/pr/672/docs/search.index/763c0d5d4643.js b/pr/672/docs/search.index/763c0d5d4643.js new file mode 100644 index 000000000..6e8513a0c --- /dev/null +++ b/pr/672/docs/search.index/763c0d5d4643.js @@ -0,0 +1 @@ +rn_("AQgA9HUQAAABALMAAQABAAGggAAAFLZm8voIAACeCgEHAPVmEwAAAQABAAEAAQAjgAKgUAAAEWNhb/MCAmZzOzAAAAEAAAgABABNBwAAfgcFAAQNAACbEQAAM4ADoEAAAAwNYWVv") \ No newline at end of file diff --git a/pr/672/docs/search.index/769cab48f482.js b/pr/672/docs/search.index/769cab48f482.js new file mode 100644 index 000000000..305f8fa47 --- /dev/null +++ b/pr/672/docs/search.index/769cab48f482.js @@ -0,0 +1 @@ +rn_("AQAA9D0JAAABAAgIAQABCQGggAAAFLZmAZgTAAABAgD13BAAAAEAAQBfAgEA8wACaXU6MAAAAQAAAAAABwAQAAAAthDvEt4T3xMOFA8ULxQwFAEIAPVCFAAAAQABAAEAAQDzAgFm9ZoQAAABAAEAAQABABOAArCgFIwAAXJ08wACYXPzyhAAAAEAAQDzgAJ0dg==") \ No newline at end of file diff --git a/pr/672/docs/search.index/76bc946ad95f.js b/pr/672/docs/search.index/76bc946ad95f.js new file mode 100644 index 000000000..b33cc5709 --- /dev/null +++ b/pr/672/docs/search.index/76bc946ad95f.js @@ -0,0 +1 @@ +rn_("AQYA9L4TAAABAAEAAQABDwD0phQAAAEAAQABAAEAAbDwFLAAAWs7MAAAAQAACAADABkUAAAeFAYASBQAAAECAPR1EAAAAQCzAAEA9wAAAIWxABSZAAGJMADy2hIAAAEAE4ACsBAMUwABZW8BBQA7MAAAAQAADQABAJwRDQABAgDzBxAAAAEAAQABAgD0AxAAAAEAAQABAAEDADswAAABAAAIAAEAuRAIAAGAAqAAAAAMJrAwFGYAAXFyAYACoEAAABEQsMAUXAABZXMBAQA7MAAAAQAAEwABACUQEwAjgAKwkBPGAAFpbXc2AACKoFAAABQnsHAUBwABoCAAAAzwnFUROzAAAAEAAAsABABWBwAA6hADALoSAQAqFAQAAQIBsDARdwABcAGhEgAAs4CEoCAAABHhAigE") \ No newline at end of file diff --git a/pr/672/docs/search.index/77dc9e44aee7.js b/pr/672/docs/search.index/77dc9e44aee7.js new file mode 100644 index 000000000..d17c65c27 --- /dev/null +++ b/pr/672/docs/search.index/77dc9e44aee7.js @@ -0,0 +1 @@ +rn_("AQUBsEASwwABZfLnEgAAAQABAAA7MAAAAQAAEQAEAAECAQC5EAgAthEDAMASAgABCgGwQBRmAAFl9SoUAAABAAEAAQABAEcGAACHsCARFAI8sMAUsAABoCAAAAb6sDAHWAABGIkKOjAAAAEAAAAAAAwAEAAAAFQHVQc/CR0MHgwkDCUMeAx5DMgMyQwWExcT") \ No newline at end of file diff --git a/pr/672/docs/search.index/780b49f04404.js b/pr/672/docs/search.index/780b49f04404.js new file mode 100644 index 000000000..a1a772cae --- /dev/null +++ b/pr/672/docs/search.index/780b49f04404.js @@ -0,0 +1 @@ +rn_("AQABoEAAABCtdPfzDAAARgPwAgEAuwCdAAEA8wABczswAAABAAAcAAgAtAQCACkMAwDwDwEAtxABAPgQAQDoEQEAphMHAJsUBQAjgAKxEBSiAAFhdA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/7811abcc9a5f.js b/pr/672/docs/search.index/7811abcc9a5f.js new file mode 100644 index 000000000..5b987d9de --- /dev/null +++ b/pr/672/docs/search.index/7811abcc9a5f.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgCOEwAA9hMFACOIArBAFGwAAWVzAQQBsDATXgABY/OGEQAAAQABAAEFADswAAABAAAIAAMA/BECAAITBAApFAAA8wMDY2Vw88sMAAABAAEAAQABsGAUDgABZQFHFAAA8wIBc/YjCQAAAQABACAIPwAiAQECAaBQAAARhXIBJhEAAPOAA2ZtcA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/7867cdb9af20.js b/pr/672/docs/search.index/7867cdb9af20.js new file mode 100644 index 000000000..b4124ec12 --- /dev/null +++ b/pr/672/docs/search.index/7867cdb9af20.js @@ -0,0 +1 @@ +rn_("AQUA9OgPAAABAPAAAQCDAISgQAAADPuwcBNxAAGwIAx4AAEQBQI7MAAAAQAACQADALsEAwB/BQIAzhECAAGAAqAgAAARJbAgFDwAAWR0AYQCsLAUjAABsAAT3gABYXPTgISgsAAAFHNAKAQ=") \ No newline at end of file diff --git a/pr/672/docs/search.index/7b4e167c9f1f.js b/pr/672/docs/search.index/7b4e167c9f1f.js new file mode 100644 index 000000000..ef4121924 --- /dev/null +++ b/pr/672/docs/search.index/7b4e167c9f1f.js @@ -0,0 +1 @@ +rn_("AQkBoIAAABS2ZgGYEwAAAQIA9dwQAAABAAEAXwIBAPMAAml1OjAAAAEAAAAAAAcAEAAAALYQ7xLeE98TDhQPFC8UMBQBCAD1QhQAAAEAAQABAAEA8wIBZvWaEAAAAQABAAEAAQATgAKwoBSMAAFydPMCAmFz88oQAAABAAEAAQQBsEAUZgABZfUqFAAAAQABAAEAAQABAgA7MAAAAQAACwACANQTBQCQFAUA8wMBbTswAAABAAANAAQA0AwAAG0RAADpEgUAThQFAPOBAmNl84ACYWU=") \ No newline at end of file diff --git a/pr/672/docs/search.index/7ca9a5d9b5a7.js b/pr/672/docs/search.index/7ca9a5d9b5a7.js new file mode 100644 index 000000000..ee2bfeb2f --- /dev/null +++ b/pr/672/docs/search.index/7ca9a5d9b5a7.js @@ -0,0 +1 @@ +rn_("AQIA9asRAAABAAEAAQABAAGAArBgExYAAaAgAAARAG95AQABoMAAABQ7afRvEwAACAAcABsAAQYA9KYUAAABAAEAAQABAgD0/hAAAAEAmgABAAECAaBAAAATRnI7MAAAAQAABwACAPwQAQCTEQUA84ACYW9nAwAAhrCAE0UAWrCgE7wAAQwwBjswAAABAAAIAAMAHAwAAMYRAwCsEgMA84ICZWkjAAKggAAAFLZmaPL6CAAAngoBBQDzHhMAAAEAAQABAQA7MAAAAQAABQACAP8LAwD2EQEAUwADsDAUVgABZXBy9OQPAAABAO8AAQABAAOwQBQcAAGhQAAAFIugUAAAEexiY2YBCAcAAPOAAm1uAQGEoLAAABPasEAQtwABsMAUCQABoGAAABGSFCAE9u8GAABgCQEAOgEuARcBAQUAOzAAAAEAAAUAAQDcEgUAAQcA9WYTAAABAAEAAQABACOAAqBQAAARY2Fv8wCFIEkCOzAAAAEAAAgABABNBwAAfgcFAAQNAACbEQAA") \ No newline at end of file diff --git a/pr/672/docs/search.index/7ce496ab7c6d.js b/pr/672/docs/search.index/7ce496ab7c6d.js new file mode 100644 index 000000000..9a7f53b0b --- /dev/null +++ b/pr/672/docs/search.index/7ce496ab7c6d.js @@ -0,0 +1 @@ +rn_("AYECsDAQPgABsBATvAABc3QBAwDzzRIAAAEAAQAjgAKwABHEAAFoaQEAADswAAABAAATAAEAhAcTACOAAqAwAAAM3WFl84ACcnTzAAJhbDswAAABAAAoAAMAmgATAK4BAAAlEBMA") \ No newline at end of file diff --git a/pr/672/docs/search.index/7d1520a0ac6f.js b/pr/672/docs/search.index/7d1520a0ac6f.js new file mode 100644 index 000000000..a3b118740 --- /dev/null +++ b/pr/672/docs/search.index/7d1520a0ac6f.js @@ -0,0 +1 @@ +rn_("AQgBoIAAABS2ZgGYEwAAE4ACoOAAABR5YnABAwGwABQxAFBz9ugPAAABAMQALAABAAADAYACsEASvAABoBAAABEYcHIBgQOgAAAAEYmwAA/PASugEAAAD8dlc3XzgANhb3IBAgD13BAAAAEAAQBfAgEAAQQAOzAAAAEAAAkAAQBOEQkAAQAA9L4RAAABAAEAAQABgAKgkAAAE/OwUBKkAAFid/OAAmR0o4CEsFAUDgABsJATMwABQCADVwMAAIagcAAAE+qgQAAAFF4wQQw7MAAAAQAAQgAhAMQEAQCIBQoAfQcAACYJAQAsCQAALQwBAFAMAgDmDAEA8wwAAJoOAAC4DwQAzw8AAPIPAQA5EAAAthAAAMQQAwDoEAEA+hAAACARAQCNEQAA4REAAOoRAQDvEQAA7xIAACkTAQDeEwEA5RMAAA4UAQAvFAEARxQAAIIUAQCzFAEAvhQBAA==") \ No newline at end of file diff --git a/pr/672/docs/search.index/7f66cf4dc275.js b/pr/672/docs/search.index/7f66cf4dc275.js new file mode 100644 index 000000000..4d8755d7d --- /dev/null +++ b/pr/672/docs/search.index/7f66cf4dc275.js @@ -0,0 +1 @@ +rn_("AYkCsCAUcQABoDAAABR7aXATAAOgIAAAEy2ggAAAE0JlaXX1EAwAADoAAQABAAEAAYYCsJAUlwABoAAAABK5YmQBAgD0GgwAAAEAjgMBAAEAAPQ3CQAAAQABAAEAAQAAOzAAAAEAAAYAAgCOEwAA9hMFACOBArBAFGwAAWVz84ADZXh5AQAA9DMJAAABAAEAAQAjgAKwIBMhAAFyeEcBAADGsMATugABoBAAABFxsEAM5gABoDAAABD4AESAC/RwBQAAAQB7BwEA1wAAAIWgMAAAEGygMAAAE3kQARM6MAAAAQAAAAAACgAQAAAA+wj8CP0ILQkuCR8MhxCOEEgRSRH9Eg==") \ No newline at end of file diff --git a/pr/672/docs/search.index/alias/a762150c532c.js b/pr/672/docs/search.index/alias/a762150c532c.js new file mode 100644 index 000000000..f829e4636 --- /dev/null +++ b/pr/672/docs/search.index/alias/a762150c532c.js @@ -0,0 +1 @@ +rd_("") \ No newline at end of file diff --git a/pr/672/docs/search.index/crateNames/2450c9cb4357.js b/pr/672/docs/search.index/crateNames/2450c9cb4357.js new file mode 100644 index 000000000..c00c15628 --- /dev/null +++ b/pr/672/docs/search.index/crateNames/2450c9cb4357.js @@ -0,0 +1 @@ +rd_("ebenchdbulkkcertificatemdata_transfercnoqhnoq_perfinoq_protognoq_udpdperfAaset_up_connection") \ No newline at end of file diff --git a/pr/672/docs/search.index/desc/5f4b62053dd2.js b/pr/672/docs/search.index/desc/5f4b62053dd2.js new file mode 100644 index 000000000..1f5dba18b --- /dev/null +++ b/pr/672/docs/search.index/desc/5f4b62053dd2.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.BmReturns the PathId of this path.AoGet the identity of this stream00CdThe path which can now be used for application data.AhWith path was abandoned.B`Which path had its state droppedAmPath which has changed statusCfPath over which the observed address was reported, \xe2\x80\xa643210AlWhich stream is now readableAlWhich stream is now writableAnWhich stream has been finishedAmWhich stream has been stoppedAnType of the Long header packetClWhether 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\xa61CdReturns the most recently observed external address.B`The current best RTT estimation.BfThe number of I/O operations executed.0BcLength 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 StreamIdBeCreates a new FourTuple.DjConstruct a state using the given config and current time \xe2\x80\xa60;9AeCreate a new endpointDcCreate a new instance of FixedLengthConnectionIdParserBdCreate a generator with a random key;:98DgBegin decoding a QUIC packet from bytes, returning any \xe2\x80\xa68BkConstruct an error with a code and a reasonCgInitialize Random CID generator with a fixed CID length98766BfQUIC transport protocol implementationCkGet SystemTime::now() or the mocked equivalentAhLook up the current time01ChCurrent best estimate of this connection\xe2\x80\x99s latency \xe2\x80\xa6000BcExperimental! Use at your own risk.0ChThe ECT(0) codepoint, indicating that an endpoint is \xe2\x80\xa600ChThe ECT(1) codepoint, indicating that an endpoint is \xe2\x80\xa600CaA complete set of keys for a certain packet space0CbA Long packet header, as used during the handshakeChNot attributed to any particular FrameType.CkAn open network transmission within a multipath-enabled \xe2\x80\xa6Ab(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 path0CjThe source address of the datagram(s) contained in the \xe2\x80\xa6mType of errorBlCurrent congestion window of the connection.0gPayloadAoReturns the argument unchanged.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000BaCalls U::from(self).000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dNextCoMethod for opening a sealed message data in-place.B`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 datagramBdMethod for sealing a token in-place.0CiQueue an unreliable, unordered datagram for immediate \xe2\x80\xa6CbSends a Transmit on the given socket.CmNumber of address validation tokens sent to a client when \xe2\x80\xa60BmThe side of the connection (client or server)ChLook up whether we\xe2\x80\x99re the client or server of this \xe2\x80\xa6AlMethod for signing a message0CdAmount of data written to the caller-supplied buffer0hSpin bitAcStop accepting data0BoStop accepting data on the given receive streamCiTry to find and take a token that was stored with the \xe2\x80\xa60BgA chunk of data from the receive stream0CiThe RFC8312 congestion controller, as widely used for TCP0Borustls reports protocol errors using this type.0DbErrors encountered while decoding TransportParametersCdErrors that the nat traversal state might encounter.BeEvents of interest to the applicationCiEvent emitted when the client receives ADD_ADDRESS or \xe2\x80\xa6CjAttributed to a specific FrameType, never \xe2\x80\xa6A`Any other error.0CcThe peer abandoned transmitting data on this streamCnThe peer is unable to continue processing this connection, \xe2\x80\xa6CdThe peer abandoned transmitting data on this stream.1AeA Retry packet headerCdA short packet header, as used during the data phaseCmBest-effort extraction of the ALPN protocols from the TLS \xe2\x80\xa60BiConstruct a fresh Controller0AiThe contents of the chunkCiThe total amount of bytes which have been transferred \xe2\x80\xa610BnUnderlying error describing reason for failureBaClose the connection immediately.CnClose all of this endpoint\xe2\x80\x99s connections immediately and \xe2\x80\xa6AaCloses this path.AnClose a connection immediatelyBcFrame 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 EndpointCaReturns the PathStats for this path.CgAggregate connection statistics at the moment of close.3BcTitle 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\xa60CeChunks returned from RecvStream::read().AmThe initiator of a connectionAdRun as a perf client1CoState of a Connection at the moment it was closed.CgAttempted to initiate NAT traversal on a closed, or \xe2\x80\xa6BcThe receiver lagged too far behind.CcFuture produced by Connection::open_biChOne 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 connectionAdRun as a perf server1mStream 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\xa61BlAllows 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 valueBeThe reason the connection was closed.BcHuman-readable reason for the close0BcReason why this path was abandoned.11BhHuman-readable explanation of the reasonBeReason that the connection was closed2AjSwitch to a new UDP socketBgReject this incoming connection attempt0BoReturns the remote address of the network path.AgKey for decrypting data10AmTLS 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 remote0CfThe size of a single datagram in the associated bufferCkStatistics about datagrams and bytes received on this path.CjStatistics about UDP datagrams received on the connection.10CgStatistics about datagrams and bytes sent on this path.CeStatistics about UDP datagrams transmitted on the \xe2\x80\xa610AnMethod for verifying a message0CcNumber of ack-eliciting bytes that may be in flight0A`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 streamCfFuture returned from SendStream::stopped.ClThe peer is no longer accepting data on this stream, and \xe2\x80\xa6CkThe peer is no longer accepting data on this stream. No \xe2\x80\xa6CgThe peer asked us to stop sending on an outgoing streamAaAccess to streamsCaThe stream is larger than the user-supplied limitCnAttributed to some frame type this implementation does not \xe2\x80\xa6l0-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 connectionBbDecrypt the Initial packet payloadCgDecrypt the packet payload with the given packet numberBcDecrypt the given packet\xe2\x80\x99s header210ClConstructs 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 header10CbCheck 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\xa60CjUniform interface to send and receive UDP packets with \xe2\x80\xa6BbOne 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 ConnectionB`Upgrades to a Path.AkSet 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\xa6CkWe didn\xe2\x80\x99t receive any data from the remote within the \xe2\x80\xa61ChResponsible 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.BnStatistics about frames received on this path.CcStatistics about frames received on the connection.10CaStatistics about frames transmitted on this path.CfStatistics about frames transmitted on the connection.10DhConstructs 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\xa60BjThe local IP used for this path, if known.BnReturns the local address of the network path.20ClCompute 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\xa600BmA path was abandoned and is no longer usable.0CfFuture produced by Connection::accept_uniCdPaths marked with as available will be used when \xe2\x80\xa60CnAt least one new stream of a certain directionality may be \xe2\x80\xa6BkThe connection was successfully establishedAoAPI to control datagram trafficAlDecoding of QUIC primitives.CkA path was discarded and all remaining state for it has \xe2\x80\xa60BgInfallible encoding of QUIC primitives.CnIdentifies a network path by the combination of remote and \xe2\x80\xa60A`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 closed0AnApplication events about paths0BjStatistics related to a transmission path.0AeA 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 state0BeThe number of UDP datagrams observed.AaControl 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 iBoLow-level protocol logic for the QUIC protoocolCjWait for the connection to be closed without keeping a \xe2\x80\xa6CjOpens an additional path if the multipath extension is \xe2\x80\xa6AaOpens a new path.BmPing 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 connections0CkWaits 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 timeCmConfiguration for the Bbr3 congestion controller0CkError indicating that a path has not been opened or has \xe2\x80\xa6BnThe path is already closed or was never opened1101BeIn-progress connection attempt futureAbA QUIC connection.CeProtocol state and logic for a single QUIC connectionCeCommon interface for different congestion controllers0CjNumber of frames transmitted or received of each frame \xe2\x80\xa60BoThe connection ID was not recognized by the \xe2\x80\xa60DdThe type used to refer to FrameTypes in closing and \xe2\x80\xa6DbA stream of PathEvents for all paths in a connection.AnThe QUIC-MULTIPATH path status0BeConfiguration 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\xa60CbReturns the number of bytes read from this stream.0CiCloses a path and sends a PATH_ABANDON frame with the \xe2\x80\xa6CdLogic for controlling the rate at which data is sent0BfWhether to print connection statisticsBnClass of error as encoded in the specificationB`Application-specific reason code10CaThe error code to be sent with the abandon frame.BoThe error that was sent with the abandon frame.AoError code supplied by the peerBcType of frame that caused the close0AiExtract the integer value0CnWhether 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 toBfThe number of bytes lost on this path.BkThe number of bytes lost on the connection.10CjPad UDP datagrams carrying application data to current \xe2\x80\xa60AgReturns path statisticsCjPer-path statistics for every path the connection knew \xe2\x80\xa6CjThe final path stats, they are no longer available via \xe2\x80\xa620C`Attempt to write bytes from buf into the stream.CoReads the next segment of data as zero-copy Bytes.AoReads 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 controller0CdA new path has established connection with the peer.0CdReasons 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.0AfA noq runtime for smolB`Application events about streamsCbAccept this incoming connection using a custom \xe2\x80\xa6CeLargest UDP payload size the path currently supports.ClStorage size required for the largest packet that can be \xe2\x80\xa61BiDescription to record in the qlog capture0BjThe socket this datagram should be sent to00DcCreates a new FourTuple without a known local address.0BkStarting 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)CiWhether this is the event is the event indicating the \xe2\x80\xa6CeThe amount of concurrent streams which should be usedDdPackets are acked in batches, all with the same now \xe2\x80\xa60AePacing rate (bytes/s)0DeA stream of PathEvents for all paths in this connection.DfGets the local PathStatus for a known PathIdCkConvenience 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.CdReturns a WeakPathHandle for this path.DgCreate a server config with the given crypto::ServerConfig0CeSets a prefix to the filename of the generated files.0DdWrites a single Bytes into this stream in its entirety.BbAn 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 invalidCkCannot close the last remaining open path via the local \xe2\x80\xa60Akn0\xe2\x80\x99s nat traversal eventsDhNull implementation of TokenLog, which never accepts tokens0ClReceived an observation of our external address from the \xe2\x80\xa60CiFuture produced by Connection::read_datagramBiThe remote changed the status of the path0BlRTT 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\xa6AgA noq runtime for TokioC`Returns the lifetime of generated Connection IDs0BjWhether the connection is closed, and 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\xa6000BhThe number of packets lost on this path.BmThe number of packets lost on the connection.10DcMaximum 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\xa6BdThe network path used for this path.CkReturns 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.0AgThe runtime type to useCkThe segment size if this transmission contains multiple \xe2\x80\xa600CkSend Quantum (bytes) used to control the size of packet \xe2\x80\xa60ChThe number of streams that may have unacknowledged data.CbSet the lifetime of CIDs created by this generator0BcSet the priority of the send streamAlSet the priority of a streamCnInitialize a QUIC-compatible TLS client configuration with \xe2\x80\xa6000AmSend 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 Initial0ChIterator over ALPN protocol names from a TLS ClientHelloAlThe packet header is invalidBkThe local application closed the connection0DaThe 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\xa610AkReturns 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 path0AgThe connection was lost00000ChGlobal configuration for the endpoint, affecting all \xe2\x80\xa60Cithe endpoint encountered an internal error and cannot \xe2\x80\xa60FbBasic adapter to let Incoming be await-ed like a ConnectingBhEncounter a frame ID that was not valid.CeThe peer sent us a TLS message with invalid contents.0Amno viable network path exists0DhNull implementation of TokenStore, which does not store \xe2\x80\xa60CiThe peer deviated from the standard TLS protocol. The \xe2\x80\xa60BmErrors that arise from reading from a stream.C`Errors from RecvStream::read_to_endCaIndicates whether a frame needs to be transmittedCmThe peer violated the QUIC specification as understood by \xe2\x80\xa60CiTransport-level errors occur when a peer violates the \xe2\x80\xa6C`Tokio-compatible UDP socket with some useful \xe2\x80\xa6DiWeak handle for a Path that does not keep the connection \xe2\x80\xa6BnCreate 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.DdReturns 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 update0AhOne packet was just lost0AhOne packet was just sent0BkConfigures 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 addressBgThe 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 ConnectionAfConnection statistics.0BiStatistics for the currently open streamsAcPlain packet headerBkEnables writing qlog traces to a directory.0AkThe remote closed the path.CfError for when a validation token may have been reused0C`Parameters governing the core QUIC state machine0CbThe peer doesn\xe2\x80\x99t implement any supported version0CdFuture that completes when a connection is fully \xe2\x80\xa6CbThis was a 0-RTT stream and the server rejected it000CjAutomatically select an appropriate runtime from those \xe2\x80\xa6CkWhether to accept QUIC packets containing any value for \xe2\x80\xa60CdMaximum number of incoming packets that may fail \xe2\x80\xa60CnThe interface index of the interface on which the datagram \xe2\x80\xa6AjSwitch to a new UDP socketCiCreates a server endpoint which runs on the given runtimeD`Sets the PathStatus for a known PathIdCfSee proto::TransportConfig::send_window()BoSee TransportConfig::send_window()BmReturns whether a frame should be transmittedD`Convert t into the socket type used by this runtimeCdWrites handshake bytes into the given buffer and \xe2\x80\xa60ClStream produced by Path::observed_external_addrCiReason given by an application for closing the connection0CbThe peer\xe2\x80\x99s QUIC stack aborted the connection \xe2\x80\xa60DjInternal identifier for a Connection currently associated \xe2\x80\xa6CdOne or more application datagrams have been receivedBjDecrypted payload of a QUIC Initial packet0CaThe 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 paths0ClThe 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 abandonedCmWe didn\xe2\x80\x99t receive a path response in time after opening \xe2\x80\xa61BcA 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 spontaneously0CnThe maximum amount of segments which can be transmitted if \xe2\x80\xa6CnMaximum duration of inactivity to accept before timing out \xe2\x80\xa60Cgn0\xe2\x80\x99s (https://n0.computer) NAT Traversal protocol \xe2\x80\xa6CeGet the number of connections that are currently openBmNumber of connections that are currently openDeOpens a new path if no path exists yet for network_path.CmOpens a new path only if no path on the same network path \xe2\x80\xa6CiMaximum reordering in packet number space before FACK \xe2\x80\xa60B`Reads the next segments of data.CfGet 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\xa60DfWrites a slice of Bytes into this stream in its entirety.Cfthe application or application protocol caused the \xe2\x80\xa60AnThe peer closed the connectionBoThe path was closed locally by the application.1B`Constructs controllers on demand0CnCommon congestion controller metrics used both for logging \xe2\x80\xa60ClThe trait for encrypting tokens that power retry packets \xe2\x80\xa60BcThe given server name was malformed0CbHints when the caller identifies a network change.0AcPacket decode errorBdReason for why a path was abandoned.BmErrors that can arise when sending a datagram0CcThe peer does not support receiving datagram frames0BdCongestion events on the connection.0AiCongestion 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\xa60CmWaits for all connections on the endpoint to be ready for \xe2\x80\xa6DgWrites Bytes from a slice of buffers into this stream, \xe2\x80\xa6Cmthe 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 status0AjTransport-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\xa6EgReturns true if the two WeakConnectionHandle point at the \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\xa6CnUpdates the set of ALPN protocols configured in the client \xe2\x80\xa6CnUpdates the set of ALPN protocols configured in the server \xe2\x80\xa610CiSee proto::TransportConfig::receive_window()CbSee TransportConfig::receive_window()B`Override supported QUIC versions0CiOverride the instant relative to which all events are \xe2\x80\xa60CcA stream of NAT traversal updates for a connection.BkNo remote CIDs available to open a new path0CkTransport 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.CgInform the connection if a path may recover after a \xe2\x80\xa60CgPeriod of inactivity before sending a keep-alive packet0CfThe number of PLPMTUD probe packets lost on this path.0BlPackets 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 SendBufferCfThe number of PLPMTUD probe packets sent on this path.0Bireceived 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\xa60CkThe remote address for the path is not supported by the \xe2\x80\xa6CiThe remote SocketAddr supplied was malformed10ChThe initial cipher suite (AES-128-GCM-SHA256) is not \xe2\x80\xa60CnWatches the external address reported by the peer for this \xe2\x80\xa6BdPaths can only be opened client-side0DmError 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\xa60CjThe number of times a black hole was detected in the path.0CgMaximum 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\xa60BmSets the max idle timeout 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\xa60CiNotify connections that the local network address has \xe2\x80\xa6ChHandle a change in the local address, i.e. an active \xe2\x80\xa6CiCounter for the number of bytes currently used in the \xe2\x80\xa6CkReduction in congestion window when a new loss event is \xe2\x80\xa60DdMaximum number of datagrams that a Transmit may encode.0CfA stream of NAT traversal updates for this connection.CjGet 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 peerAkMultipath is not negotiated210CnError when the multipath extension was not negotiated, but \xe2\x80\xa6BnHeader of an Initial packet, before decryptionCgDerive keying material from this connection\xe2\x80\x99s TLS \xe2\x80\xa6EdFill output with output.len() bytes of keying material \xe2\x80\xa60BnPathological case: many segments, get from endCfGet segments in the old way, using a loop of get callsClCurrent number of remotely initiated streams that may be \xe2\x80\xa6ClTrack changes on our external address as reported by the \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.CbThe peer\xe2\x80\x99s ACK-frequency parameters have changed0C`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 connectBnSets the max_idle_timeout for a specific path.BfThe QUIC protocol version implemented.CfThe path became unusable after a local network change.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\xa6BmSpurious congestion events on the connection.0CnCreate 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\xa60ClMaximum number of incoming application datagram bytes to \xe2\x80\xa60BcInitiates a new nat traversal round0BdPackets were incorrectly deemed lostCaequivalent to BBRHandleSpuriousLossDetection: \xe2\x80\xa610CnRemoves 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 timeout0CkConfigures an outbound rate limit (in bytes per second) \xe2\x80\xa60CkThe key exchange group negotiated with the peer. Useful \xe2\x80\xa60CbWhether 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\xa60CkSets the maximum number of nat traversal addresses this \xe2\x80\xa60AcNat traversal draft") \ No newline at end of file diff --git a/pr/672/docs/search.index/entry/843e82bff23e.js b/pr/672/docs/search.index/entry/843e82bff23e.js new file mode 100644 index 000000000..475c592ce --- /dev/null +++ b/pr/672/docs/search.index/entry/843e82bff23e.js @@ -0,0 +1 @@ +rd_("Af[477,15,478,0,133,0,0]Ak[3173,15,3174,3174,134,0,0]Ag[477,15,478,0,4208,0,0]Al[3173,15,3174,1829,4209,0,0]Al[2029,15,2030,2030,4210,0,0]Al[3173,15,1731,1731,1470,0,0]Ao[1228,13,1229,1229,4047,3111,0]Am[477,13,478,3740,4898,3111,0]Am[477,13,478,4270,1458,3111,0]Al[477,13,478,1126,520,3111,0]Am[477,13,478,1126,4491,3111,0]Am[477,13,478,4157,4484,3111,0]Am[477,13,478,4157,3121,3111,0]Am[477,13,478,4157,3324,3111,0]Am[477,13,478,4157,4483,3111,0]Am[477,13,478,4162,3342,3111,0]Am[477,13,478,4162,4248,3111,0]Aj[477,13,478,0,1480,3111,0]Aj[477,13,478,0,5069,3111,0]Aj[477,13,478,0,1184,3111,0]Aj[477,13,478,0,1463,3111,0]Aj[477,13,478,0,3317,3111,0]Aj[477,13,478,0,3116,3111,0]Aj[477,13,478,0,4954,3111,0]Aj[477,13,478,0,3290,3111,0]Aj[477,13,478,0,2343,3111,0]Aj[477,13,478,0,3303,3111,0]Aj[477,13,478,0,3118,3111,0]Aj[477,13,478,0,4194,3111,0]Aj[477,13,478,0,4583,3111,0]Aj[477,13,478,0,3112,3111,0]Aj[477,13,478,0,4450,3111,0]Aj[477,13,478,0,4064,3111,0]Aj[477,13,478,0,4050,3111,0]Aj[477,13,478,0,3099,3111,0]Aj[477,13,478,0,4580,3111,0]Aj[477,13,478,0,4787,3111,0]Aj[477,13,478,0,4200,3111,0]Aj[477,13,478,0,4203,3111,0]Aj[477,13,478,0,4208,3111,0]Aj[477,13,478,0,4959,3111,0]Ai[477,13,478,0,526,3111,0]Ai[477,13,478,0,133,3111,0]Aj[477,13,478,0,2330,3111,0]Aj[477,13,478,0,3097,3111,0]Ak[477,13,1735,0,5223,3111,0]Ak[477,13,1818,0,1201,3111,0]Ao[3173,13,3174,1856,1481,3111,0]Ao[3173,13,3174,1856,5070,3111,0]Ao[3173,13,3174,3138,1185,3111,0]Ao[3173,13,3174,3147,4899,3111,0]Ao[3173,13,3174,1403,1464,3111,0]Ao[3173,13,3174,1403,3318,3111,0]Ao[3173,13,3174,1403,3117,3111,0]Ao[3173,13,3174,1403,4897,3111,0]Ao[3173,13,3174,1403,4955,3111,0]Ao[3173,13,3174,1403,3293,3111,0]Ao[3173,13,3174,1419,2344,3111,0]Ao[3173,13,3174,1419,3304,3111,0]Ao[3173,13,3174,1419,3119,3111,0]Ao[3173,13,3174,1152,3122,3111,0]Ao[3173,13,3174,1152,4374,3111,0]Ao[3173,13,3174,1157,3343,3111,0]Ao[3173,13,3174,1157,4063,3111,0]Ao[3173,13,3174,2044,4082,3111,0]Ao[3173,13,3174,2044,4485,3111,0]Ao[3173,13,3174,2044,4198,3111,0]Ao[3173,13,3174,3741,4584,3111,0]Ao[3173,13,3174,3741,3113,3111,0]Ao[3173,13,3174,3741,4451,3111,0]Ao[3173,13,3174,3276,4065,3111,0]Ao[3173,13,3174,1733,4051,3111,0]Ao[3173,13,3174,1393,3100,3111,0]Ao[3173,13,3174,1393,4466,3111,0]Ao[3173,13,3174,1393,3310,3111,0]Ao[3173,13,3174,1393,3332,3111,0]Ao[3173,13,3174,1393,4205,3111,0]Ao[3173,13,3174,1393,4582,3111,0]Ao[3173,13,3174,1393,4789,3111,0]Ao[3173,13,3174,2354,4793,3111,0]Ao[3173,13,3174,2354,4201,3111,0]Ao[3173,13,3174,1798,2316,3111,0]Ao[3173,13,3174,1798,4894,3111,0]Ao[3173,13,3174,1829,4204,3111,0]Ao[3173,13,3174,1829,4209,3111,0]Al[3173,13,3174,0,4489,3111,0]Al[3173,13,3174,0,4960,3111,0]An[3173,13,3174,3174,527,3111,0]An[3173,13,3174,3174,134,3111,0]Ao[3173,13,3174,3174,2331,3111,0]Ao[3173,13,3174,3174,3098,3111,0]Ao[3173,13,1731,1731,4380,3111,0]Ao[3173,13,5111,5111,5007,3111,0]Ao[3173,13,5111,5111,1203,3111,0]Ao[3173,13,1736,1736,5224,3111,0]Al[3173,13,1819,0,1202,3111,0]Ao[2029,13,2030,2030,4210,3111,0]Ai[477,13,478,1126,520,0,0]Aj[477,13,478,1126,4491,0,0]Aj[477,13,478,4157,3322,0,0]Aj[477,13,478,4157,5008,0,0]Aj[477,13,478,4162,3328,0,0]Ah[477,14,3114,0,4059,0,0]Ah[477,14,3114,0,3072,0,0]Ah[477,14,3114,0,3091,0,0]Ah[477,14,3114,0,4233,0,0]Ah[477,14,3114,0,4224,0,0]Ai[3173,14,3115,0,4060,0,0]Ai[3173,14,3115,0,3073,0,0]Ai[3173,14,3115,0,3092,0,0]Ai[3173,14,3115,0,4234,0,0]Ai[3173,14,3115,0,4225,0,0]Ai[3173,14,4081,0,2326,0,0]Ai[3173,14,4081,0,2346,0,0]Ai[3173,14,4081,0,2310,0,0]Ai[3173,14,4081,0,1901,0,0]Ae[1228,7,1229,0,0,0,0]Ah[3173,14,4595,0,516,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]Ae[2408,5,2409,0,0,0,0]Af[477,6,478,3174,0,0,0]Ae[3173,6,3174,0,0,0,0]Al[3173,15,1731,1731,1470,0,0]Ag[477,19,478,0,1480,0,0]Ag[477,19,478,0,1463,0,0]Al[3173,19,3174,1856,1481,0,0]Al[3173,19,3174,1403,1464,0,0]Ae[1228,5,1229,0,0,0,0]Ae[1127,5,1725,0,0,0,0]Ae[1127,5,1824,0,0,0,0]Af[477,15,478,0,133,0,0]Ak[3173,15,3174,3174,134,0,0]Ai[477,13,478,0,2343,127,0]Ai[477,13,478,0,3303,127,0]Ai[477,13,478,0,4587,127,0]An[3173,13,3174,1419,2344,127,0]An[3173,13,3174,1419,3304,127,0]An[3173,13,3174,1419,4588,127,0]Ai[477,13,478,0,1480,144,0]Ai[477,13,478,0,1463,144,0]Ai[477,13,478,0,4064,144,0]Ai[477,13,478,0,4203,144,0]Ah[477,13,478,0,526,144,0]Ah[477,13,478,0,133,144,0]Ai[477,13,478,0,2330,144,0]An[3173,13,3174,1856,1481,144,0]An[3173,13,3174,3147,4899,144,0]An[3173,13,3174,1403,1464,144,0]An[3173,13,3174,1152,3122,144,0]An[3173,13,3174,1152,4374,144,0]An[3173,13,3174,1157,3343,144,0]An[3173,13,3174,3276,4065,144,0]An[3173,13,3174,2354,4793,144,0]An[3173,13,3174,1798,4894,144,0]An[3173,13,3174,1829,4204,144,0]Am[3173,13,3174,3174,527,144,0]Am[3173,13,3174,3174,134,144,0]An[3173,13,3174,3174,2331,144,0]Ag[477,13,478,0,2330,0,0]Al[3173,13,3174,3174,2331,0,0]Ai[3173,14,4081,0,1460,0,0]Ai[3173,14,4081,0,3081,0,0]Ag[477,14,478,0,2340,0,0]Al[3173,14,3174,3174,2341,0,0]Al[2029,14,2030,2030,2328,0,0]Al[2029,14,2030,2030,2342,0,0]An[1228,13,1229,1229,141,1191,0]Ao[1228,13,1229,1229,4075,1191,0]Ao[1228,13,1229,1229,4047,1191,0]Ao[1228,13,1417,1417,4486,1191,0]Ao[4088,13,4089,4089,5161,1191,0]Am[477,13,478,4270,5065,1191,0]Am[477,13,478,3740,3295,1191,0]Am[477,13,478,3740,3297,1191,0]Am[477,13,478,3740,1450,1191,0]Am[477,13,478,3740,5072,1191,0]Db[477,13,478,3740,4898,1873,0,\"impl-Display-for-SendDatagramError\"]D`[477,13,478,3740,4898,1191,0,\"impl-Debug-for-SendDatagramError\"]Am[477,13,478,2353,2308,1191,0]Am[477,13,478,2353,4347,1191,0]Cg[477,13,478,4270,1458,1873,0,\"impl-Display-for-Lagged\"]Ce[477,13,478,4270,1458,1191,0,\"impl-Debug-for-Lagged\"]Am[477,13,478,4270,3316,1191,0]Am[477,13,478,4270,5000,1191,0]Am[477,13,478,2385,2312,1191,0]Ck[477,13,478,2385,3325,1873,0,\"impl-Display-for-RetryError\"]Ci[477,13,478,2385,3325,1191,0,\"impl-Debug-for-RetryError\"]Am[477,13,478,2385,4465,1191,0]Al[477,13,478,1126,520,1191,0]Am[477,13,478,1126,4491,1191,0]Am[477,13,478,4157,3322,1191,0]Am[477,13,478,4157,5008,1191,0]Cm[477,13,478,4157,4484,1191,0,\"impl-Debug-for-ReadToEndError\"]Co[477,13,478,4157,4484,1873,0,\"impl-Display-for-ReadToEndError\"]Cj[477,13,478,4157,3121,1873,0,\"impl-Display-for-ReadError\"]Ch[477,13,478,4157,3121,1191,0,\"impl-Debug-for-ReadError\"]Ci[477,13,478,4157,3324,1191,0,\"impl-Debug-for-ResetError\"]Ck[477,13,478,4157,3324,1873,0,\"impl-Display-for-ResetError\"]Cm[477,13,478,4157,4483,1191,0,\"impl-Debug-for-ReadExactError\"]Co[477,13,478,4157,4483,1873,0,\"impl-Display-for-ReadExactError\"]Am[477,13,478,1431,4249,1191,0]Am[477,13,478,1169,4078,1191,0]Am[477,13,478,4162,3328,1191,0]Ck[477,13,478,4162,3342,1873,0,\"impl-Display-for-WriteError\"]Ci[477,13,478,4162,3342,1191,0,\"impl-Debug-for-WriteError\"]Cm[477,13,478,4162,4248,1873,0,\"impl-Display-for-StoppedError\"]Ck[477,13,478,4162,4248,1191,0,\"impl-Debug-for-StoppedError\"]Am[477,13,478,4162,1903,1191,0]Cd[477,13,478,0,1480,1873,0,\"impl-Display-for-VarInt\"]Cb[477,13,478,0,1480,1191,0,\"impl-Debug-for-VarInt\"]Db[477,13,478,0,5069,1873,0,\"impl-Display-for-VarIntBoundsExceeded\"]D`[477,13,478,0,5069,1191,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Aj[477,13,478,0,1184,1191,0]Cd[477,13,478,0,1463,1873,0,\"impl-Display-for-PathId\"]Cb[477,13,478,0,1463,1191,0,\"impl-Debug-for-PathId\"]Aj[477,13,478,0,3317,1191,0]Aj[477,13,478,0,3116,1191,0]Cn[477,13,478,0,4954,1191,0,\"impl-Debug-for-SetPathStatusError\"]D`[477,13,478,0,4954,1873,0,\"impl-Display-for-SetPathStatusError\"]Cf[477,13,478,0,3290,1191,0,\"impl-Debug-for-ClosedPath\"]Ch[477,13,478,0,3290,1873,0,\"impl-Display-for-ClosedPath\"]Aj[477,13,478,0,2343,1191,0]Aj[477,13,478,0,3303,1191,0]Aj[477,13,478,0,3118,1191,0]Aj[477,13,478,0,4587,1191,0]Ch[477,13,478,0,4194,1191,0,\"impl-Debug-for-ClosedStream\"]Cj[477,13,478,0,4194,1873,0,\"impl-Display-for-ClosedStream\"]Cm[477,13,478,0,4583,1873,0,\"impl-Display-for-ConnectionError\"]Ck[477,13,478,0,4583,1191,0,\"impl-Debug-for-ConnectionError\"]Cg[477,13,478,0,3112,1873,0,\"impl-Display-for-PathError\"]Ce[477,13,478,0,3112,1191,0,\"impl-Debug-for-PathError\"]Cl[477,13,478,0,4450,1873,0,\"impl-Display-for-ClosePathError\"]Cj[477,13,478,0,4450,1191,0,\"impl-Debug-for-ClosePathError\"]Aj[477,13,478,0,4597,1191,0]Aj[477,13,478,0,4606,1191,0]Aj[477,13,478,0,4924,1191,0]Aj[477,13,478,0,4939,1191,0]Aj[477,13,478,0,4064,1191,0]Aj[477,13,478,0,4460,1191,0]Aj[477,13,478,0,4242,1191,0]Aj[477,13,478,0,5126,1191,0]Aj[477,13,478,0,4188,1191,0]Cg[477,13,478,0,4050,1191,0,\"impl-Debug-for-ConfigError\"]Ci[477,13,478,0,4050,1873,0,\"impl-Display-for-ConfigError\"]Cg[477,13,478,0,3099,1873,0,\"impl-Display-for-FrameType\"]Ce[477,13,478,0,3099,1191,0,\"impl-Debug-for-FrameType\"]Cm[477,13,478,0,4580,1873,0,\"impl-Display-for-ConnectionClose\"]Ck[477,13,478,0,4580,1191,0,\"impl-Debug-for-ConnectionClose\"]Cn[477,13,478,0,4787,1873,0,\"impl-Display-for-ApplicationClose\"]Cl[477,13,478,0,4787,1191,0,\"impl-Debug-for-ApplicationClose\"]Cj[477,13,478,0,4200,1873,0,\"impl-Display-for-ConnectError\"]Ch[477,13,478,0,4200,1191,0,\"impl-Debug-for-ConnectError\"]Cj[477,13,478,0,4203,1873,0,\"impl-Display-for-ConnectionId\"]Ch[477,13,478,0,4203,1191,0,\"impl-Debug-for-ConnectionId\"]Aj[477,13,478,0,4208,1191,0]D`[477,13,478,0,4959,1873,0,\"impl-Display-for-TransportErrorCode\"]Cn[477,13,478,0,4959,1191,0,\"impl-Debug-for-TransportErrorCode\"]Aj[477,13,478,0,3306,1191,0]Aj[477,13,478,0,4821,1191,0]Ai[477,13,478,0,526,1191,0]C`[477,13,478,0,133,1873,0,\"impl-Display-for-Dir\"]Bn[477,13,478,0,133,1191,0,\"impl-Debug-for-Dir\"]Cf[477,13,478,0,2330,1873,0,\"impl-Display-for-StreamId\"]Cd[477,13,478,0,2330,1191,0,\"impl-Debug-for-StreamId\"]Aj[477,13,478,0,2340,1191,0]Cg[477,13,478,0,3097,1873,0,\"impl-Display-for-FourTuple\"]Ce[477,13,478,0,3097,1191,0,\"impl-Debug-for-FourTuple\"]Aj[477,13,3735,0,501,1191,0]Ak[477,13,3735,0,3286,1191,0]Ak[477,13,3735,0,1188,1191,0]Ak[477,13,3735,0,4055,1191,0]Ak[477,13,3735,0,1886,1191,0]Ak[477,13,3735,0,4366,1191,0]Ak[477,13,1735,0,5223,1191,0]Ak[477,13,1735,0,4053,1191,0]Ak[477,13,1735,0,4963,1191,0]Cd[477,13,1818,0,1201,1873,0,\"impl-Display-for-Error\"]Cb[477,13,1818,0,1201,1191,0,\"impl-Debug-for-Error\"]Da[477,13,1818,0,5063,1191,0,\"impl-Debug-for-NoInitialCipherSuite\"]Dc[477,13,1818,0,5063,1873,0,\"impl-Display-for-NoInitialCipherSuite\"]Ao[3173,13,3174,3741,3298,1191,0]Ao[3173,13,3174,2354,2309,1191,0]Cg[3173,13,3174,1856,1481,1191,0,\"impl-Debug-for-VarInt\"]Ci[3173,13,3174,1856,1481,1873,0,\"impl-Display-for-VarInt\"]Dg[3173,13,3174,1856,5070,1873,0,\"impl-Display-for-VarIntBoundsExceeded\"]De[3173,13,3174,1856,5070,1191,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Ao[3173,13,3174,3138,1185,1191,0]Dd[3173,13,3174,3147,4899,1873,0,\"impl-Display-for-SendDatagramError\"]Db[3173,13,3174,3147,4899,1191,0,\"impl-Debug-for-SendDatagramError\"]Ci[3173,13,3174,1403,1464,1873,0,\"impl-Display-for-PathId\"]Cg[3173,13,3174,1403,1464,1191,0,\"impl-Debug-for-PathId\"]Ao[3173,13,3174,1403,4238,1191,0]Ao[3173,13,3174,1403,3318,1191,0]Ao[3173,13,3174,1403,3117,1191,0]Ao[3173,13,3174,1403,4897,1191,0]Dc[3173,13,3174,1403,4955,1191,0,\"impl-Debug-for-SetPathStatusError\"]De[3173,13,3174,1403,4955,1873,0,\"impl-Display-for-SetPathStatusError\"]Ck[3173,13,3174,1403,3293,1191,0,\"impl-Debug-for-ClosedPath\"]Cm[3173,13,3174,1403,3293,1873,0,\"impl-Display-for-ClosedPath\"]Ao[3173,13,3174,1419,2344,1191,0]Ao[3173,13,3174,1419,3304,1191,0]Ao[3173,13,3174,1419,3119,1191,0]Ao[3173,13,3174,1419,4588,1191,0]Cj[3173,13,3174,1152,3122,1191,0,\"impl-Debug-for-ReadError\"]Cl[3173,13,3174,1152,3122,1873,0,\"impl-Display-for-ReadError\"]D`[3173,13,3174,1152,4374,1873,0,\"impl-Display-for-ReadableError\"]Cn[3173,13,3174,1152,4374,1191,0,\"impl-Debug-for-ReadableError\"]Ck[3173,13,3174,1157,3343,1191,0,\"impl-Debug-for-WriteError\"]Cm[3173,13,3174,1157,3343,1873,0,\"impl-Display-for-WriteError\"]Cl[3173,13,3174,1157,4063,1191,0,\"impl-Debug-for-FinishError\"]Cn[3173,13,3174,1157,4063,1873,0,\"impl-Display-for-FinishError\"]Ao[3173,13,3174,2044,4082,1191,0]Ao[3173,13,3174,2044,4485,1191,0]Cm[3173,13,3174,2044,4198,1191,0,\"impl-Debug-for-ClosedStream\"]Co[3173,13,3174,2044,4198,1873,0,\"impl-Display-for-ClosedStream\"]Db[3173,13,3174,3741,4584,1873,0,\"impl-Display-for-ConnectionError\"]D`[3173,13,3174,3741,4584,1191,0,\"impl-Debug-for-ConnectionError\"]Cl[3173,13,3174,3741,3113,1873,0,\"impl-Display-for-PathError\"]Cj[3173,13,3174,3741,3113,1191,0,\"impl-Debug-for-PathError\"]Da[3173,13,3174,3741,4451,1873,0,\"impl-Display-for-ClosePathError\"]Co[3173,13,3174,3741,4451,1191,0,\"impl-Debug-for-ClosePathError\"]Dg[3173,13,3174,3741,5157,1191,0,\"impl-Debug-for-MultipathNotNegotiated\"]Di[3173,13,3174,3741,5157,1873,0,\"impl-Display-for-MultipathNotNegotiated\"]Ao[3173,13,3174,3741,1206,1191,0]Ao[3173,13,3174,1148,4598,1191,0]Ao[3173,13,3174,3276,4607,1191,0]Ao[3173,13,3174,3276,4925,1191,0]Ao[3173,13,3174,3276,4940,1191,0]Ao[3173,13,3174,3276,4065,1191,0]Ao[3173,13,3174,1733,4461,1191,0]Ao[3173,13,3174,1733,4244,1191,0]Ao[3173,13,3174,1733,5127,1191,0]Ao[3173,13,3174,1733,4190,1191,0]Cn[3173,13,3174,1733,4051,1873,0,\"impl-Display-for-ConfigError\"]Cl[3173,13,3174,1733,4051,1191,0,\"impl-Debug-for-ConfigError\"]Cl[3173,13,3174,1393,3100,1873,0,\"impl-Display-for-FrameType\"]Cj[3173,13,3174,1393,3100,1191,0,\"impl-Debug-for-FrameType\"]Da[3173,13,3174,1393,4466,1873,0,\"impl-Display-for-InvalidFrameId\"]Co[3173,13,3174,1393,4466,1191,0,\"impl-Debug-for-InvalidFrameId\"]Ck[3173,13,3174,1393,3310,1191,0,\"impl-Debug-for-MaybeFrame\"]Cm[3173,13,3174,1393,3310,1873,0,\"impl-Display-for-MaybeFrame\"]Cm[3173,13,3174,1393,3332,1873,0,\"impl-Display-for-StreamInfo\"]Ck[3173,13,3174,1393,3332,1191,0,\"impl-Debug-for-StreamInfo\"]Co[3173,13,3174,1393,4205,1873,0,\"impl-Display-for-DatagramInfo\"]Cm[3173,13,3174,1393,4205,1191,0,\"impl-Debug-for-DatagramInfo\"]D`[3173,13,3174,1393,4582,1191,0,\"impl-Debug-for-ConnectionClose\"]Db[3173,13,3174,1393,4582,1873,0,\"impl-Display-for-ConnectionClose\"]Da[3173,13,3174,1393,4789,1191,0,\"impl-Debug-for-ApplicationClose\"]Dc[3173,13,3174,1393,4789,1873,0,\"impl-Display-for-ApplicationClose\"]Ci[3173,13,3174,1393,2302,1191,0,\"impl-Debug-for-Datagram\"]Ck[3173,13,3174,1393,2302,1873,0,\"impl-Display-for-Datagram\"]Ao[3173,13,3174,2354,4793,1191,0]Ao[3173,13,3174,2354,2313,1191,0]Co[3173,13,3174,2354,4201,1873,0,\"impl-Display-for-ConnectError\"]Cm[3173,13,3174,2354,4201,1191,0,\"impl-Debug-for-ConnectError\"]Ao[3173,13,3174,2354,4045,1191,0]Ck[3173,13,3174,2354,3326,1191,0,\"impl-Debug-for-RetryError\"]Cm[3173,13,3174,2354,3326,1873,0,\"impl-Display-for-RetryError\"]Ao[3173,13,3174,1798,4369,1191,0]Ao[3173,13,3174,1798,4596,1191,0]Ao[3173,13,3174,1798,5160,1191,0]Ao[3173,13,3174,1798,2316,1191,0]Db[3173,13,3174,1798,4894,1191,0,\"impl-Debug-for-PacketDecodeError\"]Dd[3173,13,3174,1798,4894,1873,0,\"impl-Display-for-PacketDecodeError\"]Ao[3173,13,3174,1829,4586,1191,0]Ao[3173,13,3174,1829,4346,1191,0]Cm[3173,13,3174,1829,4204,1191,0,\"impl-Debug-for-ConnectionId\"]Co[3173,13,3174,1829,4204,1873,0,\"impl-Display-for-ConnectionId\"]Ao[3173,13,3174,1829,4209,1191,0]Ce[3173,13,3174,0,4489,1873,0,\"impl-Display-for-Error\"]Cc[3173,13,3174,0,4489,1191,0,\"impl-Debug-for-Error\"]Cb[3173,13,3174,0,4960,1191,0,\"impl-Debug-for-Code\"]Cd[3173,13,3174,0,4960,1873,0,\"impl-Display-for-Code\"]Ao[3173,13,3174,4387,3307,1191,0]Ao[3173,13,3174,4387,5260,1191,0]Ao[3173,13,3174,4995,4822,1191,0]An[3173,13,3174,3174,527,1191,0]Cc[3173,13,3174,3174,134,1191,0,\"impl-Debug-for-Dir\"]Ce[3173,13,3174,3174,134,1873,0,\"impl-Display-for-Dir\"]Ci[3173,13,3174,3174,2331,1191,0,\"impl-Debug-for-StreamId\"]Ck[3173,13,3174,3174,2331,1873,0,\"impl-Display-for-StreamId\"]Ao[3173,13,3174,3174,2341,1191,0]Cj[3173,13,3174,3174,3098,1191,0,\"impl-Debug-for-FourTuple\"]Cl[3173,13,3174,3174,3098,1873,0,\"impl-Display-for-FourTuple\"]D`[3173,13,1731,1731,4380,1873,0,\"impl-Display-for-UnexpectedEnd\"]Cn[3173,13,1731,1731,4380,1191,0,\"impl-Debug-for-UnexpectedEnd\"]Ao[3173,13,5111,5111,5007,1191,0]Ch[3173,13,5111,5111,1203,1873,0,\"impl-Display-for-Error\"]Cf[3173,13,5111,5111,1203,1191,0,\"impl-Debug-for-Error\"]Ao[3173,13,1736,1736,5224,1191,0]Ao[3173,13,1736,1736,4054,1191,0]Ao[3173,13,1736,1736,4966,1191,0]Cc[3173,13,1819,0,1202,1191,0,\"impl-Debug-for-Error\"]Ce[3173,13,1819,0,1202,1873,0,\"impl-Display-for-Error\"]De[3173,13,1819,1819,5064,1191,0,\"impl-Debug-for-NoInitialCipherSuite\"]Dg[3173,13,1819,1819,5064,1873,0,\"impl-Display-for-NoInitialCipherSuite\"]Am[3173,13,3736,535,502,1191,0]An[3173,13,3736,535,3287,1191,0]Ao[3173,13,3736,1389,1189,1191,0]Ao[3173,13,3736,1389,4056,1191,0]Ao[3173,13,3736,2408,1887,1191,0]Ao[3173,13,3736,2408,4367,1191,0]Cf[3173,13,4853,4853,1204,1191,0,\"impl-Debug-for-Error\"]Ch[3173,13,4853,4853,1204,1873,0,\"impl-Display-for-Error\"]Ao[3173,13,4853,4853,1207,1191,0]An[2029,13,2030,431,4490,1191,0]Ao[2029,13,2030,2030,2328,1191,0]Ao[2029,13,2030,2030,2342,1191,0]Ao[2029,13,2030,2030,4210,1191,0]Aj[477,13,478,4270,5065,0,0]Al[3173,13,3174,1403,4238,0,0]Ag[477,14,478,0,2343,0,0]Al[3173,14,3174,1419,2344,0,0]Al[3173,13,3174,1798,4369,0,0]Al[3173,14,3174,1798,5160,0,0]Ah[3173,14,4595,0,516,0,0]Al[2029,14,2030,2030,2328,0,0]Ag[477,13,478,0,5126,0,0]Al[3173,13,3174,1733,5127,0,0]8Al[1228,13,1417,1417,4486,0,0]Al[4088,13,4089,4089,5161,0,0]Ag[477,13,478,0,4338,0,0]Aj[477,13,478,2353,2308,0,0]Ag[477,13,478,0,3319,0,0]Ag[477,13,478,0,4597,0,0]Ag[477,13,478,0,4460,0,0]Ag[477,13,478,0,4242,0,0]Ag[477,13,478,0,4188,0,0]Ag[477,13,478,0,4203,0,0]Ag[477,13,478,0,4821,0,0]Ag[477,13,478,0,2330,0,0]Ag[477,13,478,0,3097,0,0]Ah[477,13,3735,0,1188,0,0]Ah[477,13,3735,0,1886,0,0]Al[3173,13,3174,4616,4339,0,0]Al[3173,13,3174,1148,3320,0,0]Al[3173,13,3174,2354,2309,0,0]Al[3173,13,3174,1798,5286,0,0]Al[3173,13,3174,4387,5255,0,0]Al[3173,13,3174,1148,4598,0,0]Al[3173,13,3174,1733,4461,0,0]Al[3173,13,3174,1733,4244,0,0]Al[3173,13,3174,1733,4190,0,0]Al[3173,13,3174,1798,4369,0,0]Al[3173,13,3174,1829,4204,0,0]Ai[3173,13,3174,0,4489,0,0]Al[3173,13,3174,4387,5260,0,0]Al[3173,13,3174,4995,4822,0,0]Al[3173,13,3174,3174,2331,0,0]Al[3173,13,3174,3174,3098,0,0]Al[3173,13,3736,1389,1189,0,0]Al[3173,13,3736,2408,1887,0,0]Ak[2029,13,2030,431,4490,0,0]Al[1127,13,4305,4305,5209,0,0]Al[1127,13,4305,4305,5210,0,0]Aa[477,3,0,0,0,0,0]Ah[477,13,478,0,526,140,0]Am[3173,13,3174,3174,527,140,0]Ag[477,12,478,0,3335,0,0]Aj[477,13,478,2036,1898,0,0]0Am[477,13,478,1431,4249,1898,0]Aj[477,13,478,0,4378,3335,0]Al[3173,12,3174,1733,3336,0,0]Ao[3173,13,3174,1733,4379,3336,0]Aj[477,13,478,3740,3297,0,0]Ag[477,14,478,0,3118,0,0]Al[3173,13,3174,3741,3298,0,0]Al[3173,14,3174,1419,3119,0,0]Ae[1127,7,1725,0,0,0,0]Ae[1127,7,1824,0,0,0,0]Ac[477,4,478,0,0,0,0]Af[477,5,3735,535,0,0,0]Ag[3173,5,3736,535,0,0,0]Al[1127,15,1128,1128,4999,0,0]Ag[477,15,478,0,4208,0,0]Al[3173,15,3174,1829,4209,0,0]Al[2029,15,2030,2030,4210,0,0]210Ag[477,5,1735,1736,0,0,0]Ae[3173,5,1736,0,0,0,0]Al[3173,15,3174,1798,4596,0,0]Al[3173,15,3174,1393,3310,0,0]Af[477,5,478,1126,0,0,0]Al[3173,15,3174,3741,1206,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]Aj[477,15,478,4157,4484,0,0]Af[477,6,478,3174,0,0,0]Ae[3173,6,3174,0,0,0,0]Ag[477,19,478,0,1463,0,0]Al[3173,19,3174,1403,1464,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]Ah[477,14,3114,0,4224,0,0]Ai[3173,14,3115,0,4225,0,0]Al[2029,14,2030,2030,2328,0,0]Aa[536,3,0,0,0,0,0]Ai[3173,14,3174,0,4489,0,0]Ag[477,14,478,0,3118,0,0]Al[3173,14,3174,1419,3119,0,0]Al[3173,14,3174,1393,2302,0,0]Al[477,13,478,3740,2323,504,0]Al[477,13,478,2385,2312,504,0]Ak[477,13,478,1126,520,504,0]Al[477,13,478,1126,4491,504,0]Al[477,13,478,4157,3322,504,0]Al[477,13,478,4162,3328,504,0]An[3173,13,3174,1152,1446,504,0]Am[1228,13,1229,1229,141,511,0]An[1228,13,1229,1229,4075,511,0]An[1228,13,1229,1229,4047,511,0]An[1228,13,1417,1417,4083,511,0]An[1228,13,1417,1417,1221,511,0]An[1228,13,1417,1417,4486,511,0]Ak[536,13,537,537,4048,511,0]An[4088,13,4089,4089,5161,511,0]Al[477,13,478,3740,4611,511,0]Al[477,13,478,3740,2323,511,0]Ai[477,13,478,0,4338,511,0]Al[477,13,478,4270,5065,511,0]Al[477,13,478,1126,2324,511,0]Al[477,13,478,1126,4785,511,0]Al[477,13,478,3740,3295,511,0]Al[477,13,478,3740,3297,511,0]Al[477,13,478,3740,1889,511,0]Al[477,13,478,3740,1459,511,0]Al[477,13,478,3740,3076,511,0]Al[477,13,478,3740,2296,511,0]Al[477,13,478,3740,4232,511,0]Al[477,13,478,3740,4239,511,0]Al[477,13,478,3740,1450,511,0]Al[477,13,478,3740,5072,511,0]Al[477,13,478,3740,4898,511,0]0Al[477,13,478,2353,2308,511,0]Al[477,13,478,2353,4347,511,0]Al[477,13,478,2353,1437,511,0]Al[477,13,478,4270,1458,511,0]Al[477,13,478,4270,3316,511,0]Al[477,13,478,4270,5000,511,0]Al[477,13,478,2385,2312,511,0]Al[477,13,478,2385,3325,511,0]Al[477,13,478,2385,4465,511,0]Ak[477,13,478,1126,520,511,0]Al[477,13,478,1126,4491,511,0]Al[477,13,478,4157,3322,511,0]Al[477,13,478,4157,5008,511,0]Al[477,13,478,4157,4484,511,0]0Df[477,13,478,4157,3121,511,0,\"impl-From%3CResetError%3E-for-ReadError\"]Al[477,13,478,4157,3121,511,0]Dk[477,13,478,4157,3121,511,0,\"impl-From%3CConnectionError%3E-for-ReadError\"]Al[477,13,478,4157,3324,511,0]0Al[477,13,478,4157,4483,511,0]0Al[477,13,478,1431,4249,511,0]Al[477,13,478,1169,4078,511,0]Al[477,13,478,4162,3328,511,0]Di[477,13,478,4162,3342,511,0,\"impl-From%3CClosedStream%3E-for-WriteError\"]Di[477,13,478,4162,3342,511,0,\"impl-From%3CStoppedError%3E-for-WriteError\"]Dl[477,13,478,4162,3342,511,0,\"impl-From%3CConnectionError%3E-for-WriteError\"]Al[477,13,478,4162,3342,511,0]Al[477,13,478,4162,4248,511,0]0Al[477,13,478,4162,1903,511,0]Ai[477,13,478,0,3319,511,0]Ai[477,13,478,0,4378,511,0]Ai[477,13,478,0,4795,511,0]Ai[477,13,478,0,4603,511,0]Ai[477,13,478,0,4222,511,0]Ai[477,13,478,0,4474,511,0]Cn[477,13,478,0,1480,511,0,\"impl-From%3CStreamId%3E-for-VarInt\"]Dh[477,13,478,0,1480,511,0,\"impl-From%3CTransportErrorCode%3E-for-VarInt\"]Ci[477,13,478,0,1480,511,0,\"impl-From%3Cu32%3E-for-VarInt\"]Ci[477,13,478,0,1480,511,0,\"impl-From%3Cu16%3E-for-VarInt\"]Ch[477,13,478,0,1480,511,0,\"impl-From%3Cu8%3E-for-VarInt\"]Ai[477,13,478,0,1480,511,0]Ai[477,13,478,0,5069,511,0]Ai[477,13,478,0,1184,511,0]Ai[477,13,478,0,1463,511,0]0Ai[477,13,478,0,3317,511,0]Ai[477,13,478,0,3116,511,0]Ai[477,13,478,0,4954,511,0]Ai[477,13,478,0,3290,511,0]Ai[477,13,478,0,2343,511,0]Ai[477,13,478,0,3303,511,0]Ai[477,13,478,0,3118,511,0]Ai[477,13,478,0,4587,511,0]Ai[477,13,478,0,4194,511,0]Dj[477,13,478,0,4583,511,0,\"impl-From%3CCloseReason%3E-for-ConnectionError\"]Ai[477,13,478,0,4583,511,0]Dd[477,13,478,0,4583,511,0,\"impl-From%3CClose%3E-for-ConnectionError\"]Dm[477,13,478,0,4583,511,0,\"impl-From%3CTransportError%3E-for-ConnectionError\"]Ai[477,13,478,0,3112,511,0]Ai[477,13,478,0,4450,511,0]Ai[477,13,478,0,4597,511,0]Ai[477,13,478,0,4606,511,0]Ai[477,13,478,0,4924,511,0]Ai[477,13,478,0,4939,511,0]Ai[477,13,478,0,4064,511,0]0Ai[477,13,478,0,4460,511,0]Ai[477,13,478,0,4242,511,0]Ai[477,13,478,0,5126,511,0]Ai[477,13,478,0,4188,511,0]Ai[477,13,478,0,4050,511,0]Dj[477,13,478,0,4050,511,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]Do[477,13,478,0,4050,511,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]Ai[477,13,478,0,3099,511,0]Ai[477,13,478,0,4580,511,0]0Ai[477,13,478,0,4787,511,0]Ai[477,13,478,0,4200,511,0]0Ai[477,13,478,0,4203,511,0]Ai[477,13,478,0,4208,511,0]Ai[477,13,478,0,4959,511,0]0Ai[477,13,478,0,3306,511,0]Ai[477,13,478,0,4821,511,0]Ah[477,13,478,0,526,511,0]Ah[477,13,478,0,133,511,0]Ai[477,13,478,0,2330,511,0]0Ai[477,13,478,0,2340,511,0]Ai[477,13,478,0,3097,511,0]0Ai[477,13,3735,0,501,511,0]Aj[477,13,3735,0,3286,511,0]Aj[477,13,3735,0,1188,511,0]Aj[477,13,3735,0,4055,511,0]Aj[477,13,3735,0,1886,511,0]Aj[477,13,3735,0,4366,511,0]Aj[477,13,3735,0,4885,511,0]Aj[477,13,1735,0,1881,511,0]Ai[477,13,1735,0,514,511,0]Aj[477,13,1735,0,5223,511,0]Aj[477,13,1735,0,4053,511,0]Aj[477,13,1735,0,4963,511,0]Aj[477,13,1818,0,3337,511,0]Aj[477,13,1818,0,4349,511,0]Dd[477,13,1818,0,1201,511,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]De[477,13,1818,0,1201,511,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]Do[477,13,1818,0,1201,511,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]De[477,13,1818,0,1201,511,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Dd[477,13,1818,0,1201,511,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]Df[477,13,1818,0,1201,511,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]Df[477,13,1818,0,1201,511,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]Df[477,13,1818,0,1201,511,0,\"impl-From%3CCertificateError%3E-for-Error\"]Dm[477,13,1818,0,1201,511,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]Aj[477,13,1818,0,1201,511,0]Do[477,13,1818,0,1201,511,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]D`[477,13,1818,0,1201,511,0,\"impl-From%3COtherError%3E-for-Error\"]Aj[477,13,1818,0,4817,511,0]Aj[477,13,1818,0,5063,511,0]Aj[477,13,1818,0,4819,511,0]Am[2408,13,2409,2409,132,511,0]An[2408,13,2409,2409,2299,511,0]An[3173,13,3174,4616,4339,511,0]An[3173,13,3174,3147,3089,511,0]An[3173,13,3174,1152,1446,511,0]An[3173,13,3174,2044,1907,511,0]An[3173,13,3174,2044,3323,511,0]An[3173,13,3174,2044,3329,511,0]An[3173,13,3174,3741,3298,511,0]An[3173,13,3174,1148,3320,511,0]An[3173,13,3174,1733,4379,511,0]An[3173,13,3174,2354,2309,511,0]An[3173,13,3174,2354,4345,511,0]An[3173,13,3174,2354,4796,511,0]An[3173,13,3174,2354,4357,511,0]An[3173,13,3174,1798,5286,511,0]An[3173,13,3174,4387,5255,511,0]An[3173,13,3174,1430,4604,511,0]An[3173,13,3174,1430,4223,511,0]An[3173,13,3174,1430,4475,511,0]Cn[3173,13,3174,1856,1481,511,0,\"impl-From%3Cu32%3E-for-VarInt\"]Co[3173,13,3174,1856,1481,511,0,\"impl-From%3CCode%3E-for-VarInt\"]An[3173,13,3174,1856,1481,511,0]Cn[3173,13,3174,1856,1481,511,0,\"impl-From%3Cu16%3E-for-VarInt\"]Dc[3173,13,3174,1856,1481,511,0,\"impl-From%3CStreamId%3E-for-VarInt\"]Cm[3173,13,3174,1856,1481,511,0,\"impl-From%3Cu8%3E-for-VarInt\"]An[3173,13,3174,1856,5070,511,0]An[3173,13,3174,3138,1185,511,0]An[3173,13,3174,3147,4899,511,0]An[3173,13,3174,1403,1464,511,0]0An[3173,13,3174,1403,4238,511,0]An[3173,13,3174,1403,3318,511,0]An[3173,13,3174,1403,3117,511,0]An[3173,13,3174,1403,4897,511,0]An[3173,13,3174,1403,4955,511,0]An[3173,13,3174,1403,3293,511,0]An[3173,13,3174,1419,2344,511,0]An[3173,13,3174,1419,3304,511,0]An[3173,13,3174,1419,3119,511,0]An[3173,13,3174,1419,4588,511,0]An[3173,13,3174,1152,3122,511,0]An[3173,13,3174,1152,4374,511,0]An[3173,13,3174,1157,3343,511,0]An[3173,13,3174,1157,4063,511,0]An[3173,13,3174,2044,4082,511,0]An[3173,13,3174,2044,4485,511,0]An[3173,13,3174,2044,4198,511,0]An[3173,13,3174,3741,4584,511,0]0An[3173,13,3174,3741,3113,511,0]An[3173,13,3174,3741,4451,511,0]An[3173,13,3174,3741,5157,511,0]An[3173,13,3174,3741,1206,511,0]0An[3173,13,3174,1148,4598,511,0]An[3173,13,3174,3276,4607,511,0]An[3173,13,3174,3276,4925,511,0]An[3173,13,3174,3276,4940,511,0]An[3173,13,3174,3276,4065,511,0]0An[3173,13,3174,1733,4461,511,0]An[3173,13,3174,1733,4244,511,0]An[3173,13,3174,1733,5127,511,0]An[3173,13,3174,1733,4190,511,0]Do[3173,13,3174,1733,4051,511,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]Ed[3173,13,3174,1733,4051,511,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]An[3173,13,3174,1733,4051,511,0]An[3173,13,3174,1393,3100,511,0]An[3173,13,3174,1393,4466,511,0]An[3173,13,3174,1393,3310,511,0]An[3173,13,3174,1393,3332,511,0]An[3173,13,3174,1393,4205,511,0]An[3173,13,3174,1393,4582,511,0]0An[3173,13,3174,1393,4789,511,0]An[3173,13,3174,1393,2302,511,0]An[3173,13,3174,2354,4793,511,0]An[3173,13,3174,2354,2313,511,0]An[3173,13,3174,2354,4201,511,0]0An[3173,13,3174,2354,4045,511,0]An[3173,13,3174,2354,3326,511,0]An[3173,13,3174,1798,4369,511,0]An[3173,13,3174,1798,4596,511,0]An[3173,13,3174,1798,5160,511,0]An[3173,13,3174,1798,2316,511,0]An[3173,13,3174,1798,4894,511,0]0An[3173,13,3174,1829,4586,511,0]An[3173,13,3174,1829,4346,511,0]An[3173,13,3174,1829,4204,511,0]An[3173,13,3174,1829,4209,511,0]Ak[3173,13,3174,0,4489,511,0]0Ak[3173,13,3174,0,4960,511,0]0An[3173,13,3174,4387,3307,511,0]An[3173,13,3174,4387,5260,511,0]An[3173,13,3174,4995,4822,511,0]Am[3173,13,3174,3174,527,511,0]Am[3173,13,3174,3174,134,511,0]An[3173,13,3174,3174,2331,511,0]0An[3173,13,3174,3174,2341,511,0]An[3173,13,3174,3174,3098,511,0]0An[3173,13,1731,1731,4380,511,0]An[3173,13,5111,5111,5007,511,0]An[3173,13,5111,5111,1203,511,0]0An[3173,13,1736,1736,1882,511,0]Am[3173,13,1736,1736,515,511,0]An[3173,13,1736,1736,5224,511,0]An[3173,13,1736,1736,4054,511,0]An[3173,13,1736,1736,4966,511,0]An[3173,13,1819,1819,3338,511,0]An[3173,13,1819,1819,4350,511,0]Df[3173,13,1819,0,1202,511,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]De[3173,13,1819,0,1202,511,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]De[3173,13,1819,0,1202,511,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]Dg[3173,13,1819,0,1202,511,0,\"impl-From%3CCertificateError%3E-for-Error\"]Dn[3173,13,1819,0,1202,511,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]Dg[3173,13,1819,0,1202,511,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]E`[3173,13,1819,0,1202,511,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]Da[3173,13,1819,0,1202,511,0,\"impl-From%3COtherError%3E-for-Error\"]Ak[3173,13,1819,0,1202,511,0]E`[3173,13,1819,0,1202,511,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]Df[3173,13,1819,0,1202,511,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]Dg[3173,13,1819,0,1202,511,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]An[3173,13,1819,1819,4818,511,0]An[3173,13,1819,1819,5064,511,0]An[3173,13,1819,1819,4820,511,0]Al[3173,13,3736,535,502,511,0]Am[3173,13,3736,535,3287,511,0]An[3173,13,3736,1389,1189,511,0]An[3173,13,3736,1389,4056,511,0]An[3173,13,3736,2408,1887,511,0]An[3173,13,3736,2408,4367,511,0]An[3173,13,3736,3736,4886,511,0]An[3173,13,4853,4853,1204,511,0]An[3173,13,4853,4853,1207,511,0]An[2029,13,2030,2030,3341,511,0]0Am[2029,13,2030,431,4490,511,0]An[2029,13,2030,2030,2328,511,0]An[2029,13,2030,2030,2342,511,0]An[2029,13,2030,2030,4210,511,0]An[1127,13,1128,1128,3086,511,0]An[1127,13,1128,1128,4999,511,0]Am[1127,13,1725,1725,142,511,0]An[1127,13,1418,1418,1222,511,0]An[1127,13,1418,1418,4084,511,0]An[1127,13,1418,1418,4592,511,0]An[1127,13,4305,4305,5209,511,0]An[1127,13,4305,4305,5210,511,0]Am[1127,13,1824,1824,143,511,0]Ai[477,13,478,0,1480,512,0]Ai[477,13,478,0,1463,512,0]Ai[477,13,478,0,4064,512,0]Ai[477,13,478,0,4203,512,0]Ah[477,13,478,0,526,512,0]Ah[477,13,478,0,133,512,0]Ai[477,13,478,0,2330,512,0]Ai[477,13,478,0,3097,512,0]An[3173,13,3174,1856,1481,512,0]An[3173,13,3174,3147,4899,512,0]An[3173,13,3174,1403,1464,512,0]An[3173,13,3174,1152,3122,512,0]An[3173,13,3174,1152,4374,512,0]An[3173,13,3174,1157,3343,512,0]An[3173,13,3174,3276,4065,512,0]An[3173,13,3174,2354,4793,512,0]An[3173,13,3174,1798,4894,512,0]An[3173,13,3174,1829,4204,512,0]Am[3173,13,3174,3174,527,512,0]Am[3173,13,3174,3174,134,512,0]An[3173,13,3174,3174,2331,512,0]An[3173,13,3174,3174,3098,512,0]Am[1228,13,1229,1229,141,513,0]An[1228,13,1229,1229,4075,513,0]An[1228,13,1229,1229,4047,513,0]An[1228,13,1417,1417,4083,513,0]An[1228,13,1417,1417,1221,513,0]An[1228,13,1417,1417,4486,513,0]Ak[536,13,537,537,4048,513,0]An[4088,13,4089,4089,5161,513,0]Al[477,13,478,3740,4611,513,0]Al[477,13,478,3740,2323,513,0]Ai[477,13,478,0,4338,513,0]Al[477,13,478,4270,5065,513,0]Al[477,13,478,1126,2324,513,0]Al[477,13,478,1126,4785,513,0]Al[477,13,478,3740,3295,513,0]Al[477,13,478,3740,3297,513,0]Al[477,13,478,3740,1889,513,0]Al[477,13,478,3740,1459,513,0]Al[477,13,478,3740,3076,513,0]Al[477,13,478,3740,2296,513,0]Al[477,13,478,3740,4232,513,0]Al[477,13,478,3740,4239,513,0]Al[477,13,478,3740,1450,513,0]Al[477,13,478,3740,5072,513,0]Al[477,13,478,3740,4898,513,0]Al[477,13,478,2353,2308,513,0]Al[477,13,478,2353,4347,513,0]Al[477,13,478,2353,1437,513,0]Al[477,13,478,4270,1458,513,0]Al[477,13,478,4270,3316,513,0]Al[477,13,478,4270,5000,513,0]Al[477,13,478,2385,2312,513,0]Al[477,13,478,2385,3325,513,0]Al[477,13,478,2385,4465,513,0]Ak[477,13,478,1126,520,513,0]Al[477,13,478,1126,4491,513,0]Al[477,13,478,4157,3322,513,0]Al[477,13,478,4157,5008,513,0]Al[477,13,478,4157,4484,513,0]Al[477,13,478,4157,3121,513,0]Al[477,13,478,4157,3324,513,0]Al[477,13,478,4157,4483,513,0]Al[477,13,478,1431,4249,513,0]Al[477,13,478,1169,4078,513,0]Al[477,13,478,4162,3328,513,0]Al[477,13,478,4162,3342,513,0]Al[477,13,478,4162,4248,513,0]Al[477,13,478,4162,1903,513,0]Ai[477,13,478,0,3319,513,0]Ai[477,13,478,0,4378,513,0]Ai[477,13,478,0,4795,513,0]Ai[477,13,478,0,4603,513,0]Ai[477,13,478,0,4222,513,0]Ai[477,13,478,0,4474,513,0]Ai[477,13,478,0,1480,513,0]Ai[477,13,478,0,5069,513,0]Ai[477,13,478,0,1184,513,0]Ai[477,13,478,0,1463,513,0]Ai[477,13,478,0,3317,513,0]Ai[477,13,478,0,3116,513,0]Ai[477,13,478,0,4954,513,0]Ai[477,13,478,0,3290,513,0]Ai[477,13,478,0,2343,513,0]Ai[477,13,478,0,3303,513,0]Ai[477,13,478,0,3118,513,0]Ai[477,13,478,0,4587,513,0]Ai[477,13,478,0,4194,513,0]Ai[477,13,478,0,4583,513,0]Ai[477,13,478,0,3112,513,0]Ai[477,13,478,0,4450,513,0]Ai[477,13,478,0,4597,513,0]Ai[477,13,478,0,4606,513,0]Ai[477,13,478,0,4924,513,0]Ai[477,13,478,0,4939,513,0]Ai[477,13,478,0,4064,513,0]Ai[477,13,478,0,4460,513,0]Ai[477,13,478,0,4242,513,0]Ai[477,13,478,0,5126,513,0]Ai[477,13,478,0,4188,513,0]Ai[477,13,478,0,4050,513,0]Ai[477,13,478,0,3099,513,0]Ai[477,13,478,0,4580,513,0]Ai[477,13,478,0,4787,513,0]Ai[477,13,478,0,4200,513,0]Ai[477,13,478,0,4203,513,0]Ai[477,13,478,0,4208,513,0]Ai[477,13,478,0,4959,513,0]Ai[477,13,478,0,3306,513,0]Ai[477,13,478,0,4821,513,0]Ah[477,13,478,0,526,513,0]Ah[477,13,478,0,133,513,0]Ai[477,13,478,0,2330,513,0]Ai[477,13,478,0,2340,513,0]Ai[477,13,478,0,3097,513,0]Ai[477,13,3735,0,501,513,0]Aj[477,13,3735,0,3286,513,0]Aj[477,13,3735,0,1188,513,0]Aj[477,13,3735,0,4055,513,0]Aj[477,13,3735,0,1886,513,0]Aj[477,13,3735,0,4366,513,0]Aj[477,13,3735,0,4885,513,0]Aj[477,13,1735,0,1881,513,0]Ai[477,13,1735,0,514,513,0]Aj[477,13,1735,0,5223,513,0]Aj[477,13,1735,0,4053,513,0]Aj[477,13,1735,0,4963,513,0]Aj[477,13,1818,0,3337,513,0]Aj[477,13,1818,0,4349,513,0]Aj[477,13,1818,0,1201,513,0]Aj[477,13,1818,0,4817,513,0]Aj[477,13,1818,0,5063,513,0]Aj[477,13,1818,0,4819,513,0]Am[2408,13,2409,2409,132,513,0]An[2408,13,2409,2409,2299,513,0]An[3173,13,3174,4616,4339,513,0]An[3173,13,3174,3147,3089,513,0]An[3173,13,3174,1152,1446,513,0]An[3173,13,3174,2044,1907,513,0]An[3173,13,3174,2044,3323,513,0]An[3173,13,3174,2044,3329,513,0]An[3173,13,3174,3741,3298,513,0]An[3173,13,3174,1148,3320,513,0]An[3173,13,3174,1733,4379,513,0]An[3173,13,3174,2354,2309,513,0]An[3173,13,3174,2354,4345,513,0]An[3173,13,3174,2354,4796,513,0]An[3173,13,3174,2354,4357,513,0]An[3173,13,3174,1798,5286,513,0]An[3173,13,3174,4387,5255,513,0]An[3173,13,3174,1430,4604,513,0]An[3173,13,3174,1430,4223,513,0]An[3173,13,3174,1430,4475,513,0]An[3173,13,3174,1856,1481,513,0]An[3173,13,3174,1856,5070,513,0]An[3173,13,3174,3138,1185,513,0]An[3173,13,3174,3147,4899,513,0]An[3173,13,3174,1403,1464,513,0]An[3173,13,3174,1403,4238,513,0]An[3173,13,3174,1403,3318,513,0]An[3173,13,3174,1403,3117,513,0]An[3173,13,3174,1403,4897,513,0]An[3173,13,3174,1403,4955,513,0]An[3173,13,3174,1403,3293,513,0]An[3173,13,3174,1419,2344,513,0]An[3173,13,3174,1419,3304,513,0]An[3173,13,3174,1419,3119,513,0]An[3173,13,3174,1419,4588,513,0]An[3173,13,3174,1152,3122,513,0]An[3173,13,3174,1152,4374,513,0]An[3173,13,3174,1157,3343,513,0]An[3173,13,3174,1157,4063,513,0]An[3173,13,3174,2044,4082,513,0]An[3173,13,3174,2044,4485,513,0]An[3173,13,3174,2044,4198,513,0]An[3173,13,3174,3741,4584,513,0]An[3173,13,3174,3741,3113,513,0]An[3173,13,3174,3741,4451,513,0]An[3173,13,3174,3741,5157,513,0]An[3173,13,3174,3741,1206,513,0]An[3173,13,3174,1148,4598,513,0]An[3173,13,3174,3276,4607,513,0]An[3173,13,3174,3276,4925,513,0]An[3173,13,3174,3276,4940,513,0]An[3173,13,3174,3276,4065,513,0]An[3173,13,3174,1733,4461,513,0]An[3173,13,3174,1733,4244,513,0]An[3173,13,3174,1733,5127,513,0]An[3173,13,3174,1733,4190,513,0]An[3173,13,3174,1733,4051,513,0]An[3173,13,3174,1393,3100,513,0]An[3173,13,3174,1393,4466,513,0]An[3173,13,3174,1393,3310,513,0]An[3173,13,3174,1393,3332,513,0]An[3173,13,3174,1393,4205,513,0]An[3173,13,3174,1393,4582,513,0]An[3173,13,3174,1393,4789,513,0]An[3173,13,3174,1393,2302,513,0]An[3173,13,3174,2354,4793,513,0]An[3173,13,3174,2354,2313,513,0]An[3173,13,3174,2354,4201,513,0]An[3173,13,3174,2354,4045,513,0]An[3173,13,3174,2354,3326,513,0]An[3173,13,3174,1798,4369,513,0]An[3173,13,3174,1798,4596,513,0]An[3173,13,3174,1798,5160,513,0]An[3173,13,3174,1798,2316,513,0]An[3173,13,3174,1798,4894,513,0]An[3173,13,3174,1829,4586,513,0]An[3173,13,3174,1829,4346,513,0]An[3173,13,3174,1829,4204,513,0]An[3173,13,3174,1829,4209,513,0]Ak[3173,13,3174,0,4489,513,0]Ak[3173,13,3174,0,4960,513,0]An[3173,13,3174,4387,3307,513,0]An[3173,13,3174,4387,5260,513,0]An[3173,13,3174,4995,4822,513,0]Am[3173,13,3174,3174,527,513,0]Am[3173,13,3174,3174,134,513,0]An[3173,13,3174,3174,2331,513,0]An[3173,13,3174,3174,2341,513,0]An[3173,13,3174,3174,3098,513,0]An[3173,13,1731,1731,4380,513,0]An[3173,13,5111,5111,5007,513,0]An[3173,13,5111,5111,1203,513,0]An[3173,13,1736,1736,1882,513,0]Am[3173,13,1736,1736,515,513,0]An[3173,13,1736,1736,5224,513,0]An[3173,13,1736,1736,4054,513,0]An[3173,13,1736,1736,4966,513,0]An[3173,13,1819,1819,3338,513,0]An[3173,13,1819,1819,4350,513,0]Ak[3173,13,1819,0,1202,513,0]An[3173,13,1819,1819,4818,513,0]An[3173,13,1819,1819,5064,513,0]An[3173,13,1819,1819,4820,513,0]Al[3173,13,3736,535,502,513,0]Am[3173,13,3736,535,3287,513,0]An[3173,13,3736,1389,1189,513,0]An[3173,13,3736,1389,4056,513,0]An[3173,13,3736,2408,1887,513,0]An[3173,13,3736,2408,4367,513,0]An[3173,13,3736,3736,4886,513,0]An[3173,13,4853,4853,1204,513,0]An[3173,13,4853,4853,1207,513,0]An[2029,13,2030,2030,3341,513,0]Am[2029,13,2030,431,4490,513,0]An[2029,13,2030,2030,2328,513,0]An[2029,13,2030,2030,2342,513,0]An[2029,13,2030,2030,4210,513,0]An[1127,13,1128,1128,3086,513,0]An[1127,13,1128,1128,4999,513,0]Am[1127,13,1725,1725,142,513,0]An[1127,13,1418,1418,1222,513,0]An[1127,13,1418,1418,4084,513,0]An[1127,13,1418,1418,4592,513,0]An[1127,13,4305,4305,5209,513,0]An[1127,13,4305,4305,5210,513,0]Am[1127,13,1824,1824,143,513,0]Ac[536,7,537,0,0,0,0]Ae[4088,7,4089,0,0,0,0]Ae[4390,7,4391,0,0,0,0]Ae[2408,7,2409,0,0,0,0]Ae[4917,7,4918,0,0,0,0]Al[3173,13,3174,1152,1446,0,0]Ao[3173,13,3174,2354,4357,2314,0]Ah[477,12,1735,0,4887,0,0]Al[3173,13,3174,2044,1907,0,0]Al[3173,12,1736,1736,4888,0,0]Aj[477,13,478,3740,3297,0,0]Ab[1127,3,0,0,0,0,0]Ai[477,13,478,1126,520,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,13,3174,3741,3298,0,0]Al[3173,14,3174,1419,3304,0,0]Aj[477,12,478,2036,3282,0,0]Am[477,13,478,3740,4611,1455,0]Am[477,13,478,3740,2323,1455,0]Am[477,13,478,1126,2324,1455,0]Am[477,13,478,3740,3295,1455,0]Am[477,13,478,3740,1889,1455,0]Am[477,13,478,3740,1459,1455,0]Am[477,13,478,3740,3076,1455,0]Am[477,13,478,3740,2296,1455,0]Am[477,13,478,3740,4232,1455,0]Am[477,13,478,3740,4239,1455,0]Am[477,13,478,2353,1437,1455,0]Am[477,13,478,2385,4465,1455,0]Am[477,13,478,4162,1903,1455,0]?Aj[477,13,478,4157,3322,0,0]Al[3173,13,3174,2044,3323,0,0]Al[3173,13,5111,5111,5007,0,0]Al[3173,13,3174,3147,3089,0,0]Ak[2029,13,2030,431,4490,0,0]Ah[477,12,1735,0,4887,0,0]Al[3173,12,1736,1736,4888,0,0]32Ag[477,13,478,0,5126,0,0]Al[3173,13,3174,1733,5127,0,0]Aj[477,13,478,3740,3297,0,0]Al[3173,13,3174,3741,3298,0,0]Ah[477,12,1735,0,1877,0,0]Al[3173,12,1736,1736,1878,0,0]Al[1228,14,1417,1417,4486,0,0]Ag[477,14,478,0,2340,0,0]Al[3173,14,3174,3174,2341,0,0]Ai[3173,14,4595,0,1218,0,0]Aj[477,13,478,4157,3322,0,0]Aj[477,13,478,4157,5008,0,0]Al[3173,13,3174,2044,3323,0,0]Ag[477,12,478,0,3339,0,0]Aj[477,13,478,0,4474,3339,0]Aj[477,13,478,0,4821,3339,0]Al[3173,12,3174,1430,3340,0,0]Ao[3173,13,3174,1430,4475,3340,0]Ao[3173,13,3174,4995,4822,3340,0]Af[477,5,478,3138,0,0,0]Ah[3173,5,3174,3138,0,0,0]Ag[477,5,3735,1389,0,0,0]Ah[3173,5,3736,1389,0,0,0]Al[1127,15,1128,1128,4999,0,0]Ag[477,6,1818,1392,0,0,0]Ah[3173,6,1819,1392,0,0,0]Ae[3173,6,5111,0,0,0,0]Ae[3173,6,4853,0,0,0,0]Ah[3173,6,3174,3741,0,0,0]1Al[3173,15,3174,1393,3310,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]Aj[477,15,478,4157,3121,0,0]Ag[477,15,478,0,4583,0,0]Al[3173,15,3174,1152,3122,0,0]Al[3173,15,3174,3741,4584,0,0]Al[3173,15,3174,1798,4596,0,0]0Ae[1228,5,1417,0,0,0,0]Ae[1127,5,1418,0,0,0,0]Al[1228,15,1229,1229,4075,0,0]Ag[477,13,478,0,4795,0,0]Al[3173,13,3174,2354,4796,0,0]Ab[1228,3,0,0,0,0,0]Ah[477,12,3735,0,4883,0,0]Ak[477,13,3735,0,3286,4883,0]Ak[477,13,3735,0,4055,4883,0]Ak[477,13,3735,0,4366,4883,0]Al[3173,12,3736,3736,4884,0,0]An[3173,13,3736,535,3287,4884,0]Ao[3173,13,3736,1389,4056,4884,0]Ao[3173,13,3736,2408,4367,4884,0]Al[1127,13,1128,1128,4999,0,0]Ag[477,14,478,0,1184,0,0]Ag[477,14,478,0,2343,0,0]Al[3173,14,3174,3138,1185,0,0]Al[3173,14,3174,1419,2344,0,0]Al[3173,14,3174,2354,4045,0,0]An[1228,13,1229,1229,141,1186,0]Ao[1228,13,1229,1229,4075,1186,0]Ao[1228,13,1229,1229,4047,1186,0]Am[477,13,478,3740,3297,1186,0]Am[477,13,478,3740,1450,1186,0]Am[477,13,478,3740,5072,1186,0]Am[477,13,478,3740,4898,1186,0]Am[477,13,478,2353,2308,1186,0]Am[477,13,478,2353,4347,1186,0]Am[477,13,478,4270,1458,1186,0]Al[477,13,478,1126,520,1186,0]Am[477,13,478,1126,4491,1186,0]Am[477,13,478,4157,4484,1186,0]Am[477,13,478,4157,3121,1186,0]Am[477,13,478,4157,3324,1186,0]Am[477,13,478,4157,4483,1186,0]Am[477,13,478,4162,3342,1186,0]Am[477,13,478,4162,4248,1186,0]Aj[477,13,478,0,1480,1186,0]Aj[477,13,478,0,5069,1186,0]Aj[477,13,478,0,1463,1186,0]Aj[477,13,478,0,3317,1186,0]Aj[477,13,478,0,3116,1186,0]Aj[477,13,478,0,4954,1186,0]Aj[477,13,478,0,3290,1186,0]Aj[477,13,478,0,2343,1186,0]Aj[477,13,478,0,3303,1186,0]Aj[477,13,478,0,3118,1186,0]Aj[477,13,478,0,4587,1186,0]Aj[477,13,478,0,4194,1186,0]Aj[477,13,478,0,4583,1186,0]Aj[477,13,478,0,3112,1186,0]Aj[477,13,478,0,4450,1186,0]Aj[477,13,478,0,4606,1186,0]Aj[477,13,478,0,4924,1186,0]Aj[477,13,478,0,4939,1186,0]Aj[477,13,478,0,4064,1186,0]Aj[477,13,478,0,4460,1186,0]Aj[477,13,478,0,4242,1186,0]Aj[477,13,478,0,5126,1186,0]Aj[477,13,478,0,4188,1186,0]Aj[477,13,478,0,4050,1186,0]Aj[477,13,478,0,3099,1186,0]Aj[477,13,478,0,4580,1186,0]Aj[477,13,478,0,4787,1186,0]Aj[477,13,478,0,4200,1186,0]Aj[477,13,478,0,4203,1186,0]Aj[477,13,478,0,4208,1186,0]Aj[477,13,478,0,4959,1186,0]Aj[477,13,478,0,3306,1186,0]Ai[477,13,478,0,526,1186,0]Ai[477,13,478,0,133,1186,0]Aj[477,13,478,0,2330,1186,0]Aj[477,13,478,0,3097,1186,0]Aj[477,13,3735,0,501,1186,0]Ak[477,13,3735,0,3286,1186,0]Ak[477,13,3735,0,1188,1186,0]Ak[477,13,3735,0,4055,1186,0]Ak[477,13,3735,0,1886,1186,0]Ak[477,13,3735,0,4366,1186,0]Ak[477,13,1818,0,1201,1186,0]Ak[477,13,1818,0,4817,1186,0]Ak[477,13,1818,0,5063,1186,0]Ak[477,13,1818,0,4819,1186,0]Ao[3173,13,3174,1856,1481,1186,0]Ao[3173,13,3174,1856,5070,1186,0]Ao[3173,13,3174,3147,4899,1186,0]Ao[3173,13,3174,1403,1464,1186,0]Ao[3173,13,3174,1403,4238,1186,0]Ao[3173,13,3174,1403,3318,1186,0]Ao[3173,13,3174,1403,3117,1186,0]Ao[3173,13,3174,1403,4897,1186,0]Ao[3173,13,3174,1403,4955,1186,0]Ao[3173,13,3174,1403,3293,1186,0]Ao[3173,13,3174,1419,2344,1186,0]Ao[3173,13,3174,1419,3304,1186,0]Ao[3173,13,3174,1419,3119,1186,0]Ao[3173,13,3174,1419,4588,1186,0]Ao[3173,13,3174,1152,3122,1186,0]Ao[3173,13,3174,1152,4374,1186,0]Ao[3173,13,3174,1157,3343,1186,0]Ao[3173,13,3174,1157,4063,1186,0]Ao[3173,13,3174,2044,4485,1186,0]Ao[3173,13,3174,2044,4198,1186,0]Ao[3173,13,3174,3741,4584,1186,0]Ao[3173,13,3174,3741,3113,1186,0]Ao[3173,13,3174,3741,4451,1186,0]Ao[3173,13,3174,3741,5157,1186,0]Ao[3173,13,3174,3276,4607,1186,0]Ao[3173,13,3174,3276,4925,1186,0]Ao[3173,13,3174,3276,4940,1186,0]Ao[3173,13,3174,3276,4065,1186,0]Ao[3173,13,3174,1733,4461,1186,0]Ao[3173,13,3174,1733,4244,1186,0]Ao[3173,13,3174,1733,5127,1186,0]Ao[3173,13,3174,1733,4190,1186,0]Ao[3173,13,3174,1733,4051,1186,0]Ao[3173,13,3174,1393,3100,1186,0]Ao[3173,13,3174,1393,3310,1186,0]Ao[3173,13,3174,1393,3332,1186,0]Ao[3173,13,3174,1393,4205,1186,0]Ao[3173,13,3174,1393,4582,1186,0]Ao[3173,13,3174,1393,4789,1186,0]Ao[3173,13,3174,1393,2302,1186,0]Ao[3173,13,3174,2354,4793,1186,0]Ao[3173,13,3174,2354,4201,1186,0]Ao[3173,13,3174,1798,4596,1186,0]Ao[3173,13,3174,1798,5160,1186,0]Ao[3173,13,3174,1798,2316,1186,0]Ao[3173,13,3174,1798,4894,1186,0]Ao[3173,13,3174,1829,4204,1186,0]Ao[3173,13,3174,1829,4209,1186,0]Al[3173,13,3174,0,4489,1186,0]Al[3173,13,3174,0,4960,1186,0]Ao[3173,13,3174,4387,3307,1186,0]Ao[3173,13,3174,4387,5260,1186,0]An[3173,13,3174,3174,527,1186,0]An[3173,13,3174,3174,134,1186,0]Ao[3173,13,3174,3174,2331,1186,0]Ao[3173,13,3174,3174,3098,1186,0]Ao[3173,13,1731,1731,4380,1186,0]Ao[3173,13,5111,5111,5007,1186,0]Ao[3173,13,5111,5111,1203,1186,0]Al[3173,13,1819,0,1202,1186,0]Ao[3173,13,1819,1819,4818,1186,0]Ao[3173,13,1819,1819,5064,1186,0]Ao[3173,13,1819,1819,4820,1186,0]Am[3173,13,3736,535,502,1186,0]An[3173,13,3736,535,3287,1186,0]Ao[3173,13,3736,1389,1189,1186,0]Ao[3173,13,3736,1389,4056,1186,0]Ao[3173,13,3736,2408,1887,1186,0]Ao[3173,13,3736,2408,4367,1186,0]Ao[3173,13,4853,4853,1207,1186,0]Ao[2029,13,2030,2030,2328,1186,0]Ao[2029,13,2030,2030,2342,1186,0]Ao[2029,13,2030,2030,4210,1186,0]Ao[1127,13,1128,1128,4999,1186,0]Ao[1127,13,1418,1418,4592,1186,0]Ao[1127,13,4305,4305,5209,1186,0]Ao[1127,13,4305,4305,5210,1186,0]Aj[477,13,478,3740,3297,0,0]Aj[477,13,478,2353,2308,0,0]Ai[477,13,478,1126,520,0,0]Al[3173,13,3174,3741,3298,0,0]Aj[477,13,478,0,4203,1192,0]Ao[3173,13,3174,1829,4204,1192,0]Ai[3173,14,3174,0,4489,0,0]Ag[477,13,478,0,2330,0,0]Al[3173,13,3174,3174,2331,0,0]Ag[477,13,478,0,4208,0,0]Al[3173,13,3174,1829,4209,0,0]Ah[477,14,1735,0,1881,0,0]Al[3173,14,1736,1736,1882,0,0]Al[3173,12,3174,1798,4930,0,0]Ao[3173,13,3174,1798,5286,4930,0];Al[1228,13,1417,1417,1221,0,0]Ai[536,13,537,537,4048,0,0]Al[1127,13,1418,1418,1222,0,0]Aj[477,12,478,2036,3282,0,0]Aj[477,13,478,4162,3328,0,0]Al[3173,13,3174,2044,3329,0,0]Aj[477,13,478,2385,2312,0,0]Al[3173,13,3174,2354,2309,0,0]Aj[477,12,478,2036,1898,0,0]Am[477,13,478,1431,4249,1898,0]Am[477,13,478,1169,4078,1898,0]Ae[1228,2,1229,0,0,0,0]Ak[1228,14,1229,1229,141,0,0]Aj[477,13,478,3740,3297,0,0]Aj[477,13,478,2353,2308,0,0]Ai[477,13,478,1126,520,0,0]Aj[477,14,478,3740,1450,0,0]Al[3173,13,3174,3741,3298,0,0]Ae[1127,2,1128,0,0,0,0]Ag[477,13,478,0,3319,0,0]Al[3173,13,3174,1148,3320,0,0]Aj[477,13,478,4162,3328,0,0]Al[3173,13,3174,2044,3329,0,0]Al[3173,13,5111,5111,5007,0,0]Af[477,5,478,2353,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]Al[1228,15,1229,1229,4047,0,0]0Ag[477,15,478,0,3317,0,0]Al[3173,15,3174,1403,3318,0,0]Ah[3173,5,3174,1152,0,0,0]Af[477,15,478,0,526,0,0]Al[2408,15,2409,2409,2299,0,0]Ak[3173,15,3174,3174,527,0,0]Af[477,5,478,3740,0,0,0]Al[3173,15,4853,4853,1204,0,0]:9Af[477,5,478,4270,0,0,0]2Al[3173,15,3174,2044,4082,0,0]Af[477,5,478,1403,0,0,0]Ah[3173,5,3174,1403,0,0,0]Ae[3173,8,1731,0,0,0,0]987?Al[3173,15,3174,3741,1206,0,0]?Af[477,5,478,1856,0,0,0]Ah[3173,5,3174,1856,0,0,0]Aj[477,13,478,2353,2308,0,0]Aj[477,13,478,2385,2312,0,0]Al[3173,13,3174,2044,1907,0,0]Al[3173,13,3174,2354,2309,0,0]An[1228,13,1229,1229,141,1444,0]Ao[1228,13,1229,1229,4075,1444,0]Ao[1228,13,1229,1229,4047,1444,0]Ao[1228,13,1417,1417,4083,1444,0]Ao[1228,13,1417,1417,1221,1444,0]Ao[1228,13,1417,1417,4486,1444,0]Al[536,13,537,537,4048,1444,0]Ao[4088,13,4089,4089,5161,1444,0]Am[477,13,478,3740,4611,1444,0]Am[477,13,478,3740,2323,1444,0]Aj[477,13,478,0,4338,1444,0]Am[477,13,478,4270,5065,1444,0]Am[477,13,478,1126,2324,1444,0]Am[477,13,478,1126,4785,1444,0]Am[477,13,478,3740,3295,1444,0]Am[477,13,478,3740,3297,1444,0]Am[477,13,478,3740,1889,1444,0]Am[477,13,478,3740,1459,1444,0]Am[477,13,478,3740,3076,1444,0]Am[477,13,478,3740,2296,1444,0]Am[477,13,478,3740,4232,1444,0]Am[477,13,478,3740,4239,1444,0]Am[477,13,478,3740,1450,1444,0]Am[477,13,478,3740,5072,1444,0]Am[477,13,478,3740,4898,1444,0]Am[477,13,478,2353,2308,1444,0]Am[477,13,478,2353,4347,1444,0]Am[477,13,478,2353,1437,1444,0]Am[477,13,478,4270,1458,1444,0]Am[477,13,478,4270,3316,1444,0]Am[477,13,478,4270,5000,1444,0]Am[477,13,478,2385,2312,1444,0]Am[477,13,478,2385,3325,1444,0]Am[477,13,478,2385,4465,1444,0]Al[477,13,478,1126,520,1444,0]Am[477,13,478,1126,4491,1444,0]Am[477,13,478,4157,3322,1444,0]Am[477,13,478,4157,5008,1444,0]Am[477,13,478,4157,4484,1444,0]Am[477,13,478,4157,3121,1444,0]Am[477,13,478,4157,3324,1444,0]Am[477,13,478,4157,4483,1444,0]Am[477,13,478,1431,4249,1444,0]Am[477,13,478,1169,4078,1444,0]Am[477,13,478,4162,3328,1444,0]Am[477,13,478,4162,3342,1444,0]Am[477,13,478,4162,4248,1444,0]Am[477,13,478,4162,1903,1444,0]Aj[477,13,478,0,3319,1444,0]Aj[477,13,478,0,4378,1444,0]Aj[477,13,478,0,4795,1444,0]Aj[477,13,478,0,4603,1444,0]Aj[477,13,478,0,4222,1444,0]Aj[477,13,478,0,4474,1444,0]Aj[477,13,478,0,1480,1444,0]Aj[477,13,478,0,5069,1444,0]Aj[477,13,478,0,1184,1444,0]Aj[477,13,478,0,1463,1444,0]Aj[477,13,478,0,3317,1444,0]Aj[477,13,478,0,3116,1444,0]Aj[477,13,478,0,4954,1444,0]Aj[477,13,478,0,3290,1444,0]Aj[477,13,478,0,2343,1444,0]Aj[477,13,478,0,3303,1444,0]Aj[477,13,478,0,3118,1444,0]Aj[477,13,478,0,4587,1444,0]Aj[477,13,478,0,4194,1444,0]Aj[477,13,478,0,4583,1444,0]Aj[477,13,478,0,3112,1444,0]Aj[477,13,478,0,4450,1444,0]Aj[477,13,478,0,4597,1444,0]Aj[477,13,478,0,4606,1444,0]Aj[477,13,478,0,4924,1444,0]Aj[477,13,478,0,4939,1444,0]Aj[477,13,478,0,4064,1444,0]Aj[477,13,478,0,4460,1444,0]Aj[477,13,478,0,4242,1444,0]Aj[477,13,478,0,5126,1444,0]Aj[477,13,478,0,4188,1444,0]Aj[477,13,478,0,4050,1444,0]Aj[477,13,478,0,3099,1444,0]Aj[477,13,478,0,4580,1444,0]Aj[477,13,478,0,4787,1444,0]Aj[477,13,478,0,4200,1444,0]Aj[477,13,478,0,4203,1444,0]Aj[477,13,478,0,4208,1444,0]Aj[477,13,478,0,4959,1444,0]Aj[477,13,478,0,3306,1444,0]Aj[477,13,478,0,4821,1444,0]Ai[477,13,478,0,526,1444,0]Ai[477,13,478,0,133,1444,0]Aj[477,13,478,0,2330,1444,0]Aj[477,13,478,0,2340,1444,0]Aj[477,13,478,0,3097,1444,0]Aj[477,13,3735,0,501,1444,0]Ak[477,13,3735,0,3286,1444,0]Ak[477,13,3735,0,1188,1444,0]Ak[477,13,3735,0,4055,1444,0]Ak[477,13,3735,0,1886,1444,0]Ak[477,13,3735,0,4366,1444,0]Ak[477,13,3735,0,4885,1444,0]Ak[477,13,1735,0,1881,1444,0]Aj[477,13,1735,0,514,1444,0]Ak[477,13,1735,0,5223,1444,0]Ak[477,13,1735,0,4053,1444,0]Ak[477,13,1735,0,4963,1444,0]Ak[477,13,1818,0,3337,1444,0]Ak[477,13,1818,0,4349,1444,0]Ak[477,13,1818,0,1201,1444,0]Ak[477,13,1818,0,4817,1444,0]Ak[477,13,1818,0,5063,1444,0]Ak[477,13,1818,0,4819,1444,0]An[2408,13,2409,2409,132,1444,0]Ao[2408,13,2409,2409,2299,1444,0]Ao[3173,13,3174,4616,4339,1444,0]Ao[3173,13,3174,3147,3089,1444,0]Ao[3173,13,3174,1152,1446,1444,0]Ao[3173,13,3174,2044,1907,1444,0]Ao[3173,13,3174,2044,3323,1444,0]Ao[3173,13,3174,2044,3329,1444,0]Ao[3173,13,3174,3741,3298,1444,0]Ao[3173,13,3174,1148,3320,1444,0]Ao[3173,13,3174,1733,4379,1444,0]Ao[3173,13,3174,2354,2309,1444,0]Ao[3173,13,3174,2354,4345,1444,0]Ao[3173,13,3174,2354,4796,1444,0]Ao[3173,13,3174,2354,4357,1444,0]Ao[3173,13,3174,1798,5286,1444,0]Ao[3173,13,3174,4387,5255,1444,0]Ao[3173,13,3174,1430,4604,1444,0]Ao[3173,13,3174,1430,4223,1444,0]Ao[3173,13,3174,1430,4475,1444,0]Ao[3173,13,3174,1856,1481,1444,0]Ao[3173,13,3174,1856,5070,1444,0]Ao[3173,13,3174,3138,1185,1444,0]Ao[3173,13,3174,3147,4899,1444,0]Ao[3173,13,3174,1403,1464,1444,0]Ao[3173,13,3174,1403,4238,1444,0]Ao[3173,13,3174,1403,3318,1444,0]Ao[3173,13,3174,1403,3117,1444,0]Ao[3173,13,3174,1403,4897,1444,0]Ao[3173,13,3174,1403,4955,1444,0]Ao[3173,13,3174,1403,3293,1444,0]Ao[3173,13,3174,1419,2344,1444,0]Ao[3173,13,3174,1419,3304,1444,0]Ao[3173,13,3174,1419,3119,1444,0]Ao[3173,13,3174,1419,4588,1444,0]Ao[3173,13,3174,1152,3122,1444,0]Ao[3173,13,3174,1152,4374,1444,0]Ao[3173,13,3174,1157,3343,1444,0]Ao[3173,13,3174,1157,4063,1444,0]Ao[3173,13,3174,2044,4082,1444,0]Ao[3173,13,3174,2044,4485,1444,0]Ao[3173,13,3174,2044,4198,1444,0]Ao[3173,13,3174,3741,4584,1444,0]Ao[3173,13,3174,3741,3113,1444,0]Ao[3173,13,3174,3741,4451,1444,0]Ao[3173,13,3174,3741,5157,1444,0]Ao[3173,13,3174,3741,1206,1444,0]Ao[3173,13,3174,1148,4598,1444,0]Ao[3173,13,3174,3276,4607,1444,0]Ao[3173,13,3174,3276,4925,1444,0]Ao[3173,13,3174,3276,4940,1444,0]Ao[3173,13,3174,3276,4065,1444,0]Ao[3173,13,3174,1733,4461,1444,0]Ao[3173,13,3174,1733,4244,1444,0]Ao[3173,13,3174,1733,5127,1444,0]Ao[3173,13,3174,1733,4190,1444,0]Ao[3173,13,3174,1733,4051,1444,0]Ao[3173,13,3174,1393,3100,1444,0]Ao[3173,13,3174,1393,4466,1444,0]Ao[3173,13,3174,1393,3310,1444,0]Ao[3173,13,3174,1393,3332,1444,0]Ao[3173,13,3174,1393,4205,1444,0]Ao[3173,13,3174,1393,4582,1444,0]Ao[3173,13,3174,1393,4789,1444,0]Ao[3173,13,3174,1393,2302,1444,0]Ao[3173,13,3174,2354,4793,1444,0]Ao[3173,13,3174,2354,2313,1444,0]Ao[3173,13,3174,2354,4201,1444,0]Ao[3173,13,3174,2354,4045,1444,0]Ao[3173,13,3174,2354,3326,1444,0]Ao[3173,13,3174,1798,4369,1444,0]Ao[3173,13,3174,1798,4596,1444,0]Ao[3173,13,3174,1798,5160,1444,0]Ao[3173,13,3174,1798,2316,1444,0]Ao[3173,13,3174,1798,4894,1444,0]Ao[3173,13,3174,1829,4586,1444,0]Ao[3173,13,3174,1829,4346,1444,0]Ao[3173,13,3174,1829,4204,1444,0]Ao[3173,13,3174,1829,4209,1444,0]Al[3173,13,3174,0,4489,1444,0]Al[3173,13,3174,0,4960,1444,0]Ao[3173,13,3174,4387,3307,1444,0]Ao[3173,13,3174,4387,5260,1444,0]Ao[3173,13,3174,4995,4822,1444,0]An[3173,13,3174,3174,527,1444,0]An[3173,13,3174,3174,134,1444,0]Ao[3173,13,3174,3174,2331,1444,0]Ao[3173,13,3174,3174,2341,1444,0]Ao[3173,13,3174,3174,3098,1444,0]Ao[3173,13,1731,1731,4380,1444,0]Ao[3173,13,5111,5111,5007,1444,0]Ao[3173,13,5111,5111,1203,1444,0]Ao[3173,13,1736,1736,1882,1444,0]An[3173,13,1736,1736,515,1444,0]Ao[3173,13,1736,1736,5224,1444,0]Ao[3173,13,1736,1736,4054,1444,0]Ao[3173,13,1736,1736,4966,1444,0]Ao[3173,13,1819,1819,3338,1444,0]Ao[3173,13,1819,1819,4350,1444,0]Al[3173,13,1819,0,1202,1444,0]Ao[3173,13,1819,1819,4818,1444,0]Ao[3173,13,1819,1819,5064,1444,0]Ao[3173,13,1819,1819,4820,1444,0]Am[3173,13,3736,535,502,1444,0]An[3173,13,3736,535,3287,1444,0]Ao[3173,13,3736,1389,1189,1444,0]Ao[3173,13,3736,1389,4056,1444,0]Ao[3173,13,3736,2408,1887,1444,0]Ao[3173,13,3736,2408,4367,1444,0]Ao[3173,13,3736,3736,4886,1444,0]Ao[3173,13,4853,4853,1204,1444,0]Ao[3173,13,4853,4853,1207,1444,0]Ao[2029,13,2030,2030,3341,1444,0]An[2029,13,2030,431,4490,1444,0]Ao[2029,13,2030,2030,2328,1444,0]Ao[2029,13,2030,2030,2342,1444,0]Ao[2029,13,2030,2030,4210,1444,0]Ao[1127,13,1128,1128,3086,1444,0]Ao[1127,13,1128,1128,4999,1444,0]An[1127,13,1725,1725,142,1444,0]Ao[1127,13,1418,1418,1222,1444,0]Ao[1127,13,1418,1418,4084,1444,0]Ao[1127,13,1418,1418,4592,1444,0]Ao[1127,13,4305,4305,5209,1444,0]Ao[1127,13,4305,4305,5210,1444,0]An[1127,13,1824,1824,143,1444,0]Ak[1228,14,1229,1229,141,0,0]Ac[536,7,537,0,0,0,0]Aj[477,13,478,2353,2308,0,0]Ae[1127,2,1128,0,0,0,0]Ae[4917,7,4918,0,0,0,0]Aj[477,13,478,3740,3297,0,0]Ae[3173,2,3174,0,0,0,0]Al[3173,13,3174,2354,2309,0,0]Af[477,2,478,3174,0,0,0]Ag[477,13,478,0,4959,0,0]Ag[477,14,478,0,3303,0,0]Ag[477,14,478,0,4242,0,0]5Ai[3173,13,3174,0,4960,0,0]Al[3173,14,3174,1419,3304,0,0]Al[3173,14,3174,1733,4244,0,0]Ai[3173,14,3174,0,4489,0,0]Aj[477,13,478,0,1480,3090,0]Aj[477,13,478,0,1463,3090,0]Aj[477,13,478,0,3099,3090,0]Aj[477,13,478,0,4959,3090,0]Aj[477,13,478,0,2330,3090,0]Ao[3173,13,3174,1856,1481,3090,0]Ao[3173,13,3174,1403,1464,3090,0]Ao[3173,13,3174,1393,3100,3090,0]Ao[3173,13,3174,1393,3310,3090,0]Al[3173,13,3174,1798,4596,0,0]Al[3173,13,3174,0,4960,3090,0]Ao[3173,13,3174,3174,2331,3090,0]Al[3173,12,1731,1731,3090,0,0]Al[2029,14,2030,2030,2328,0,0]Aj[477,13,478,0,1480,3095,0]Aj[477,13,478,0,1463,3095,0]Aj[477,13,478,0,3099,3095,0]Aj[477,13,478,0,4959,3095,0]Aj[477,13,478,0,2330,3095,0]Ao[3173,13,3174,1856,1481,3095,0]Ao[3173,13,3174,1403,1464,3095,0]Ao[3173,13,3174,1393,3100,3095,0]Ao[3173,13,3174,1393,3310,3095,0]Ao[3173,13,3174,1393,2302,3095,0]Al[3173,13,3174,0,4960,3095,0]Ao[3173,13,3174,3174,2331,3095,0]Al[3173,12,1731,1731,3095,0,0]Aj[477,13,478,4162,3328,0,0]Al[3173,13,3174,2044,3329,0,0]Al[1127,13,1418,1418,4084,0,0]Al[3173,13,3174,2354,2309,0,0]Ag[477,14,1735,0,514,0,0]Ak[3173,14,1736,1736,515,0,0]Aj[477,13,478,2385,2312,0,0]3Ag[477,12,478,0,3339,0,0]Aj[477,13,478,0,4474,3339,0]Aj[477,13,478,0,4821,3339,0]Al[3173,12,3174,1430,3340,0,0]Ao[3173,13,3174,1430,4475,3340,0]Ao[3173,13,3174,4995,4822,3340,0]Al[1127,14,1128,1128,3086,0,0]Ag[477,14,478,0,1184,0,0]Al[3173,14,3174,3138,1185,0,0]Ah[477,13,3735,0,3299,0,0]Aj[477,13,3735,0,501,3299,0]Ak[477,13,3735,0,1188,3299,0]Ak[477,13,3735,0,1886,3299,0]Al[3173,13,3736,3736,3300,0,0]Am[3173,13,3736,535,502,3300,0]Ao[3173,13,3736,1389,1189,3300,0]Ao[3173,13,3736,2408,1887,3300,0]Ag[477,14,1735,0,514,0,0]Ak[3173,14,1736,1736,515,0,0]Ai[3173,14,4595,0,4827,0,0]Aj[477,14,478,3740,1450,0,0]Ag[477,14,478,0,4580,0,0]Ag[477,14,478,0,4787,0,0]Ah[477,14,3114,0,3072,0,0]Al[3173,14,3174,1393,4582,0,0]Al[3173,14,3174,1393,4789,0,0]Ai[3173,14,3174,0,4489,0,0]Ai[3173,14,1205,0,4453,0,0]Ai[3173,14,3115,0,3073,0,0]Aj[477,13,478,2353,2308,0,0]Aj[477,13,478,2385,2312,0,0]Al[3173,13,3174,2354,2309,0,0]Ag[477,13,478,0,3097,0,0]Ah[477,14,1735,0,1881,0,0]Al[3173,13,3174,3174,3098,0,0]Al[3173,14,1736,1736,1882,0,0]Ac[477,4,478,0,0,0,0]Ag[477,2,1735,1736,0,0,0]Ae[3173,4,3174,0,0,0,0]Ae[3173,2,1736,0,0,0,0]Ac[536,7,537,0,0,0,0];Ae[1127,2,1128,0,0,0,0]Ae[4917,7,4918,0,0,0,0]Am[477,13,478,3740,4898,1194,0]Am[477,13,478,4157,4484,1194,0]Am[477,13,478,4157,3121,1194,0]Am[477,13,478,4157,3324,1194,0]Am[477,13,478,4157,4483,1194,0]Am[477,13,478,4162,3342,1194,0]Am[477,13,478,4162,4248,1194,0]Aj[477,13,478,0,4583,1194,0]Ao[3173,13,3174,3741,4584,1194,0]Ag[477,14,478,0,2340,0,0]Al[3173,14,3174,3174,2341,0,0]Al[2029,14,2030,2030,2342,0,0]Ai[477,13,478,1126,520,0,0]Ah[477,14,3114,0,4233,0,0]Ai[3173,14,3115,0,4234,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]Al[2029,14,2030,2030,2328,0,0]Ag[477,14,478,0,3118,0,0]Ag[477,14,478,0,4587,0,0]Al[3173,14,3174,1419,3119,0,0]Al[3173,14,3174,1419,4588,0,0]3210Ah[477,12,1735,0,1877,0,0]Al[3173,12,1736,1736,1878,0,0]Ah[477,12,3735,0,3299,0,0]Aj[477,13,3735,0,501,3299,0]Ak[477,13,3735,0,1188,3299,0]Ak[477,13,3735,0,1886,3299,0]Al[3173,12,3736,3736,3300,0,0]Am[3173,13,3736,535,502,3300,0]Ao[3173,13,3736,1389,1189,3300,0]Ao[3173,13,3736,2408,1887,3300,0]Al[3173,15,3174,3147,4899,0,0]Al[3173,15,3174,1152,3122,0,0]Al[3173,15,3174,1157,3343,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]Ah[477,10,1735,1736,0,0,0]Af[3173,10,1736,0,0,0,0]Al[3173,15,3174,1798,4596,0,0]Ag[477,5,1735,1736,0,0,0]Ae[3173,5,1736,0,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]Ag[477,5,3735,2408,0,0,0]Ah[3173,5,3736,2408,0,0,0]Al[1127,15,1128,1128,4999,0,0]Af[477,5,478,3740,0,0,0]5454Ag[477,10,478,2036,0,0,0];:Aj[477,15,478,4162,3342,0,0]Af[477,5,478,4162,0,0,0]Al[3173,15,3174,1157,3343,0,0]Al[3173,15,3174,1157,4063,0,0]Al[3173,15,3174,2044,4082,0,0]Ah[3173,5,3174,2044,0,0,0]Aj[477,15,478,4157,4484,0,0]Al[3173,15,3174,1393,3310,0,0]Al[3173,15,3174,1798,2316,0,0]Ag[477,12,478,0,5117,0,0]Al[3173,12,3174,4387,5118,0,0]Ao[3173,13,3174,4387,5255,5118,0]Ao[3173,13,3174,4387,5260,5118,0]Ak[1228,14,1229,1229,141,0,0]An[1228,13,1229,1229,141,4452,0]An[2408,13,2409,2409,132,4452,0]Ak[2408,14,2409,2409,132,0,0]Ao[1127,13,1128,1128,3086,4452,0]An[1127,13,1725,1725,142,4452,0]An[1127,13,1824,1824,143,4452,0]Aj[477,13,478,0,1480,3294,0]Aj[477,13,478,0,1463,3294,0]Aj[477,13,478,0,4064,3294,0]Aj[477,13,478,0,4203,3294,0]Ai[477,13,478,0,526,3294,0]Ai[477,13,478,0,133,3294,0]Aj[477,13,478,0,2330,3294,0]Ao[3173,13,3174,1856,1481,3294,0]Ao[3173,13,3174,3147,4899,3294,0]Ao[3173,13,3174,1403,1464,3294,0]Ao[3173,13,3174,1152,3122,3294,0]Ao[3173,13,3174,1152,4374,3294,0]Ao[3173,13,3174,1157,3343,3294,0]Ao[3173,13,3174,3276,4065,3294,0]Ao[3173,13,3174,2354,4793,3294,0]Ao[3173,13,3174,1798,4894,3294,0]Ao[3173,13,3174,1829,4204,3294,0]An[3173,13,3174,3174,527,3294,0]An[3173,13,3174,3174,134,3294,0]Ao[3173,13,3174,3174,2331,3294,0]Aj[477,13,478,2353,2308,0,0]Al[3173,13,3174,2354,2309,0,0]Aj[477,13,478,2385,2312,0,0]Ah[477,12,1735,0,3109,0,0]Ah[477,12,1735,0,3102,0,0]Al[3173,13,3174,2354,2313,0,0]Al[3173,12,1736,1736,3110,0,0]Al[3173,12,1736,1736,3103,0,0]Ao[1228,13,1417,1417,4083,1872,0]Ao[1228,13,1417,1417,1221,1872,0]Al[536,13,537,537,4048,1872,0]Aj[477,13,478,0,4338,1872,0]Am[477,13,478,2353,4347,1872,0]Aj[477,13,478,0,1480,1872,0]Aj[477,13,478,0,1463,1872,0]Aj[477,13,478,0,3317,1872,0]Aj[477,13,478,0,3290,1872,0]Aj[477,13,478,0,2343,1872,0]Aj[477,13,478,0,3303,1872,0]Aj[477,13,478,0,3118,1872,0]Aj[477,13,478,0,4587,1872,0]Aj[477,13,478,0,4194,1872,0]Aj[477,13,478,0,4606,1872,0]Aj[477,13,478,0,4924,1872,0]Aj[477,13,478,0,4939,1872,0]Aj[477,13,478,0,4064,1872,0]Aj[477,13,478,0,4460,1872,0]Aj[477,13,478,0,5126,1872,0]Aj[477,13,478,0,4821,1872,0]Ak[477,13,3735,0,3286,1872,0]Ak[477,13,3735,0,4055,1872,0]Ak[477,13,3735,0,4366,1872,0]Ak[477,13,3735,0,4885,1872,0]Ao[3173,13,3174,4616,4339,1872,0]Ao[3173,13,3174,4387,5255,1872,0]Ao[3173,13,3174,1856,1481,1872,0]Ao[3173,13,3174,1403,1464,1872,0]Ao[3173,13,3174,1403,3318,1872,0]Ao[3173,13,3174,1403,3293,1872,0]Ao[3173,13,3174,1419,2344,1872,0]Ao[3173,13,3174,1419,3304,1872,0]Ao[3173,13,3174,1419,3119,1872,0]Ao[3173,13,3174,1419,4588,1872,0]Ao[3173,13,3174,2044,4485,1872,0]Ao[3173,13,3174,2044,4198,1872,0]Ao[3173,13,3174,3276,4607,1872,0]Ao[3173,13,3174,3276,4925,1872,0]Ao[3173,13,3174,3276,4940,1872,0]Ao[3173,13,3174,3276,4065,1872,0]Ao[3173,13,3174,1733,4461,1872,0]Ao[3173,13,3174,1733,5127,1872,0]Ao[3173,13,3174,4387,5260,1872,0]Ao[3173,13,3174,4995,4822,1872,0]An[3173,13,3736,535,3287,1872,0]Ao[3173,13,3736,1389,4056,1872,0]Ao[3173,13,3736,2408,4367,1872,0]Ao[3173,13,3736,3736,4886,1872,0]Ao[2029,13,2030,2030,2328,1872,0]Ao[1127,13,1418,1418,1222,1872,0]Ao[1127,13,1418,1418,4592,1872,0]Al[3173,13,3174,1829,4346,0,0]Al[3173,13,3174,1798,4369,0,0]Al[3173,13,3174,1798,4596,0,0]Al[3173,14,3174,1798,5160,0,0]Ai[3173,14,4893,0,4961,0,0]Ah[3173,14,4595,0,516,0,0]Ai[3173,14,4595,0,1216,0,0]Ai[3173,14,4595,0,1218,0,0]Ai[3173,14,4595,0,4827,0,0]Ah[477,12,1735,0,3109,0,0]Ah[477,12,1735,0,3102,0,0]Al[3173,12,1736,1736,3110,0,0]Al[3173,12,1736,1736,3103,0,0]Aj[477,13,478,4157,3322,0,0]Aj[477,13,478,4157,5008,0,0]Ah[477,13,3735,0,3299,0,0]Aj[477,13,3735,0,501,3299,0]Ak[477,13,3735,0,1188,3299,0]Ak[477,13,3735,0,1886,3299,0]Al[3173,13,3736,3736,3300,0,0]Am[3173,13,3736,535,502,3300,0]Ao[3173,13,3736,1389,1189,3300,0]Ao[3173,13,3736,2408,1887,3300,0]Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]Ab[2029,3,0,0,0,0,0]:6Aj[477,13,478,3740,3297,0,0]Aj[477,13,478,1126,2324,0,0]Al[3173,14,3174,1798,5160,0,0]Ai[3173,14,4893,0,4961,0,0]Ah[3173,14,4595,0,516,0,0]Ai[3173,14,4595,0,1216,0,0]Ai[3173,14,4595,0,4827,0,0]Aj[477,13,478,4162,3328,0,0]Al[3173,13,3174,2044,3329,0,0]Ak[1228,14,1229,1229,141,0,0]Al[1228,14,1417,1417,1221,0,0]Al[3173,13,3174,3741,3298,0,0]Ah[477,12,1735,0,3109,0,0]Al[3173,12,1736,1736,3110,0,0]Am[1228,13,1229,1229,141,128,0]An[1228,13,1229,1229,4075,128,0]An[1228,13,1229,1229,4047,128,0]An[1228,13,1417,1417,4083,128,0]An[1228,13,1417,1417,1221,128,0]An[1228,13,1417,1417,4486,128,0]Ak[536,13,537,537,4048,128,0]An[4088,13,4089,4089,5161,128,0]Al[477,13,478,3740,4611,128,0]Al[477,13,478,3740,2323,128,0]Ai[477,13,478,0,4338,128,0]Al[477,13,478,4270,5065,128,0]Al[477,13,478,1126,2324,128,0]Al[477,13,478,1126,4785,128,0]Al[477,13,478,3740,3295,128,0]Al[477,13,478,3740,3297,128,0]Al[477,13,478,3740,1889,128,0]Al[477,13,478,3740,1459,128,0]Al[477,13,478,3740,3076,128,0]Al[477,13,478,3740,2296,128,0]Al[477,13,478,3740,4232,128,0]Al[477,13,478,3740,4239,128,0]Al[477,13,478,3740,1450,128,0]Al[477,13,478,3740,5072,128,0]Al[477,13,478,3740,4898,128,0]Al[477,13,478,2353,2308,128,0]Al[477,13,478,2353,4347,128,0]Al[477,13,478,2353,1437,128,0]Al[477,13,478,4270,1458,128,0]Al[477,13,478,4270,3316,128,0]Al[477,13,478,4270,5000,128,0]Al[477,13,478,2385,2312,128,0]Al[477,13,478,2385,3325,128,0]Al[477,13,478,2385,4465,128,0]Ak[477,13,478,1126,520,128,0]Al[477,13,478,1126,4491,128,0]Al[477,13,478,4157,3322,128,0]Al[477,13,478,4157,5008,128,0]Al[477,13,478,4157,4484,128,0]Al[477,13,478,4157,3121,128,0]Al[477,13,478,4157,3324,128,0]Al[477,13,478,4157,4483,128,0]Al[477,13,478,1431,4249,128,0]Al[477,13,478,1169,4078,128,0]Al[477,13,478,4162,3328,128,0]Al[477,13,478,4162,3342,128,0]Al[477,13,478,4162,4248,128,0]Al[477,13,478,4162,1903,128,0]Ai[477,13,478,0,3319,128,0]Ai[477,13,478,0,4378,128,0]Ai[477,13,478,0,4795,128,0]Ai[477,13,478,0,4603,128,0]Ai[477,13,478,0,4222,128,0]Ai[477,13,478,0,4474,128,0]Ai[477,13,478,0,1480,128,0]Ai[477,13,478,0,5069,128,0]Ai[477,13,478,0,1184,128,0]Ai[477,13,478,0,1463,128,0]Ai[477,13,478,0,3317,128,0]Ai[477,13,478,0,3116,128,0]Ai[477,13,478,0,4954,128,0]Ai[477,13,478,0,3290,128,0]Ai[477,13,478,0,2343,128,0]Ai[477,13,478,0,3303,128,0]Ai[477,13,478,0,3118,128,0]Ai[477,13,478,0,4587,128,0]Ai[477,13,478,0,4194,128,0]Ai[477,13,478,0,4583,128,0]Ai[477,13,478,0,3112,128,0]Ai[477,13,478,0,4450,128,0]Ai[477,13,478,0,4597,128,0]Ai[477,13,478,0,4606,128,0]Ai[477,13,478,0,4924,128,0]Ai[477,13,478,0,4939,128,0]Ai[477,13,478,0,4064,128,0]Ai[477,13,478,0,4460,128,0]Ai[477,13,478,0,4242,128,0]Ai[477,13,478,0,5126,128,0]Ai[477,13,478,0,4188,128,0]Ai[477,13,478,0,4050,128,0]Ai[477,13,478,0,3099,128,0]Ai[477,13,478,0,4580,128,0]Ai[477,13,478,0,4787,128,0]Ai[477,13,478,0,4200,128,0]Ai[477,13,478,0,4203,128,0]Ai[477,13,478,0,4208,128,0]Ai[477,13,478,0,4959,128,0]Ai[477,13,478,0,3306,128,0]Ai[477,13,478,0,4821,128,0]Ah[477,13,478,0,526,128,0]Ah[477,13,478,0,133,128,0]Ai[477,13,478,0,2330,128,0]Ai[477,13,478,0,2340,128,0]Ai[477,13,478,0,3097,128,0]Ai[477,13,3735,0,501,128,0]Aj[477,13,3735,0,3286,128,0]Aj[477,13,3735,0,1188,128,0]Aj[477,13,3735,0,4055,128,0]Aj[477,13,3735,0,1886,128,0]Aj[477,13,3735,0,4366,128,0]Aj[477,13,3735,0,4885,128,0]Aj[477,13,1735,0,1881,128,0]Ai[477,13,1735,0,514,128,0]Aj[477,13,1735,0,5223,128,0]Aj[477,13,1735,0,4053,128,0]Aj[477,13,1735,0,4963,128,0]Aj[477,13,1818,0,3337,128,0]Aj[477,13,1818,0,4349,128,0]Aj[477,13,1818,0,1201,128,0]Aj[477,13,1818,0,4817,128,0]Aj[477,13,1818,0,5063,128,0]Aj[477,13,1818,0,4819,128,0]Am[2408,13,2409,2409,132,128,0]An[2408,13,2409,2409,2299,128,0]An[3173,13,3174,4616,4339,128,0]An[3173,13,3174,3147,3089,128,0]An[3173,13,3174,1152,1446,128,0]An[3173,13,3174,2044,1907,128,0]An[3173,13,3174,2044,3323,128,0]An[3173,13,3174,2044,3329,128,0]An[3173,13,3174,3741,3298,128,0]An[3173,13,3174,1148,3320,128,0]An[3173,13,3174,1733,4379,128,0]An[3173,13,3174,2354,2309,128,0]An[3173,13,3174,2354,4345,128,0]An[3173,13,3174,2354,4796,128,0]An[3173,13,3174,2354,4357,128,0]An[3173,13,3174,1798,5286,128,0]An[3173,13,3174,4387,5255,128,0]An[3173,13,3174,1430,4604,128,0]An[3173,13,3174,1430,4223,128,0]An[3173,13,3174,1430,4475,128,0]An[3173,13,3174,1856,1481,128,0]An[3173,13,3174,1856,5070,128,0]An[3173,13,3174,3138,1185,128,0]An[3173,13,3174,3147,4899,128,0]An[3173,13,3174,1403,1464,128,0]An[3173,13,3174,1403,4238,128,0]An[3173,13,3174,1403,3318,128,0]An[3173,13,3174,1403,3117,128,0]An[3173,13,3174,1403,4897,128,0]An[3173,13,3174,1403,4955,128,0]An[3173,13,3174,1403,3293,128,0]An[3173,13,3174,1419,2344,128,0]An[3173,13,3174,1419,3304,128,0]An[3173,13,3174,1419,3119,128,0]An[3173,13,3174,1419,4588,128,0]An[3173,13,3174,1152,3122,128,0]An[3173,13,3174,1152,4374,128,0]An[3173,13,3174,1157,3343,128,0]An[3173,13,3174,1157,4063,128,0]An[3173,13,3174,2044,4082,128,0]An[3173,13,3174,2044,4485,128,0]An[3173,13,3174,2044,4198,128,0]An[3173,13,3174,3741,4584,128,0]An[3173,13,3174,3741,3113,128,0]An[3173,13,3174,3741,4451,128,0]An[3173,13,3174,3741,5157,128,0]An[3173,13,3174,3741,1206,128,0]An[3173,13,3174,1148,4598,128,0]An[3173,13,3174,3276,4607,128,0]An[3173,13,3174,3276,4925,128,0]An[3173,13,3174,3276,4940,128,0]An[3173,13,3174,3276,4065,128,0]An[3173,13,3174,1733,4461,128,0]An[3173,13,3174,1733,4244,128,0]An[3173,13,3174,1733,5127,128,0]An[3173,13,3174,1733,4190,128,0]An[3173,13,3174,1733,4051,128,0]An[3173,13,3174,1393,3100,128,0]An[3173,13,3174,1393,4466,128,0]An[3173,13,3174,1393,3310,128,0]An[3173,13,3174,1393,3332,128,0]An[3173,13,3174,1393,4205,128,0]An[3173,13,3174,1393,4582,128,0]An[3173,13,3174,1393,4789,128,0]An[3173,13,3174,1393,2302,128,0]An[3173,13,3174,2354,4793,128,0]An[3173,13,3174,2354,2313,128,0]An[3173,13,3174,2354,4201,128,0]An[3173,13,3174,2354,4045,128,0]An[3173,13,3174,2354,3326,128,0]An[3173,13,3174,1798,4369,128,0]An[3173,13,3174,1798,4596,128,0]An[3173,13,3174,1798,5160,128,0]An[3173,13,3174,1798,2316,128,0]An[3173,13,3174,1798,4894,128,0]An[3173,13,3174,1829,4586,128,0]An[3173,13,3174,1829,4346,128,0]An[3173,13,3174,1829,4204,128,0]An[3173,13,3174,1829,4209,128,0]Ak[3173,13,3174,0,4489,128,0]Ak[3173,13,3174,0,4960,128,0]An[3173,13,3174,4387,3307,128,0]An[3173,13,3174,4387,5260,128,0]An[3173,13,3174,4995,4822,128,0]Am[3173,13,3174,3174,527,128,0]Am[3173,13,3174,3174,134,128,0]An[3173,13,3174,3174,2331,128,0]An[3173,13,3174,3174,2341,128,0]An[3173,13,3174,3174,3098,128,0]An[3173,13,1731,1731,4380,128,0]An[3173,13,5111,5111,5007,128,0]An[3173,13,5111,5111,1203,128,0]An[3173,13,1736,1736,1882,128,0]Am[3173,13,1736,1736,515,128,0]An[3173,13,1736,1736,5224,128,0]An[3173,13,1736,1736,4054,128,0]An[3173,13,1736,1736,4966,128,0]An[3173,13,1819,1819,3338,128,0]An[3173,13,1819,1819,4350,128,0]Ak[3173,13,1819,0,1202,128,0]An[3173,13,1819,1819,4818,128,0]An[3173,13,1819,1819,5064,128,0]An[3173,13,1819,1819,4820,128,0]Al[3173,13,3736,535,502,128,0]Am[3173,13,3736,535,3287,128,0]An[3173,13,3736,1389,1189,128,0]An[3173,13,3736,1389,4056,128,0]An[3173,13,3736,2408,1887,128,0]An[3173,13,3736,2408,4367,128,0]An[3173,13,3736,3736,4886,128,0]An[3173,13,4853,4853,1204,128,0]An[3173,13,4853,4853,1207,128,0]An[2029,13,2030,2030,3341,128,0]Am[2029,13,2030,431,4490,128,0]An[2029,13,2030,2030,2328,128,0]An[2029,13,2030,2030,2342,128,0]An[2029,13,2030,2030,4210,128,0]An[1127,13,1128,1128,3086,128,0]An[1127,13,1128,1128,4999,128,0]Am[1127,13,1725,1725,142,128,0]An[1127,13,1418,1418,1222,128,0]An[1127,13,1418,1418,4084,128,0]An[1127,13,1418,1418,4592,128,0]An[1127,13,4305,4305,5209,128,0]An[1127,13,4305,4305,5210,128,0]Am[1127,13,1824,1824,143,128,0]Aj[477,13,478,3740,5072,0,0]Aj[477,13,478,1126,4491,0,0]Ag[477,13,478,0,4188,0,0]Al[3173,13,3174,1733,4190,0,0]Al[3173,14,3174,1798,5160,0,0]Ai[3173,14,4893,0,4961,0,0]Ah[3173,14,4595,0,516,0,0]Ai[3173,14,4595,0,1216,0,0]Af[477,5,478,3740,0,0,0]Al[1228,15,1229,1229,4047,0,0]Ae[2408,6,2409,0,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]Ah[3173,5,3174,1393,0,0,0]Aj[477,15,478,3740,4898,0,0]Al[3173,15,3174,3147,4899,0,0]Af[477,5,478,2353,0,0,0]Ah[3173,5,3174,2354,0,0,0]Al[3173,15,3174,2044,4082,0,0]Af[477,5,478,2385,0,0,0]2Ah[3173,6,3174,1798,0,0,0]Ag[477,19,478,0,1480,0,0]Al[3173,19,3174,1856,1481,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]=Ah[477,12,1735,0,4243,0,0]>Ak[477,13,1818,0,4819,4243,0]>Al[3173,12,1736,1736,4245,0,0]>Ao[3173,13,1819,1819,4820,4245,0]Ao[1127,13,4305,4305,5210,4245,0]An[2408,13,2409,2409,132,4066,0]Ao[2408,13,2409,2409,2299,4066,0]Ag[477,14,478,0,3118,0,0]Ag[477,14,478,0,4587,0,0]Al[3173,14,3174,1419,3119,0,0]Al[3173,14,3174,1419,4588,0,0]Ag[477,13,478,0,4242,0,0]Al[3173,13,3174,1733,4244,0,0]Aj[477,13,478,2036,4448,0,0]0Ak[2029,13,2030,431,4490,0,0]Ai[477,13,478,1126,520,0,0]Al[3173,13,3174,3741,3298,0,0]Al[1127,13,1418,1418,4592,0,0]Ae[1127,2,1128,0,0,0,0]Aj[477,13,478,2385,2312,0,0]Al[3173,13,3174,2354,2313,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]6Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]32Ak[1228,14,1229,1229,141,0,0]Ag[477,14,478,0,2340,0,0]Al[3173,14,3174,3174,2341,0,0]Al[2029,14,2030,2030,2342,0,0]Ah[477,14,3735,0,4885,0,0]Al[3173,14,3736,3736,4886,0,0]Al[3173,13,3174,2044,1907,0,0]Al[3173,13,3174,4387,5255,0,0]Al[3173,13,3174,4387,5260,0,0]Aj[477,13,478,4162,3328,0,0]Al[3173,13,3174,2044,3329,0,0]>=Al[1228,14,1417,1417,1221,0,0]Ai[536,14,537,537,4048,0,0]Ah[477,13,1818,0,4817,0,0]Ah[477,13,1818,0,4819,0,0]Al[3173,13,1819,1819,4818,0,0]Al[3173,13,1819,1819,4820,0,0]6Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]Af[477,5,478,4616,0,0,0]Ah[3173,5,3174,4616,0,0,0]Ag[477,15,478,0,4583,0,0]Ag[477,15,478,0,4200,0,0]Al[3173,15,3174,3741,4584,0,0]Al[3173,15,3174,2354,4201,0,0]Ah[3173,6,3174,2354,0,0,0]Ah[3173,5,3174,1829,0,0,0]Af[477,5,478,2353,0,0,0]Aj[477,15,478,4157,4483,0,0]Ag[477,5,1818,1819,0,0,0]Ae[3173,5,1819,0,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Ah[3173,5,3174,2354,0,0,0]Al[3173,15,3174,1798,4894,0,0]?=5454Al[3173,15,3174,2354,4345,0,0]Ag[477,5,3735,2408,0,0,0]Ah[3173,5,3736,2408,0,0,0]Ah[3173,5,3174,1798,0,0,0]98Ah[3173,6,3174,1152,0,0,0]:9Af[477,5,478,1733,0,0,0]Ah[3173,5,3174,1733,0,0,0]Ae[3173,5,1731,0,0,0,0]Al[3173,13,3174,3741,3298,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]Al[1127,14,1128,1128,3086,0,0]Ae[3173,2,3174,0,0,0,0]Ag[477,13,478,0,4460,0,0]Al[3173,13,3174,1733,4461,0,0]Aj[477,12,478,2036,4448,0,0]Ab[4390,3,0,0,0,0,0]Ak[1228,14,1229,1229,141,0,0]Al[1228,14,1417,1417,4083,0,0]98Aj[477,13,478,2385,3325,0,0]Al[3173,13,3174,2354,3326,0,0]Ag[477,13,478,0,4924,0,0]Al[3173,13,3174,3276,4925,0,0];==;:Ak[1228,14,1229,1229,141,0,0]21Al[1127,14,1128,1128,3086,0,0]Aj[477,13,478,4157,3322,0,0]Aj[477,13,478,4157,5008,0,0]Al[3173,13,3174,2044,3323,0,0]Aj[477,13,478,3740,3295,0,0]Aj[477,13,478,2385,2312,0,0]Ai[477,13,478,1126,520,0,0]Al[3173,13,3174,2354,2313,0,0]<;Ag[477,13,478,0,1480,0,0]Ag[477,13,478,0,1463,0,0]Al[3173,13,3174,1856,1481,0,0]Al[3173,13,3174,1403,1464,0,0]20Ae[1228,7,1417,0,0,0,0]?>Al[1228,14,1417,1417,1221,0,0]Ao[1127,13,1128,1128,4999,3127,0]Ag[477,15,478,0,3099,0,0]Af[477,5,478,1393,0,0,0]Al[3173,15,3174,1393,3100,0,0]Ah[3173,5,3174,1393,0,0,0]Af[477,6,478,3741,0,0,0]Ah[3173,6,3174,3741,0,0,0]Al[3173,15,3174,2354,4345,0,0]Ah[3173,5,3174,1829,0,0,0]Af[477,5,478,1419,0,0,0]Ah[3173,5,3174,1419,0,0,0]97Ae[1127,5,1418,0,0,0,0]:8Ah[3173,6,3174,1798,0,0,0]Af[477,5,478,1148,0,0,0]Ah[3173,5,3174,1148,0,0,0]Al[3173,15,3174,1403,4897,0,0]Af[477,5,478,1430,0,0,0]Ah[3173,5,3174,1430,0,0,0]Af[477,5,478,3276,0,0,0]Ah[3173,5,3174,3276,0,0,0]Ag[477,15,478,0,4583,0,0]Al[3173,15,3174,3741,4584,0,0]Af[477,5,478,3740,0,0,0]Aj[477,15,478,4157,3121,0,0]Aj[477,15,478,4157,3324,0,0]Aj[477,15,478,4162,3342,0,0]Aj[477,15,478,4162,4248,0,0]An[1228,13,1229,1229,141,4344,0]Ao[1228,13,1229,1229,4075,4344,0]Ao[1228,13,1229,1229,4047,4344,0]Am[477,13,478,3740,3297,4344,0]Am[477,13,478,3740,1450,4344,0]Am[477,13,478,3740,5072,4344,0]Am[477,13,478,3740,4898,4344,0]Am[477,13,478,2353,2308,4344,0]Am[477,13,478,2353,4347,4344,0]Am[477,13,478,4270,1458,4344,0]Al[477,13,478,1126,520,4344,0]Am[477,13,478,1126,4491,4344,0]Am[477,13,478,4157,4484,4344,0]Am[477,13,478,4157,3121,4344,0]Am[477,13,478,4157,3324,4344,0]Am[477,13,478,4157,4483,4344,0]Am[477,13,478,4162,3342,4344,0]Am[477,13,478,4162,4248,4344,0]Aj[477,13,478,0,1480,4344,0]Aj[477,13,478,0,5069,4344,0]Aj[477,13,478,0,1463,4344,0]Aj[477,13,478,0,3317,4344,0]Aj[477,13,478,0,3116,4344,0]Aj[477,13,478,0,4954,4344,0]Aj[477,13,478,0,3290,4344,0]Aj[477,13,478,0,2343,4344,0]Aj[477,13,478,0,3303,4344,0]Aj[477,13,478,0,3118,4344,0]Aj[477,13,478,0,4587,4344,0]Aj[477,13,478,0,4194,4344,0]Aj[477,13,478,0,4583,4344,0]Aj[477,13,478,0,3112,4344,0]Aj[477,13,478,0,4450,4344,0]Aj[477,13,478,0,4606,4344,0]Aj[477,13,478,0,4924,4344,0]Aj[477,13,478,0,4939,4344,0]Aj[477,13,478,0,4064,4344,0]Aj[477,13,478,0,4460,4344,0]Aj[477,13,478,0,4242,4344,0]Aj[477,13,478,0,5126,4344,0]Aj[477,13,478,0,4188,4344,0]Aj[477,13,478,0,4050,4344,0]Aj[477,13,478,0,3099,4344,0]Aj[477,13,478,0,4580,4344,0]Aj[477,13,478,0,4787,4344,0]Aj[477,13,478,0,4200,4344,0]Aj[477,13,478,0,4203,4344,0]Aj[477,13,478,0,4208,4344,0]Aj[477,13,478,0,4959,4344,0]Aj[477,13,478,0,3306,4344,0]Ai[477,13,478,0,526,4344,0]Ai[477,13,478,0,133,4344,0]Aj[477,13,478,0,2330,4344,0]Aj[477,13,478,0,3097,4344,0]Aj[477,13,3735,0,501,4344,0]Ak[477,13,3735,0,3286,4344,0]Ak[477,13,3735,0,1188,4344,0]Ak[477,13,3735,0,4055,4344,0]Ak[477,13,3735,0,1886,4344,0]Ak[477,13,3735,0,4366,4344,0]Ak[477,13,1818,0,1201,4344,0]Ak[477,13,1818,0,4817,4344,0]Ak[477,13,1818,0,5063,4344,0]Ak[477,13,1818,0,4819,4344,0]Ao[3173,13,3174,1856,1481,4344,0]Ao[3173,13,3174,1856,5070,4344,0]Ao[3173,13,3174,3147,4899,4344,0]Ao[3173,13,3174,1403,1464,4344,0]Ao[3173,13,3174,1403,4238,4344,0]Ao[3173,13,3174,1403,3318,4344,0]Ao[3173,13,3174,1403,3117,4344,0]Ao[3173,13,3174,1403,4897,4344,0]Ao[3173,13,3174,1403,4955,4344,0]Ao[3173,13,3174,1403,3293,4344,0]Ao[3173,13,3174,1419,2344,4344,0]Ao[3173,13,3174,1419,3304,4344,0]Ao[3173,13,3174,1419,3119,4344,0]Ao[3173,13,3174,1419,4588,4344,0]Ao[3173,13,3174,1152,3122,4344,0]Ao[3173,13,3174,1152,4374,4344,0]Ao[3173,13,3174,1157,3343,4344,0]Ao[3173,13,3174,1157,4063,4344,0]Ao[3173,13,3174,2044,4485,4344,0]Ao[3173,13,3174,2044,4198,4344,0]Ao[3173,13,3174,3741,4584,4344,0]Ao[3173,13,3174,3741,3113,4344,0]Ao[3173,13,3174,3741,4451,4344,0]Ao[3173,13,3174,3741,5157,4344,0]Ao[3173,13,3174,3276,4607,4344,0]Ao[3173,13,3174,3276,4925,4344,0]Ao[3173,13,3174,3276,4940,4344,0]Ao[3173,13,3174,3276,4065,4344,0]Ao[3173,13,3174,1733,4461,4344,0]Ao[3173,13,3174,1733,4244,4344,0]Ao[3173,13,3174,1733,5127,4344,0]Ao[3173,13,3174,1733,4190,4344,0]Ao[3173,13,3174,1733,4051,4344,0]Ao[3173,13,3174,1393,3100,4344,0]Ao[3173,13,3174,1393,3310,4344,0]Ao[3173,13,3174,1393,3332,4344,0]Ao[3173,13,3174,1393,4205,4344,0]Ao[3173,13,3174,1393,4582,4344,0]Ao[3173,13,3174,1393,4789,4344,0]Ao[3173,13,3174,1393,2302,4344,0]Ao[3173,13,3174,2354,4793,4344,0]Ao[3173,13,3174,2354,4201,4344,0]Ao[3173,13,3174,1798,4596,4344,0]Ao[3173,13,3174,1798,5160,4344,0]Ao[3173,13,3174,1798,2316,4344,0]Ao[3173,13,3174,1798,4894,4344,0]Ao[3173,13,3174,1829,4204,4344,0]Ao[3173,13,3174,1829,4209,4344,0]Al[3173,13,3174,0,4489,4344,0]Al[3173,13,3174,0,4960,4344,0]Ao[3173,13,3174,4387,3307,4344,0]Ao[3173,13,3174,4387,5260,4344,0]An[3173,13,3174,3174,527,4344,0]An[3173,13,3174,3174,134,4344,0]Ao[3173,13,3174,3174,2331,4344,0]Ao[3173,13,3174,3174,3098,4344,0]Ao[3173,13,1731,1731,4380,4344,0]Ao[3173,13,5111,5111,5007,4344,0]Ao[3173,13,5111,5111,1203,4344,0]Al[3173,13,1819,0,1202,4344,0]Ao[3173,13,1819,1819,4818,4344,0]Ao[3173,13,1819,1819,5064,4344,0]Ao[3173,13,1819,1819,4820,4344,0]Am[3173,13,3736,535,502,4344,0]An[3173,13,3736,535,3287,4344,0]Ao[3173,13,3736,1389,1189,4344,0]Ao[3173,13,3736,1389,4056,4344,0]Ao[3173,13,3736,2408,1887,4344,0]Ao[3173,13,3736,2408,4367,4344,0]Ao[3173,13,4853,4853,1207,4344,0]Ao[2029,13,2030,2030,2328,4344,0]Ao[2029,13,2030,2030,2342,4344,0]Ao[2029,13,2030,2030,4210,4344,0]Ao[1127,13,1128,1128,4999,4344,0]Ao[1127,13,1418,1418,4592,4344,0]Ao[1127,13,4305,4305,5209,4344,0]Ao[1127,13,4305,4305,5210,4344,0]Af[477,7,478,2036,0,0,0]Ag[477,13,478,0,4460,0,0]Al[3173,13,3174,1733,4461,0,0]Ah[477,12,1735,0,3109,0,0]Al[3173,12,1736,1736,3110,0,0]Al[2029,14,2030,2030,2328,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]10Ae[1127,7,1128,0,0,0,0]Aj[477,13,478,2353,2308,0,0]Ae[4390,7,4391,0,0,0,0]Ae[1228,7,1229,0,0,0,0]Al[3173,13,3174,3741,3298,0,0]Aj[477,13,478,3740,3297,0,0]1Al[3173,13,3174,2044,4485,0,0]Al[1228,13,1417,1417,1221,0,0]Al[1228,14,1417,1417,4083,0,0]Aj[477,12,478,2036,1898,0,0]Am[477,13,478,1431,4249,1898,0]Am[477,13,478,1169,4078,1898,0]Ah[477,12,1735,0,1899,0,0]Ak[477,13,1818,0,3337,1899,0]Al[3173,12,1736,1736,1900,0,0]Ao[3173,13,1819,1819,3338,1900,0]Af[477,5,478,1126,0,0,0]Ag[477,15,478,0,3099,0,0]Af[477,5,478,1393,0,0,0]Al[3173,15,3174,1393,3100,0,0]Ah[3173,5,3174,1393,0,0,0]Ag[477,15,478,0,4583,0,0]Al[3173,15,3174,3741,4584,0,0]Ah[3173,5,3174,2354,0,0,0]Al[3173,15,3174,3741,1206,0,0]Af[477,5,478,2354,0,0,0]2Ag[477,15,478,0,4200,0,0]Al[3173,15,3174,2354,4201,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]32Ag[477,15,478,0,3112,0,0]Al[3173,15,3174,3741,3113,0,0]Ag[477,15,478,0,3099,0,0]?0?0?43Ag[477,5,1818,1819,0,0,0]Ae[3173,5,1819,0,0,0,0]10Af[477,5,478,4995,0,0,0]Ah[3173,5,3174,4995,0,0,0]Al[3173,15,4853,4853,1204,0,0]7Al[3173,15,3174,1403,4897,0,0]7Al[3173,15,3174,1798,4596,0,0]Ag[477,12,478,0,2336,0,0]Aj[477,13,478,0,4338,2336,0]Aj[477,13,478,0,4222,2336,0]Al[3173,12,3174,1430,2337,0,0]Ao[3173,13,3174,4616,4339,2337,0]Ao[3173,13,3174,1430,4223,2337,0]Ae[4088,7,4089,0,0,0,0]Aj[477,13,478,3740,3297,0,0]Al[3173,13,3174,3741,3298,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]32An[1228,13,1229,1229,141,4462,0]An[2408,13,2409,2409,132,4462,0]Ao[2408,13,2409,2409,2299,4462,0]Ao[1127,13,1128,1128,3086,4462,0]An[1127,13,1725,1725,142,4462,0]An[1127,13,1824,1824,143,4462,0]Ak[2029,13,2030,431,4490,0,0]Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]:9Ae[3173,2,3174,0,0,0,0]Aj[477,13,478,2353,2308,0,0]Al[3173,13,3174,2354,2309,0,0]?>43Aj[477,13,478,4157,3322,0,0]Ae[4390,7,4391,0,0,0,0]7Al[1127,14,1128,1128,3086,0,0]80Ae[1228,7,1229,0,0,0,0]Ag[477,13,478,0,4242,0,0]Ag[477,13,478,0,4188,0,0]Al[3173,13,3174,1733,4244,0,0]Al[3173,13,3174,1733,4190,0,0]Ag[477,14,478,0,4242,0,0]Al[3173,14,3174,1733,4244,0,0]53Aj[477,13,478,4162,3328,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Ag[477,15,478,0,4583,0,0]Al[3173,15,3174,1403,4897,0,0]Al[3173,15,3174,3741,4584,0,0]Ah[477,10,3735,3736,0,0,0]Af[3173,10,3736,0,0,0,0]Ag[477,5,3735,3736,0,0,0]Ae[3173,5,3736,0,0,0,0]Ah[477,10,1735,1736,0,0,0]Af[3173,10,1736,0,0,0,0]Ag[477,15,478,0,4200,0,0]Al[3173,15,3174,2354,4201,0,0]Ag[477,10,478,3741,0,0,0]Ai[3173,10,3174,3741,0,0,0]Ah[3173,6,3174,1798,0,0,0]Ah[3173,6,3174,1403,0,0,0]Af[477,6,478,3740,0,0,0]Ah[3173,6,3174,3147,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]10Aj[477,15,478,3740,4898,0,0]Al[3173,15,3174,3147,4899,0,0]Ag[477,14,478,0,3118,0,0]Al[3173,14,3174,1419,3119,0,0]Ah[477,14,3735,0,4885,0,0]Al[3173,14,3736,3736,4886,0,0]Aj[477,13,478,3740,3297,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]10Al[3173,13,3174,3147,3089,0,0]Aj[477,13,478,2353,2308,0,0]Al[3173,13,3174,2354,2309,0,0]Ab[4917,3,0,0,0,0,0]Ao[1127,13,1128,1128,4999,3127,0]3Aj[477,13,478,4162,3328,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Af[477,5,478,3276,0,0,0]Ah[3173,5,3174,3276,0,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]54Ai[3173,10,3174,1798,0,0,0]Al[3173,15,3174,3741,1206,0,0]7600Al[3173,15,3174,1152,4374,0,0]4365Al[3173,15,4853,4853,1204,0,0]Ae[1127,9,1128,0,0,0,0]:9Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]<;<;Af[477,6,478,1403,0,0,0]Ah[3173,6,3174,1403,0,0,0]32Al[1228,15,1229,1229,4075,0,0]Ac[477,5,478,0,0,0,0]Ae[3173,5,3174,0,0,0,0]Ag[477,15,478,0,4200,0,0]Ag[477,5,1735,1736,0,0,0]Al[3173,15,3174,2354,4201,0,0]Al[3173,15,3174,1798,4894,0,0]Ae[3173,5,1736,0,0,0,0]An[1228,13,1229,1229,141,4452,0]An[2408,13,2409,2409,132,4452,0]Ao[1127,13,1128,1128,3086,4452,0]An[1127,13,1725,1725,142,4452,0]An[1127,13,1824,1824,143,4452,0]Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]Ag[477,13,478,0,1480,0,0]Al[3173,13,3174,1856,1481,0,0]Aj[477,14,478,2353,4347,0,0]Aj[477,13,478,3740,5072,0,0]Ag[477,13,478,0,4460,0,0]Al[3173,13,3174,1733,4461,0,0]Ag[477,13,478,0,4338,0,0]Al[3173,13,3174,4616,4339,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]7Al[3173,13,3174,3741,3298,0,0]Aj[477,13,478,3740,3297,0,0]Ah[477,13,1818,0,4817,0,0]Ah[477,13,1818,0,4819,0,0]Al[3173,13,1819,1819,4818,0,0]Al[3173,13,1819,1819,4820,0,0]45;:Ao[4088,13,4089,4089,5161,4953,0]Ag[477,13,478,0,4597,0,0]Al[3173,13,3174,1148,4598,0,0]Ae[1127,6,1128,0,0,0,0]Af[477,5,478,4270,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]10Ag[477,15,478,0,3112,0,0]Al[3173,15,3174,3741,3113,0,0]Ae[3173,5,5111,0,0,0,0]Af[477,5,478,4157,0,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]Al[3173,15,4853,4853,1204,0,0]Aj[477,14,478,2353,4347,0,0]Ai[477,13,478,0,526,3078,0]Ai[477,13,478,0,133,3078,0]An[3173,13,3174,3174,527,3078,0]An[3173,13,3174,3174,134,3078,0]Ao[2408,13,2409,2409,2299,3333,0]Ag[477,13,478,0,4939,0,0]Al[3173,13,3174,3276,4940,0,0]Ah[477,12,1735,0,1899,0,0]Ak[477,13,1818,0,3337,1899,0]Al[3173,12,1736,1736,1900,0,0]Ao[3173,13,1819,1819,3338,1900,0]Aj[477,13,478,3740,3297,0,0]Ag[477,12,478,0,4891,0,0]Al[3173,12,3174,3741,4892,0,0]Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]Ag[477,14,478,0,3118,0,0]Al[3173,14,3174,1419,3119,0,0]Ah[477,12,3735,0,3299,0,0]Aj[477,13,3735,0,501,3299,0]Ak[477,13,3735,0,1188,3299,0]Ak[477,13,3735,0,1886,3299,0]Al[3173,12,3736,3736,3300,0,0]Am[3173,13,3736,535,502,3300,0]Ao[3173,13,3736,1389,1189,3300,0]Ao[3173,13,3736,2408,1887,3300,0]Aj[477,14,478,2353,4347,0,0]Al[3173,13,3174,2044,1907,0,0]Ah[3173,2,4385,4161,0,0,0]Ae[1228,7,1229,0,0,0,0]==Ai[477,13,478,1126,520,0,0]Ak[2029,13,2030,431,4490,0,0]032Ah[477,12,1735,0,1899,0,0]Ak[477,13,1818,0,3337,1899,0]Ae[3173,2,3174,0,0,0,0]Al[3173,12,1736,1736,1900,0,0]Ao[3173,13,1819,1819,3338,1900,0]Ag[477,10,478,4387,0,0,0]Ai[3173,10,3174,4387,0,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]Ag[477,15,478,0,4200,0,0]Al[3173,15,3174,2354,4201,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Af[477,5,478,1733,0,0,0]Ah[3173,5,3174,1733,0,0,0]Ah[477,12,1735,0,3109,0,0]Al[3173,12,1736,1736,3110,0,0]Aj[477,13,478,2353,2308,0,0]Al[3173,13,3174,3741,3298,0,0]Al[3173,13,3174,2354,2309,0,0]Ah[477,13,3735,0,4366,0,0]Al[3173,13,3736,2408,4367,0,0]Aj[477,13,478,2036,3125,0,0]0Aj[477,13,478,3740,3297,0,0]5Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]Al[1127,14,1128,1128,3086,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Al[3173,15,4853,4853,1204,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]10Ag[477,15,478,0,4954,0,0]Ag[477,15,478,0,3112,0,0]Ag[477,15,478,0,4450,0,0]Al[3173,15,3174,1403,4955,0,0]Al[3173,15,3174,3741,3113,0,0]Al[3173,15,3174,3741,4451,0,0]Ah[3173,5,3174,3741,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]Ah[3173,5,3174,1798,0,0,0]Ae[4088,5,4089,0,0,0,0]Al[1127,13,1128,1128,3086,0,0]Aj[477,13,478,3740,3297,0,0]Ah[477,12,1735,0,1899,0,0]Ak[477,13,1818,0,3337,1899,0]Al[3173,12,1736,1736,1900,0,0]Ao[3173,13,1819,1819,3338,1900,0]Ah[3173,7,5042,5041,0,0,0]0Al[3173,13,3174,3741,3298,0,0]6Ai[477,13,478,1126,520,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]Ao[4088,13,4089,4089,5161,4953,0]0Ag[477,13,478,0,4188,0,0]Al[3173,13,3174,1733,4190,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]10Ag[477,13,478,0,4924,0,0]Al[3173,13,3174,3276,4925,0,0]Am[1228,13,1229,1229,141,499,0]Am[2408,13,2409,2409,132,499,0]An[1127,13,1128,1128,3086,499,0]Am[1127,13,1725,1725,142,499,0]Am[1127,13,1824,1824,143,499,0]??Ah[477,13,3735,0,3299,0,0]Aj[477,13,3735,0,501,3299,0]Al[3173,13,3736,3736,3300,0,0]Am[3173,13,3736,535,502,3300,0]Ai[477,13,478,1126,520,0,0]An[1228,13,1229,1229,141,4462,0]An[2408,13,2409,2409,132,4462,0]Ao[2408,13,2409,2409,2299,4462,0]Ao[1127,13,1128,1128,3086,4462,0]An[1127,13,1725,1725,142,4462,0]An[1127,13,1824,1824,143,4462,0]Ag[477,13,478,0,4242,0,0]Al[3173,13,3174,1733,4244,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Ae[1127,5,4305,0,0,0,0]0Ag[477,13,478,0,4460,0,0]Al[3173,13,3174,1733,4461,0,0]Ag[477,13,478,0,4188,0,0]Al[3173,13,3174,1733,4190,0,0]Aj[477,13,478,2353,2308,0,0]Aj[477,13,478,2385,2312,0,0]Al[3173,13,3174,2354,2313,0,0]Al[3173,13,3174,3741,3298,0,0]Ao[4088,13,4089,4089,5161,4953,0];:Ag[477,5,1735,1736,0,0,0]Ae[3173,5,1736,0,0,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]?>?>Aj[477,13,478,3740,3297,0,0]6Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]Ae[4088,7,4089,0,0,0,0]Ae[4390,7,4391,0,0,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]?>>>>>Aa[\"{BFLlB@Mb}\",[]]??????Aa[\"{BBChBFCl}\",[]]A`[\"{cc{}}\",[\"T\"]]00000A`[\"{IFfBCAb}\",[]]1A`[\"{KIbBFL`}\",[]]222222A`[\"{KIbABCf}\",[]]33Aa[\"{AJ@fAHCd}\",[]]44A`[\"{BBChIFf}\",[]]55555555A`[\"{BCMjIFd}\",[]]A`[\"{BBOhIFd}\",[]]A`[\"{BBNfIFd}\",[]]A`[\"{BEFlIFd}\",[]]A`[\"{BHGfIFd}\",[]]A`[\"{BEHbIFd}\",[]]A`[\"{BHMnIFd}\",[]]A`[\"{AIN`IFd}\",[]]=A`[\"{BHLlIFd}\",[]]A`[\"{BCOdIFd}\",[]]A`[\"{BEIlIFd}\",[]]A`[\"{cc{}}\",[\"T\"]]00000000000An[\"{{{AHO`{c}}}AJAjCNh}\",[\"S\"]]11111111111111Bm[\"{{{AHO`{KI`}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bk[\"{{{AHO`{KFn}}{AHO`{CGbc}}}ICjKF`}\",[\"H\"]]Bn[\"{{{AHO`{AOL`}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{B@Mf}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bm[\"{{{AHO`{DAl}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bm[\"{{{AHO`{A@j}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{ABCd}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{AHCb}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bm[\"{{{AHO`{KIb}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{BFDf}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bk[\"{{{AHO`{KG`}}{AHO`{CGbc}}}ICjKF`}\",[\"H\"]]Bn[\"{{{AHO`{AHFd}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{BBBl}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{AJAn}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{AOLb}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{BEGb}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{BFCl}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{B@Mh}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bm[\"{{{AHO`{DAn}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bm[\"{{{AHO`{A@l}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{ABCf}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{AHCd}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Aa[\"{{}c{}}\",[\"U\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n[\"{{}ICj}\",[]]0000Cc[\"{{{AHO`{CGbKDl}}KCb}{{KGd{{KFl{IDb}}AHFd}}}}\",[]]Cf[\"{{{AHO`{CGbBB@j}}}{{KFl{c}}}{}}\",[\"Iterator::Item\"]]Dl[\"{{{AHO`{BFBn}}ICh{AHO`{CGb{K@j{Oh}}}}}{{KGd{{AHO`{{K@j{Oh}}}}AOJj}}}}\",[]]Bi[\"{{{AHO`{CGbNNf}}A@l}{{KFl{ABCf}}}}\",[]]Dl[\"{{{AHO`{BFC`}}ICh{AHO`{CGb{K@j{Oh}}}}}{{KGd{{AHO`{{K@j{Oh}}}}AOJl}}}}\",[]]Bf[\"{{{AHO`{AILb}}KFn}{{KFl{DA`}}}}\",[]]Bf[\"{{{AHO`{DA`}}}{{KGd{ICjAIKd}}}}\",[]]A`[\"{AILnCNb}\",[]]Am[\"{{{AHO`{CGbAILd}}}ICj}\",[]]A`[\"{AIM`CNb}\",[]]Ck[\"{{{ABb{{AHO`{CGbAIJd}}}}{AHO`{CGbNIn}}}{{DAh{ICj}}}}\",[]]Dk[\"{{{ABb{{AHO`{CGbBD@f}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbABBf}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbABBh}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbAIKn}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{ABb{{AHO`{CGbNLb}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{ABb{{AHO`{CGbKFf}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbAH@h}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbAAO`}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbBAA`}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbBAAn}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{ABb{{AHO`{CGbKCj}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbBBNb}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{ABb{{AHO`{CGbNMn}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Bf[\"{{{AHO`{CGbAILd}}}{{KFl{IFl}}}}\",[]]De[\"{{{AHO`{CGbAIOd}}{AHO`{CGb{K@j{Oh}}}}}{{KGd{{KFl{KCb}}AHFb}}}}\",[]]Bm[\"{{{AHO`{CGbAIOf}}DC`}{{KGd{KDlBBBl}}}}\",[]]C`[\"{{DAn{AHO`{CGbc}}}{{KGd{BGAnIFf}}}A@f}\",[\"R\"]]Bf[\"{{{AHO`{CGbAHBb}}}{{KFl{ICn}}}}\",[]]Ec[\"{{{AHO`{BCAd}}AJAj{AHO`{CGb{K@j{AIMh}}}}{AHO`{CGb{K@j{ABC`}}}}}{{KGf{KCb}}}}\",[]]Cn[\"{{{AHO`{BFBn}}ICh{AHO`{CGb{ABh{Oh}}}}}{{KGd{ICjAOJj}}}}\",[]]Cn[\"{{{AHO`{BFC`}}ICh{AHO`{CGb{ABh{Oh}}}}}{{KGd{ICjAOJl}}}}\",[]]C`[\"{{{AHO`{CGbAHBb}}ICnDC`}{{KGd{ICjBFDf}}}}\",[]]Cc[\"{{{AHO`{BCAd}}AJAj{AHO`{ABDl}}}{{KGf{ICj}}}}\",[]]Bn[\"{{{AHO`{CGbBH@l}}CN`}{{AHO`{CGbBH@l}}}}\",[]]Bn[\"{{{AHO`{CGbBH@n}}CN`}{{AHO`{CGbBH@n}}}}\",[]]Aj[\"{{{AHO`{AILb}}}DAl}\",[]]Aj[\"{{{AHO`{AILd}}}DAn}\",[]]Cn[\"{{{AHO`{NJj}}{AHO`{{K@j{Oh}}}}{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]Cn[\"{{{AHO`{NJl}}{AHO`{{K@j{Oh}}}}{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]A`[\"{BC@lCNb}\",[]]A`[\"{ABDhKCb}\",[]]A`[\"{ABDjKCb}\",[]]o[\"{IHdDC`}\",[]]Bm[\"{{{AHO`{CGbAIOd}}KI`}{{KGd{ICjB@Ld}}}}\",[]]Bm[\"{{{AHO`{CGbBGB`}}KI`}{{KGd{ICjB@Ld}}}}\",[]]Bm[\"{{{AHO`{CGbAIOf}}KIb}{{KGd{ICjB@Ll}}}}\",[]]Bn[\"{{{AHO`{AJAf}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bn[\"{{{AHO`{BBOd}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bn[\"{{{AHO`{BEJj}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bn[\"{{{AHO`{AJAh}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bn[\"{{{AHO`{BBOf}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bn[\"{{{AHO`{BEJl}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bd[\"{{{AHO`{BEGf}}}{{KFl{BB@h}}}}\",[]]Bd[\"{{{AHO`{BEGh}}}{{KFl{BB@j}}}}\",[]]Bi[\"{{{A@b{BFBf}}NK`CMn}{{A@d{AILf}}}}\",[]]Bi[\"{{{A@b{AIJl}}NK`CMn}{{A@d{AILf}}}}\",[]]Bi[\"{{{A@b{AOJn}}NK`CMn}{{A@d{AILf}}}}\",[]]Bi[\"{{{A@b{BBAl}}NK`CMn}{{A@d{AILf}}}}\",[]]Bi[\"{{{A@b{BFBh}}NK`CMn}{{A@d{AILh}}}}\",[]]Bi[\"{{{A@b{AIJn}}NK`CMn}{{A@d{AILh}}}}\",[]]Bi[\"{{{A@b{AOK`}}NK`CMn}{{A@d{AILh}}}}\",[]]Bi[\"{{{A@b{BBAn}}NK`CMn}{{A@d{AILh}}}}\",[]]Aj[\"{BG@n{{A@b{BFBh}}}}\",[]]o[\"{ID`ICn}\",[]]A`[\"{ABDnCNb}\",[]]o[\"{IDbICn}\",[]]A`[\"{ABE`CNb}\",[]]Aa[\"{AOIjBCM`}\",[]]Ai[\"{{{AHO`{AAj}}}AAj}\",[]]Ak[\"{{{AHO`{AOMf}}}AOMf}\",[]]Ak[\"{{{AHO`{AOIn}}}AOIn}\",[]]Ak[\"{{{AHO`{AILb}}}AILb}\",[]]Ai[\"{{{AHO`{KEd}}}KEd}\",[]]Ak[\"{{{AHO`{BGJ`}}}BGJ`}\",[]]Ak[\"{{{AHO`{BFDd}}}BFDd}\",[]]Ak[\"{{{AHO`{AB@h}}}AB@h}\",[]]Ak[\"{{{AHO`{BAOf}}}BAOf}\",[]]Ai[\"{{{AHO`{KFd}}}KFd}\",[]]Ai[\"{{{AHO`{DA`}}}DA`}\",[]]Ak[\"{{{AHO`{BCAf}}}BCAf}\",[]]Ak[\"{{{AHO`{BC@h}}}BC@h}\",[]]Ak[\"{{{AHO`{AHFb}}}AHFb}\",[]]Ak[\"{{{AHO`{AIOh}}}AIOh}\",[]]Ak[\"{{{AHO`{BC@f}}}BC@f}\",[]]Ak[\"{{{AHO`{AJAl}}}AJAl}\",[]]Ak[\"{{{AHO`{BAC`}}}BAC`}\",[]]Ai[\"{{{AHO`{KI`}}}KI`}\",[]]Ak[\"{{{AHO`{BGIj}}}BGIj}\",[]]Ai[\"{{{AHO`{KFn}}}KFn}\",[]]Ak[\"{{{AHO`{AINj}}}AINj}\",[]]Ak[\"{{{AHO`{AHEh}}}AHEh}\",[]]Ak[\"{{{AHO`{BFKd}}}BFKd}\",[]]Ak[\"{{{AHO`{AIKd}}}AIKd}\",[]]Ak[\"{{{AHO`{ABDn}}}ABDn}\",[]]Ak[\"{{{AHO`{AILn}}}AILn}\",[]]Ak[\"{{{AHO`{AHEl}}}AHEl}\",[]]Ak[\"{{{AHO`{BCMf}}}BCMf}\",[]]Ak[\"{{{AHO`{B@Ld}}}B@Ld}\",[]]Ak[\"{{{AHO`{BCLn}}}BCLn}\",[]]Ak[\"{{{AHO`{AHE`}}}AHE`}\",[]]Ak[\"{{{AHO`{BBLd}}}BBLd}\",[]]Ak[\"{{{AHO`{BCOl}}}BCOl}\",[]]Ak[\"{{{AHO`{BFGh}}}BFGh}\",[]]Ak[\"{{{AHO`{BFIf}}}BFIf}\",[]]Ak[\"{{{AHO`{AOL`}}}AOL`}\",[]]Ak[\"{{{AHO`{BBMh}}}BBMh}\",[]]Ak[\"{{{AHO`{BABd}}}BABd}\",[]]Ak[\"{{{AHO`{BH@l}}}BH@l}\",[]]Ak[\"{{{AHO`{B@Kh}}}B@Kh}\",[]]Ak[\"{{{AHO`{AOJd}}}AOJd}\",[]]Ak[\"{{{AHO`{AHCf}}}AHCf}\",[]]Ak[\"{{{AHO`{BCLh}}}BCLh}\",[]]Ak[\"{{{AHO`{BEFf}}}BEFf}\",[]]Ak[\"{{{AHO`{B@M`}}}B@M`}\",[]]Ak[\"{{{AHO`{B@Mf}}}B@Mf}\",[]]Ak[\"{{{AHO`{B@N`}}}B@N`}\",[]]Ak[\"{{{AHO`{BFKn}}}BFKn}\",[]]Ak[\"{{{AHO`{AIMd}}}AIMd}\",[]]Ai[\"{{{AHO`{DAl}}}DAl}\",[]]Ai[\"{{{AHO`{A@j}}}A@j}\",[]]Ak[\"{{{AHO`{ABCd}}}ABCd}\",[]]Ak[\"{{{AHO`{AHCb}}}AHCb}\",[]]Ai[\"{{{AHO`{CNj}}}CNj}\",[]]Ak[\"{{{AHO`{AIJl}}}AIJl}\",[]]Ai[\"{{{AHO`{IDh}}}IDh}\",[]]Ak[\"{{{AHO`{AOJn}}}AOJn}\",[]]Ai[\"{{{AHO`{NKl}}}NKl}\",[]]Ak[\"{{{AHO`{BBAl}}}BBAl}\",[]]Ai[\"{{{AHO`{IFb}}}IFb}\",[]]Ak[\"{{{AHO`{BEJb}}}BEJb}\",[]]Ak[\"{{{AHO`{BGHn}}}BGHn}\",[]]Ak[\"{{{AHO`{BEJf}}}BEJf}\",[]]Ai[\"{{{AHO`{KIb}}}KIb}\",[]]Ak[\"{{{AHO`{BGIl}}}BGIl}\",[]]Ak[\"{{{AHO`{BFDf}}}BFDf}\",[]]Ai[\"{{{AHO`{KG`}}}KG`}\",[]]Ak[\"{{{AHO`{BAAl}}}BAAl}\",[]]Ak[\"{{{AHO`{AINl}}}AINl}\",[]]Ak[\"{{{AHO`{AHEj}}}AHEj}\",[]]Ak[\"{{{AHO`{BFDb}}}BFDb}\",[]]Ak[\"{{{AHO`{BFKf}}}BFKf}\",[]]Ak[\"{{{AHO`{AIKj}}}AIKj}\",[]]Ak[\"{{{AHO`{ABE`}}}ABE`}\",[]]Ak[\"{{{AHO`{AIM`}}}AIM`}\",[]]Ak[\"{{{AHO`{AHEn}}}AHEn}\",[]]Ak[\"{{{AHO`{BCMh}}}BCMh}\",[]]Ak[\"{{{AHO`{AHFd}}}AHFd}\",[]]Ak[\"{{{AHO`{BBBl}}}BBBl}\",[]]Ak[\"{{{AHO`{AJAn}}}AJAn}\",[]]Ak[\"{{{AHO`{AOKn}}}AOKn}\",[]]Ak[\"{{{AHO`{BC@j}}}BC@j}\",[]]Ak[\"{{{AHO`{B@Ll}}}B@Ll}\",[]]Ak[\"{{{AHO`{BCM`}}}BCM`}\",[]]Ak[\"{{{AHO`{AHEb}}}AHEb}\",[]]Ak[\"{{{AHO`{BBLf}}}BBLf}\",[]]Ak[\"{{{AHO`{BHDj}}}BHDj}\",[]]Ak[\"{{{AHO`{BCOn}}}BCOn}\",[]]Ak[\"{{{AHO`{BFGj}}}BFGj}\",[]]Ak[\"{{{AHO`{BFIh}}}BFIh}\",[]]Ak[\"{{{AHO`{AOLb}}}AOLb}\",[]]Ak[\"{{{AHO`{BBMj}}}BBMj}\",[]]Ak[\"{{{AHO`{BABh}}}BABh}\",[]]Ak[\"{{{AHO`{BH@n}}}BH@n}\",[]]Ak[\"{{{AHO`{B@Kl}}}B@Kl}\",[]]Ak[\"{{{AHO`{AOJf}}}AOJf}\",[]]Ak[\"{{{AHO`{AHCh}}}AHCh}\",[]]Ak[\"{{{AHO`{AIMl}}}AIMl}\",[]]Ak[\"{{{AHO`{AJ@h}}}AJ@h}\",[]]Ak[\"{{{AHO`{B@Mj}}}B@Mj}\",[]]Ak[\"{{{AHO`{BCLl}}}BCLl}\",[]]Ak[\"{{{AHO`{BEFj}}}BEFj}\",[]]Ak[\"{{{AHO`{AAOl}}}AAOl}\",[]]Ak[\"{{{AHO`{BEGb}}}BEGb}\",[]]Ak[\"{{{AHO`{B@Mb}}}B@Mb}\",[]]Ak[\"{{{AHO`{BCNh}}}BCNh}\",[]]Ak[\"{{{AHO`{BHE`}}}BHE`}\",[]]Ak[\"{{{AHO`{ABAh}}}ABAh}\",[]]Ak[\"{{{AHO`{BFCl}}}BFCl}\",[]]Ak[\"{{{AHO`{B@Mh}}}B@Mh}\",[]]Ak[\"{{{AHO`{B@Nb}}}B@Nb}\",[]]Ak[\"{{{AHO`{BCAb}}}BCAb}\",[]]Ak[\"{{{AHO`{BFL`}}}BFL`}\",[]]Ak[\"{{{AHO`{AIMf}}}AIMf}\",[]]Ak[\"{{{AHO`{BIAh}}}BIAh}\",[]]Ai[\"{{{AHO`{DAn}}}DAn}\",[]]Ai[\"{{{AHO`{A@l}}}A@l}\",[]]Ak[\"{{{AHO`{ABCf}}}ABCf}\",[]]Ak[\"{{{AHO`{AHCd}}}AHCd}\",[]]Ak[\"{{{AHO`{BBCh}}}BBCh}\",[]]Ak[\"{{{AHO`{BGAn}}}BGAn}\",[]]Ai[\"{{{AHO`{IFf}}}IFf}\",[]]Ai[\"{{{AHO`{IFd}}}IFd}\",[]]Ak[\"{{{AHO`{BEJd}}}BEJd}\",[]]Ak[\"{{{AHO`{BGI`}}}BGI`}\",[]]Ak[\"{{{AHO`{BEJh}}}BEJh}\",[]]Ai[\"{{{AHO`{CNl}}}CNl}\",[]]Ak[\"{{{AHO`{AIJn}}}AIJn}\",[]]Ai[\"{{{AHO`{IDj}}}IDj}\",[]]Ak[\"{{{AHO`{AOK`}}}AOK`}\",[]]Ai[\"{{{AHO`{NKn}}}NKn}\",[]]Ak[\"{{{AHO`{BBAn}}}BBAn}\",[]]Ai[\"{{{AHO`{IFn}}}IFn}\",[]]Ak[\"{{{AHO`{ABC`}}}ABC`}\",[]]Ak[\"{{{AHO`{ABDl}}}ABDl}\",[]]Ak[\"{{{AHO`{B@Nd}}}B@Nd}\",[]]Ak[\"{{{AHO`{BG@n}}}BG@n}\",[]]Ak[\"{{{AHO`{BCN`}}}BCN`}\",[]]Ak[\"{{{AHO`{BHKb}}}BHKb}\",[]]Ak[\"{{{AHO`{BHKd}}}BHKd}\",[]]Bn[\"{{{AHO`{AILb}}KI`{AHO`{{K@j{Oh}}}}}ICj}\",[]]Bn[\"{{{AHO`{AB@h}}KI`{AHO`{{K@j{Oh}}}}}ICj}\",[]]Bf[\"{{{AHO`{DA`}}}{{KGd{ICjBBLd}}}}\",[]]Bf[\"{{{AHO`{CGbAILd}}NK`KIbICn}ICj}\",[]]Bj[\"{{{AHO`{B@Mf}}}{{AHO`{{K@j{Oh}}}}}}\",[]]Bj[\"{{{AHO`{B@Mh}}}{{AHO`{{K@j{Oh}}}}}}\",[]]Aa[\"{BCAbAIMl}\",[]]A`[\"{ABCdCNb}\",[]]A`[\"{ABCfCNb}\",[]]A`[\"{B@N`DC`}\",[]]A`[\"{B@NbDC`}\",[]]l[\"{NKb}\",[]]l[\"{NKd}\",[]]Cf[\"{{{AHO`{BFHd}}{AHO`{CGbA@f}}}{{KGd{B@MhBFCl}}}}\",[]]Cf[\"{{{AHO`{BIDl}}{AHO`{CGbA@f}}}{{KGd{B@MhBFCl}}}}\",[]]Bc[\"{{{AHO`{AILd}}}{{ABh{KG`}}}}\",[]]Bd[\"{{{AHO`{IHj}}{AHO`{CMl}}}ICj}\",[]]Am[\"{{{AHO`{AOJ`}}KCb}ICj}\",[]]Ai[\"{{{AHO`{IHl}}}ICj}\",[]]Bg[\"{{{ABb{{AHO`{CGbAIJd}}}}NK`}ICj}\",[]]Bm[\"{{{AHO`{CGbAJ@`}}KI`}{{KGd{ICjB@Ld}}}}\",[]]Bm[\"{{{AHO`{CGbAJ@b}}KIb}{{KGd{ICjB@Ll}}}}\",[]]Am[\"{ABA`{{KGd{ICjAIOj}}}}\",[]]Dc[\"{{{AHO`{CGbAB@j}}ABAb{AHO`{CGb{ABh{Oh}}}}}{{KGd{ABDjAIOl}}}}\",[]]Bj[\"{{{AHO`{NMd}}{ABb{{A@d{KEn}}}}}ICj}\",[]]Bk[\"{{{AHO`{BACb}}{ABb{{A@d{KEn}}}}}ICj}\",[]]Bk[\"{{{AHO`{AOMl}}{ABb{{A@d{KEn}}}}}ICj}\",[]]o[\"{AAjDC`}\",[]]Ak[\"{{{AHO`{AILb}}}BCMf}\",[]]Ak[\"{{{AHO`{AB@h}}}BAOf}\",[]]Aj[\"{{{AHO`{DA`}}}AHEl}\",[]]A`[\"{KEdBCMf}\",[]]An[\"{{{AHO`{CGbAILd}}}BCMh}\",[]]Ce[\"{{{AHO`{CGbAINn}}{KFl{KHl}}}{{AHO`{CGbAINn}}}}\",[]]Ce[\"{{{AHO`{CGbAIO`}}{KFl{KHl}}}{{AHO`{CGbAIO`}}}}\",[]]Ck[\"{{{AHO`{CGbAJ@`}}{AHO`{{K@j{Oh}}}}}{{KGd{KCbAJAl}}}}\",[]]Ck[\"{{{AHO`{CGbAJ@b}}{AHO`{{K@j{Oh}}}}}{{KGd{KCbAJAn}}}}\",[]]Bl[\"{{{AHO`{BGAn}}{AHO`{CGbc}}}ICjKDj}\",[\"W\"]]Aj[\"{{{AHO`{AB@h}}}KCj}\",[]]An[\"{ABA`{{KGd{AIKnBCLn}}}}\",[]]Bi[\"{{{AHO`{CGbNNf}}A@l}{{KFl{ABCf}}}}\",[]]Fj[\"{{{AHO`{CGbAB@j}}ABAbNK`{AHO`{CGb{ABh{Oh}}}}{KFl{{A@b{BABh}}}}}{{KGd{{KC`{BEGbAILd}}{A@d{AOIj}}}}}}\",[]]Am[\"{AHO`{{AHO`{c}}}{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A`[\"{AAjAOIn}\",[]]Bc[\"{{AJ@fBBLbAAj}{{KGh{AOJ`}}}}\",[]]Aj[\"{AJ@f{{KGf{AB@h}}}}\",[]]Ba[\"{{}{{KGd{ICj{A@d{IEd}}}}}}\",[]]Ak[\"{{{AHO`{AILb}}}BCLn}\",[]]Be[\"{{{AHO`{AB@j}}}{{AHO`{BBMj}}}}\",[]]o[\"{OhBFKn}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{BABdA@b}\",[]]o[\"{OhBFL`}\",[]]A`[\"{AIM`CNb}\",[]]A`[\"{BABhA@b}\",[]]A`[\"{BCAbKFl}\",[]]Bm[\"{{{AHO`{CGbc}}}{{KGd{KI`BBCh}}}A@f}\",[\"B\"]]Bm[\"{{{AHO`{CGbc}}}{{KGd{KFnBBCh}}}A@f}\",[\"B\"]]Bn[\"{{{AHO`{CGbc}}}{{KGd{AHCfBBCh}}}A@f}\",[\"B\"]]Bn[\"{{{AHO`{CGbc}}}{{KGd{BFKnBBCh}}}A@f}\",[\"B\"]]Bn[\"{{{AHO`{CGbc}}}{{KGd{ABCdBBCh}}}A@f}\",[\"B\"]]Bi[\"{{{AHO`{CGbc}}}{{KGl{KIb}}}A@f}\",[\"B\"]]Bi[\"{{{AHO`{CGbc}}}{{KGl{KG`}}}A@f}\",[\"B\"]]Bj[\"{{{AHO`{CGbc}}}{{KGl{AHCh}}}A@f}\",[\"B\"]]Bj[\"{{{AHO`{CGbc}}}{{KGl{AIMl}}}A@f}\",[\"B\"]]Ek[\"{{{AHO`{CGb{KEl{AAOb}}}}{AHO`{c}}{AHO`{{K@j{CN`}}}}DC`}{{KGd{BCNhBFCl}}}{BFHdIHh}}\",[\"\"]]Bj[\"{{{AHO`{CGbc}}}{{KGl{BFL`}}}A@f}\",[\"B\"]]Bj[\"{{{AHO`{CGbc}}}{{KGl{ABCf}}}A@f}\",[\"B\"]]Bj[\"{{{AHO`{CGbc}}}{{KGl{AHBd}}}A@f}\",[\"B\"]]A`[\"{ABC`KFl}\",[]]Bk[\"{{{AHO`{KI`}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bk[\"{{{AHO`{KFn}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{AHCf}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{BFKn}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{ABCd}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bk[\"{{{AHO`{KIb}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bk[\"{{{AHO`{KG`}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{AHCh}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{AIMl}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{AAOl}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{BFL`}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{ABCf}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{AHBn}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bj[\"{{{AHO`{CGbAJ@`}}}{{KGd{ICjB@Ld}}}}\",[]]Bj[\"{{{AHO`{CGbAJ@b}}}{{KGd{ICjAOKn}}}}\",[]]An[\"{{{AHO`{AONh}}AB@d}ICj}\",[]]Ea[\"{{{AHO`{CGbAB@j}}NK`AHCd{KFl{B@Nb}}AAOb{AHO`{CGb{ABh{Oh}}}}}{{KFl{BAOb}}}}\",[]]o[\"{D@dNKb}\",[]]o[\"{D@fNKd}\",[]]A`[\"{ABA`ICj}\",[]]Ba[\"{{{AHO`{CGbAB@j}}ABAb}ICj}\",[]]Bh[\"{{{AHO`{AJAf}}{AHO`{CMl}}ICn}ICj}\",[]]Bh[\"{{{AHO`{BBOd}}{AHO`{CMl}}ICn}ICj}\",[]]Bh[\"{{{AHO`{BEJj}}{AHO`{CMl}}ICn}ICj}\",[]]Bh[\"{{{AHO`{AJAh}}{AHO`{CMl}}ICn}ICj}\",[]]Bh[\"{{{AHO`{BBOf}}{AHO`{CMl}}ICn}ICj}\",[]]Bh[\"{{{AHO`{BEJl}}{AHO`{CMl}}ICn}ICj}\",[]]A`[\"{AHAlDC`}\",[]]o[\"{ID`CNb}\",[]]o[\"{IDbCNb}\",[]]Ch[\"{{{AHO`{CGbAILf}}NK`NK`CNbCNbDC`{AHO`{BAAj}}}ICj}\",[]]Cg[\"{{{AHO`{CGbCNj}}NK`NK`CNbCNbDC`{AHO`{BAAj}}}ICj}\",[]]Cg[\"{{{AHO`{CGbIDh}}NK`NK`CNbCNbDC`{AHO`{BAAj}}}ICj}\",[]]Cg[\"{{{AHO`{CGbNKl}}NK`NK`CNbCNbDC`{AHO`{BAAj}}}ICj}\",[]]Ch[\"{{{AHO`{CGbAILh}}NK`NK`CNbCNbDC`{AHO`{BAAl}}}ICj}\",[]]Cg[\"{{{AHO`{CGbCNl}}NK`NK`CNbCNbDC`{AHO`{BAAl}}}ICj}\",[]]Cg[\"{{{AHO`{CGbIDj}}NK`NK`CNbCNbDC`{AHO`{BAAl}}}ICj}\",[]]Cg[\"{{{AHO`{CGbNKn}}NK`NK`CNbCNbDC`{AHO`{BAAl}}}ICj}\",[]]o[\"{D@dNKb}\",[]]o[\"{D@fNKd}\",[]]o[\"{BEKfOh}\",[]]A`[\"{KEdBCLn}\",[]]A`[\"{BCLhICn}\",[]]A`[\"{BEFfICn}\",[]]Aa[\"{AH@`BFD`}\",[]]A`[\"{BCLlICn}\",[]]A`[\"{BEFjICn}\",[]]A`[\"{BCAbKHl}\",[]]Aa[\"{BBLjBCM`}\",[]]Aa[\"{AH@bBFDb}\",[]]Bg[\"{{{AHO`{AB@h}}AHFl}{{KGf{ICj}}}}\",[]]A`[\"{ABA`ICj}\",[]]Cf[\"{{{AHO`{CGbAB@j}}ABAb{AHO`{CGb{ABh{Oh}}}}}ABDj}\",[]]Ak[\"{{{AHO`{AHCb}}}AJ@f}\",[]]l[\"{NKb}\",[]]Ak[\"{{{AHO`{AHCd}}}AJ@f}\",[]]l[\"{NKd}\",[]]An[\"{{AB@fAAj}{{KGh{ICj}}}}\",[]]B`[\"{{BABdAJ@f}{{KGf{AB@h}}}}\",[]]Bc[\"{BAB`{{KGd{ICj{A@d{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BFDd}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BC@h}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{AHFb}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{AIOh}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BC@f}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{AJAl}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BAC`}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BCLn}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BCM`}}}{{KFl{{AHO`{IEd}}}}}}\",[]]A`[\"{ABDhKFl}\",[]]A`[\"{ABDjKFl}\",[]]A`[\"{ABDlKFl}\",[]]Bg[\"{{{AHO`{DA`}}}{{KGd{AINjAIKd}}}}\",[]]Aa[\"{BAAbAINj}\",[]]Aa[\"{BAAdAINl}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]A`[\"{ABC`KCb}\",[]]Aa[\"{AHElABDn}\",[]]Aa[\"{BCMfABDn}\",[]]Aa[\"{AHEnABE`}\",[]]Aa[\"{BCMhABE`}\",[]]3210Dh[\"{{{AHO`{NJj}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjAOJj}}}}\",[]]Dh[\"{{{AHO`{NJl}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjAOJl}}}}\",[]]Aj[\"{{{AHO`{AILf}}}CNb}\",[]]Ai[\"{{{AHO`{CNj}}}CNb}\",[]]Ai[\"{{{AHO`{IDh}}}CNb}\",[]]Ai[\"{{{AHO`{NKl}}}CNb}\",[]]Aj[\"{{{AHO`{AILh}}}CNb}\",[]]Ai[\"{{{AHO`{CNl}}}CNb}\",[]]Ai[\"{{{AHO`{IDj}}}CNb}\",[]]Ai[\"{{{AHO`{NKn}}}CNb}\",[]]Aj[\"{{{AHO`{BGOj}}}KCb}\",[]]Aj[\"{{{AHO`{BGOl}}}KCb}\",[]]Aj[\"{{{AHO`{BI@n}}}KCb}\",[]]Aj[\"{{{AHO`{BIAh}}}KCb}\",[]]o[\"{AAjKCb}\",[]]n[\"{{}NIl}\",[]]0A`[\"{A@hAAOf}\",[]]111Ba[\"{{AHO`{AHO`{c}}}ABBj{}}\",[\"K\"]]0000000000000000000Cg[\"{{{AHO`{AB@h}}AJ@f{AHO`{CMl}}}{{KGd{AIKnB@M`}}}}\",[]]Dl[\"{{{AHO`{CGbAB@j}}NK`B@KlAJ@f{AHO`{CMl}}}{{KGd{{KC`{BEGbAILd}}B@Mb}}}}\",[]]Bd[\"{{{AHO`{ABA`}}}{{KFl{BEGf}}}}\",[]]Dm[\"{{{AHO`{AHDj}}KFnCNb{AHO`{{K@j{Oh}}}}{AHO`{CGbAAOb}}}{{KGd{ICjAOJj}}}}\",[]]Ca[\"{{{AHO`{AHCl}}KCb{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]Bd[\"{{{AHO`{ABAb}}}{{KFl{BEGh}}}}\",[]]Dm[\"{{{AHO`{AHDl}}KG`CNb{AHO`{{K@j{Oh}}}}{AHO`{CGbAAOb}}}{{KGd{ICjAOJl}}}}\",[]]Ca[\"{{{AHO`{AHCn}}KCb{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]o[\"{{}AONf}\",[]]n[\"{{}IHj}\",[]]o[\"{{}AOJ`}\",[]]o[\"{{}BANd}\",[]]o[\"{{}BAOf}\",[]]n[\"{{}KI`}\",[]]n[\"{{}KFn}\",[]]o[\"{{}AINj}\",[]]o[\"{{}AIKd}\",[]]o[\"{{}ABDn}\",[]]o[\"{{}AILn}\",[]]o[\"{{}AHEl}\",[]]o[\"{{}BCMf}\",[]]o[\"{{}B@Ld}\",[]]o[\"{{}BCOl}\",[]]o[\"{{}BFGh}\",[]]o[\"{{}BFIf}\",[]]o[\"{{}AOL`}\",[]]o[\"{{}BBMh}\",[]]o[\"{{}BH@l}\",[]]o[\"{{}BEJj}\",[]]o[\"{{}AIJl}\",[]]o[\"{{}AOJn}\",[]]o[\"{{}BBAl}\",[]]o[\"{{}BFBj}\",[]]o[\"{{}BANf}\",[]]o[\"{{}BI@n}\",[]]n[\"{{}KIb}\",[]]n[\"{{}KG`}\",[]]o[\"{{}AINl}\",[]]o[\"{{}AIKj}\",[]]o[\"{{}ABE`}\",[]]o[\"{{}AIM`}\",[]]o[\"{{}AHEn}\",[]]o[\"{{}BCMh}\",[]]o[\"{{}BC@j}\",[]]o[\"{{}B@Ll}\",[]]o[\"{{}BCOn}\",[]]o[\"{{}BFGj}\",[]]o[\"{{}BFIh}\",[]]o[\"{{}AOLb}\",[]]o[\"{{}BBMj}\",[]]o[\"{{}BH@n}\",[]]o[\"{{}BIAh}\",[]]o[\"{{}BEJl}\",[]]o[\"{{}AIJn}\",[]]o[\"{{}AOK`}\",[]]o[\"{{}BBAn}\",[]]o[\"{{}BFBl}\",[]]o[\"{{}ABC`}\",[]]n[\"{{}IHl}\",[]]o[\"{{}BCN`}\",[]]o[\"{{}BAOd}\",[]]Ak[\"{{{AHO`{BBBb}}}B@Mh}\",[]]Ak[\"{{{AHO`{BCNh}}}B@Mh}\",[]]Aa[\"{BHE`B@Mh}\",[]]Aa[\"{BFLbB@Mh}\",[]]A`[\"{D@hB@Mh}\",[]]A`[\"{IH`B@Mh}\",[]]A`[\"{IHdB@Mh}\",[]]Aa[\"{BEKfB@Mh}\",[]]Cg[\"{{{AHO`{AHDj}}KFnCNb{AHO`{CGb{K@j{Oh}}}}KCb}ICj}\",[]]Ca[\"{{{AHO`{AHCl}}KCb{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]Cg[\"{{{AHO`{AHDl}}KG`CNb{AHO`{CGb{K@j{Oh}}}}KCb}ICj}\",[]]Ca[\"{{{AHO`{AHCn}}KCb{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]Aj[\"{{{AHO`{AIOd}}}DC`}\",[]]Aj[\"{{{AHO`{BGB`}}}DC`}\",[]]Ak[\"{{{AHO`{AILf}}}BFBj}\",[]]Aj[\"{{{AHO`{CNj}}}BFBj}\",[]]Aj[\"{{{AHO`{IDh}}}BFBj}\",[]]Aj[\"{{{AHO`{NKl}}}BFBj}\",[]]Ak[\"{{{AHO`{AILh}}}BFBl}\",[]]Aj[\"{{{AHO`{CNl}}}BFBl}\",[]]Aj[\"{{{AHO`{IDj}}}BFBl}\",[]]Aj[\"{{{AHO`{NKn}}}BFBl}\",[]]Bn[\"{{{AHO`{CGbBCOl}}CMn}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}CMn}{{AHO`{CGbBCOn}}}}\",[]]Bf[\"{{{AHO`{CGbAILf}}NK`CNbCNb}ICj}\",[]]Bf[\"{{{AHO`{CGbAILh}}NK`CNbCNb}ICj}\",[]]Aj[\"{{{AHO`{AILb}}}KFf}\",[]]Bc[\"{{{AHO`{ABBh}}}{{KFl{KFn}}}}\",[]]Aa[\"{BHE`B@Mh}\",[]]Aa[\"{BFLbB@Mh}\",[]]A`[\"{D@hB@Mh}\",[]]A`[\"{IH`B@Mh}\",[]]Aa[\"{BEKfB@Mh}\",[]]Aj[\"{{{AHO`{AJ@`}}}NMn}\",[]]Bn[\"{{{AHO`{AJ@b}}}{{KGd{{KFl{KIb}}B@Ll}}}}\",[]]o[\"{AAjKCb}\",[]]o[\"{IHjKCb}\",[]]Am[\"{{{AHO`{CGbAILd}}}NNf}\",[]]Aj[\"{{{AHO`{AHDj}}}KCb}\",[]]Aj[\"{{{AHO`{AHDl}}}KCb}\",[]]A`[\"{AHO`KHn}\",[]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bd[\"{{{AHO`{BGJ`}}}{{KFl{AILb}}}}\",[]]Bc[\"{{{AHO`{BCAf}}}{{KFl{DA`}}}}\",[]]Bn[\"{{{AHO`{CGbB@Kh}}CN`}{{AHO`{CGbB@Kh}}}}\",[]]Bn[\"{{{AHO`{CGbB@Kl}}CN`}{{AHO`{CGbB@Kl}}}}\",[]]A`[\"{BHE`CN`}\",[]]A`[\"{BFLbCN`}\",[]]o[\"{D@hCN`}\",[]]o[\"{IH`CN`}\",[]]A`[\"{AHAlKFl}\",[]]Aa[\"{ABDlAHO`}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Aa[\"{BC@lAB@d}\",[]]A`[\"{KDlBC@j}\",[]]Aa[\"{AHElAILn}\",[]]Aa[\"{BCMfAILn}\",[]]Aa[\"{AHEnAIM`}\",[]]Aa[\"{BCMhAIM`}\",[]]3210Bh[\"{{{AHO`{CGbc}}KCb}B@Mf{A@fIHh}}\",[\"\"]]Bh[\"{{{AHO`{CGbc}}KCb}B@Mh{A@fIHh}}\",[\"\"]]o[\"{{}BCNj}\",[]]o[\"{{}BCNl}\",[]]A`[\"{CNbBI@n}\",[]]Cd[\"{{{AHO`{CMl}}}{{KGh{AOMfc}}}{}}\",[\"FromStr::Err\"]]Cd[\"{{{AHO`{CMl}}}{{KGh{AOInc}}}{}}\",[\"FromStr::Err\"]]o[\"{CN`KI`}\",[]]o[\"{CN`KIb}\",[]]Al[\"{CNb{{KGd{KI`BGIj}}}}\",[]]Al[\"{CNb{{KGd{KIbBGIl}}}}\",[]]Aa[\"{BGGnAOJh}\",[]]Aa[\"{BIDnBB@b}\",[]]Aa[\"{BGH`AOJh}\",[]]Aa[\"{BIE`BB@b}\",[]]Af[\"{{}{{KFl{Ab}}}}\",[]]0000Aj[\"{{{AHO`{AILd}}}DC`}\",[]]Bo[\"{{{AHO`{CGbBFIf}}AB@d}{{AHO`{CGbBFIf}}}}\",[]]Bo[\"{{{AHO`{CGbBFIh}}AB@d}{{AHO`{CGbBFIh}}}}\",[]]Bb[\"{{{A@d{AILf}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{CNj}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{IDh}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{NKl}}}{{A@d{A@`}}}}\",[]]Bb[\"{{{A@d{AILh}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{CNl}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{IDj}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{NKn}}}{{A@d{A@`}}}}\",[]]Aj[\"{{{AHO`{BGJ`}}}DC`}\",[]]Bo[\"{{{AHO`{CGbBH@l}}AB@d}{{AHO`{CGbBH@l}}}}\",[]]Bo[\"{{{AHO`{CGbBH@n}}AB@d}{{AHO`{CGbBH@n}}}}\",[]]Bc[\"{{{AHO`{AIKn}}}{{KFl{KFb}}}}\",[]]Bc[\"{{{AHO`{ABA`}}}{{KFl{KFb}}}}\",[]]Bm[\"{{{AHO`{DA`}}}{{KGd{{KFl{KFb}}AIKd}}}}\",[]]Bc[\"{{{AHO`{AHCb}}}{{KFl{KFb}}}}\",[]]Bc[\"{{{AHO`{ABAb}}}{{KFl{KFb}}}}\",[]]Bc[\"{{{AHO`{AHCd}}}{{KFl{KFb}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Bc[\"{{{AHO`{AHBb}}}{{KFl{KCb}}}}\",[]]Am[\"{{{AHO`{AONh}}KCb}ICj}\",[]]Aj[\"{{{AHO`{AILb}}}NLb}\",[]]Bg[\"{{{AHO`{AJ@`}}}{{KGd{CElB@Ld}}}}\",[]]Bg[\"{{{AHO`{AJ@b}}}{{KGd{CElB@Ll}}}}\",[]]A`[\"{BAOjKFl}\",[]]A`[\"{BAOlKFl}\",[]]A`[\"{AHAlKFl}\",[]]A`[\"{AOIjKFl}\",[]]Ck[\"{{{AHO`{CGbBBMh}}{KFl{{IIh{Oh}}}}}{{AHO`{CGbBBMh}}}}\",[]]Ck[\"{{{AHO`{CGbBBMj}}{KFl{{IIh{Oh}}}}}{{AHO`{CGbBBMj}}}}\",[]]A`[\"{BFBjKFl}\",[]]A`[\"{BFBlKFl}\",[]]Ac[\"{AHO`c{}}\",[\"T\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Ao[\"{c{{KGd{e}}}{}{}}\",[\"U\",\"T\"]]00000000000000000000000000000000000000000000000000000Al[\"{ICh{{KGd{KI`BGIj}}}}\",[]]Al[\"{CNb{{KGd{KI`BGIj}}}}\",[]]Al[\"{KCb{{KGd{KI`BGIj}}}}\",[]]333333333333333333333Aj[\"{AB@d{{KGd{AOL`}}}}\",[]]44444Ai[\"{CNb{{KGd{AHCf}}}}\",[]]555555555555555555555555555555Bc[\"{{{A@b{B@Kf}}}{{KGd{BEJb}}}}\",[]]Aj[\"{B@Kf{{KGd{BEJb}}}}\",[]]7Bc[\"{{{A@b{BABb}}}{{KGd{BEJf}}}}\",[]]Aj[\"{BABb{{KGd{BEJf}}}}\",[]]999999999999999999999Al[\"{KCb{{KGd{KIbBGIl}}}}\",[]]Al[\"{CNb{{KGd{KIbBGIl}}}}\",[]];Al[\"{ICh{{KGd{KIbBGIl}}}}\",[]]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Bm[\"{B@Kf{{KGd{BEJdc}}}{}}\",[\"TryFrom::Error\"]]Cf[\"{{{A@b{B@Kf}}}{{KGd{BEJdc}}}{}}\",[\"TryFrom::Error\"]]Ao[\"{c{{KGd{e}}}{}{}}\",[\"U\",\"T\"]]Cf[\"{{{A@b{BABb}}}{{KGd{BEJhc}}}{}}\",[\"TryFrom::Error\"]]Bm[\"{BABb{{KGd{BEJhc}}}{}}\",[\"TryFrom::Error\"]]222222222222222222222222Aj[\"{{}{{KGd{c}}}{}}\",[\"U\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cd[\"{{{ABb{{AHO`{CGbc}}}}{AHO`{CGbNIn}}}DAh{}}\",[\"F\"]]000000000Cc[\"{{{AHO`{BCAd}}AJAj{AHO`{ABDl}}}{{KGf{ICj}}}}\",[]]Bk[\"{{{AHO`{BGOj}}B@Mf}{{KGd{ICjAIMd}}}}\",[]]Bk[\"{{{AHO`{BGOl}}B@Mh}{{KGd{ICjAIMf}}}}\",[]]0Bk[\"{{{AHO`{BI@n}}B@Mh}{{KGd{ICjAIMf}}}}\",[]]Ak[\"{{{AHO`{AILb}}}AAO`}\",[]]Bi[\"{{{AHO`{CGbBACd}}}{{KGd{KI`IF`}}}}\",[]]Bi[\"{{{AHO`{CGbBACd}}}{{KGd{DAlIF`}}}}\",[]]Bi[\"{{{AHO`{CGbBACd}}}{{KGd{A@jIF`}}}}\",[]]Bj[\"{{{AHO`{CGbBACd}}}{{KGd{ABCdIF`}}}}\",[]]Bf[\"{{{AHO`{CGbBACd}}}{{KGj{KIb}}}}\",[]]Bf[\"{{{AHO`{CGbBACd}}}{{KGj{DAn}}}}\",[]]Bf[\"{{{AHO`{CGbBACd}}}{{KGj{A@l}}}}\",[]]Bg[\"{{{AHO`{CGbBACd}}}{{KGj{ABCf}}}}\",[]]Aa[\"{AOInBGIh}\",[]]Bd[\"{{{AHO`{AILf}}}{{A@d{AILf}}}}\",[]]Bc[\"{{{AHO`{CNj}}}{{A@d{AILf}}}}\",[]]Bc[\"{{{AHO`{IDh}}}{{A@d{AILf}}}}\",[]]Bc[\"{{{AHO`{NKl}}}{{A@d{AILf}}}}\",[]]Bd[\"{{{AHO`{AILh}}}{{A@d{AILh}}}}\",[]]Bc[\"{{{AHO`{CNl}}}{{A@d{AILh}}}}\",[]]Bc[\"{{{AHO`{IDj}}}{{A@d{AILh}}}}\",[]]Bc[\"{{{AHO`{NKn}}}{{A@d{AILh}}}}\",[]]A`[\"{ABDnCNb}\",[]]An[\"{{{AHO`{CGbAILd}}}AHBb}\",[]]A`[\"{ABE`CNb}\",[]]C`[\"{{{AHO`{CGbB@Mf}}}{{AHO`{CGb{K@j{Oh}}}}}}\",[]]C`[\"{{{AHO`{CGbB@Mh}}}{{AHO`{CGb{K@j{Oh}}}}}}\",[]]Ah[\"{Oh{{KFl{B@N`}}}}\",[]]Ah[\"{Oh{{KFl{B@Nb}}}}\",[]]Ah[\"{Oh{{KFl{B@Nd}}}}\",[]]A`[\"{ABCdDAl}\",[]]A`[\"{ABCfDAn}\",[]]Bi[\"{AIKn{{KGd{{KC`{AILbBD@f}}AIKn}}}}\",[]]Aa[\"{{}c{}}\",[\"I\"]]o[\"{DAlDC`}\",[]]o[\"{DAnDC`}\",[]]Aj[\"{{{AHO`{AILd}}}DC`}\",[]]21Aj[\"{{{AHO`{ABA`}}}DC`}\",[]]Aj[\"{{{AHO`{ABAb}}}DC`}\",[]]Bn[\"{{{AHO`{CGbBABd}}DC`}{{AHO`{CGbBABd}}}}\",[]]Bn[\"{{{AHO`{CGbBABh}}DC`}{{AHO`{CGbBABh}}}}\",[]]Bm[\"{{{AHO`{NMd}}NK`}{{ABb{{A@d{AIJd}}}}}}\",[]]Bn[\"{{{AHO`{BACb}}NK`}{{ABb{{A@d{AIJd}}}}}}\",[]]Bn[\"{{{AHO`{AOMl}}NK`}{{ABb{{A@d{AIJd}}}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Ak[\"{{{AHO`{AILb}}}ABBf}\",[]]Bo[\"{{{AHO`{AILb}}cAINj}ABBh{{D@b{AHCb}}}}\",[\"\"]]Ce[\"{{{AHO`{CGbAILd}}AHCdAINlNK`}{{KGd{KG`AHEb}}}}\",[]]43Bm[\"{{{AHO`{CGbAILd}}KG`}{{KGd{ICjAIKj}}}}\",[]]E`[\"{{{ABb{{AHO`{CGbBGIb}}}}{AHO`{CGbNIn}}}{{DAh{{KFl{c}}}}}{}}\",[\"Stream::Item\"]]E`[\"{{{ABb{{AHO`{CGbBEFb}}}}{AHO`{CGbNIn}}}{{DAh{{KFl{c}}}}}{}}\",[\"Stream::Item\"]]E`[\"{{{ABb{{AHO`{CGbAINh}}}}{AHO`{CGbNIn}}}{{DAh{{KFl{c}}}}}{}}\",[\"Stream::Item\"]]E`[\"{{{ABb{{AHO`{CGbBGA`}}}}{AHO`{CGbNIn}}}{{DAh{{KFl{c}}}}}{}}\",[\"Stream::Item\"]]Ec[\"{{{AHO`{CGbAIOd}}{AHO`{CGbNIn}}{AHO`{CGb{K@j{Oh}}}}}{{DAh{{KGd{KCbAHFb}}}}}}\",[]]E`[\"{{{ABb{{AHO`{CGbAIOd}}}}{AHO`{CGbNIn}}{AHO`{CGbNLn}}}{{DAh{{KGf{ICj}}}}}}\",[]]Ef[\"{{{ABb{{AHO`{CGbAIOd}}}}{AHO`{CGbNIn}}{AHO`{CGb{K@j{Oh}}}}}{{DAh{{KGf{KCb}}}}}}\",[]]Fg[\"{{{AHO`{CGbBBL`}}{AHO`{CGbNIn}}{AHO`{CGb{K@j{AIMh}}}}{AHO`{CGb{K@j{ABC`}}}}}{{DAh{{KGf{KCb}}}}}}\",[]]Dn[\"{{{ABb{{AHO`{CGbAHFj}}}}{AHO`{ABDl}}{AHO`{CGbNIn}}}{{DAh{{KGf{ICj}}}}}}\",[]]>=Ce[\"{{{AHO`{CGbBBMh}}{A@b{NJj}}}{{AHO`{CGbBBMh}}}}\",[]]Ce[\"{{{AHO`{CGbBBMj}}{A@b{NJl}}}{{AHO`{CGbBBMj}}}}\",[]]Cj[\"{{{AHO`{BABf}}CN`B@Mf{AHO`{{K@j{Oh}}}}}{{IIh{Oh}}}}\",[]]Cj[\"{{{AHO`{BEJf}}CN`B@Mf{AHO`{{K@j{Oh}}}}}{{IIh{Oh}}}}\",[]]Cj[\"{{{AHO`{BABj}}CN`B@Mh{AHO`{{K@j{Oh}}}}}{{IIh{Oh}}}}\",[]]Cj[\"{{{AHO`{BEJh}}CN`B@Mh{AHO`{{K@j{Oh}}}}}{{IIh{Oh}}}}\",[]]Cj[\"{{{AHO`{BHKd}}CN`B@Md{AHO`{{K@j{Oh}}}}}{{IIh{Oh}}}}\",[]]Bb[\"{KCb{{KC`{KCb{KFl{KCb}}}}}}\",[]]000Aj[\"{{{AHO`{AILb}}}KCb}\",[]]A`[\"{AHO`KHl}\",[]]0000000000000000000000000000000000000000000000000000000000000000000Cf[\"{{{AHO`{CGbBABd}}{A@b{BFBn}}}{{AHO`{CGbBABd}}}}\",[]]Cf[\"{{{AHO`{CGbBABh}}{A@b{BFC`}}}{{AHO`{CGbBABh}}}}\",[]]A`[\"{BHE`IGf}\",[]]A`[\"{BABdA@b}\",[]]A`[\"{BABhA@b}\",[]]Aj[\"{{{AHO`{AB@h}}}ICj}\",[]]Ck[\"{{{AHO`{CGbAJ@`}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjAJAl}}}}\",[]]n[\"{{}KCb}\",[]]Ak[\"{{{AHO`{AILb}}}AH@h}\",[]]Ba[\"{{{AHO`{CGbABDn}}ABDn}ICj}\",[]]Ba[\"{{{AHO`{CGbAILn}}AILn}ICj}\",[]]Ba[\"{{{AHO`{CGbBCMf}}AHEl}ICj}\",[]]Ba[\"{{{AHO`{CGbABE`}}ABE`}ICj}\",[]]Ba[\"{{{AHO`{CGbAIM`}}AIM`}ICj}\",[]]Ba[\"{{{AHO`{CGbBCMh}}AHEn}ICj}\",[]]Bn[\"{{{AHO`{CGbBCOl}}DC`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}DC`}{{AHO`{CGbBCOn}}}}\",[]]Bi[\"{{{AHO`{CGb}}}{{AHO`{CGbc}}}{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bg[\"{{{AHO`{AIOd}}}{{KGd{CNbB@Ld}}}}\",[]]Bg[\"{{{AHO`{AIOf}}}{{KGd{CNbB@Ll}}}}\",[]]Bc[\"{{AHO`{AHO`{CGbc}}}ICj{}}\",[\"T\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cc[\"{{{AHO`{CGbAILd}}NK`KG`KIb}{{KGd{ICjBBLf}}}}\",[]]A`[\"{AHAlDC`}\",[]]B`[\"{{AHO`{AHO`{c}}}DC`{}}\",[\"K\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Aa[\"{BCLhBFKn}\",[]]A`[\"{BEFfKI`}\",[]]Aa[\"{BCLlBFL`}\",[]]A`[\"{BEFjKIb}\",[]]A`[\"{BFAnKIb}\",[]]A`[\"{BCNnKIb}\",[]]o[\"{NMjKIb}\",[]]Aa[\"{BCLhAIMj}\",[]]Aa[\"{BCLlAIMl}\",[]]o[\"{KI`CNb}\",[]]o[\"{KIbCNb}\",[]]Aj[\"{{{AHO`{AILd}}}DC`}\",[]]Aj[\"{{{AHO`{BAOd}}}DC`}\",[]]Bd[\"{{{AHO`{BBL`}}}{{KGf{AJ@f}}}}\",[]]Bd[\"{{{AHO`{AB@h}}}{{KGf{AJ@f}}}}\",[]]A`[\"{AHElCNb}\",[]]A`[\"{BCMfCNb}\",[]]A`[\"{AHEnCNb}\",[]]A`[\"{BCMhCNb}\",[]]Cc[\"{{{AHO`{BCN`}}{AHO`{AIOn}}CNb}{{A@b{AONh}}}}\",[]]Bn[\"{{{AHO`{CGbBCOl}}DC`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}DC`}{{AHO`{CGbBCOn}}}}\",[]]Bg[\"{{{AHO`{AILb}}KFn}{{KFl{AHEl}}}}\",[]]o[\"{KEdABh}\",[]]A`[\"{AHBfA@d}\",[]]Bj[\"{{{AHO`{CGbAILd}}KG`}{{KFl{AHEn}}}}\",[]]A`[\"{AHBhA@d}\",[]]Db[\"{{{ABb{{AHO`{CGbAJ@`}}}}{AHO`{CGbNIn}}}{{DAh{{KGf{ICj}}}}}}\",[]]00Ec[\"{{{ABb{{AHO`{CGbAJ@`}}}}{AHO`{CGbNIn}}{AHO`{{K@j{Oh}}}}}{{DAh{{KGf{KCb}}}}}}\",[]]0Eg[\"{{{ABb{{AHO`{CGbAJ@`}}}}{AHO`{CGbNIn}}{AHO`{{K@j{Oh}}}}}{{DAh{{KGd{KCbAJAl}}}}}}\",[]]Bm[\"{{{AHO`{IHl}}{AHO`{D@n}}}{{KGf{ICj}}}}\",[]]Cd[\"{{{AHO`{CGbAIOd}}KCb}{{KGd{{KFl{ICn}}AHFb}}}}\",[]]Cd[\"{{{AHO`{CGbBGB`}}KCb}{{KGd{{KFl{ID`}}AHFb}}}}\",[]]Cn[\"{{{AHO`{CGbAIOd}}{AHO`{CGb{K@j{Oh}}}}}{{KGd{ICjBC@f}}}}\",[]]Bk[\"{{{AHO`{DA`}}AINj}{{KGd{AINjBFKd}}}}\",[]]Bn[\"{{{AHO`{CGbAINn}}NK`}{{AHO`{CGbAINn}}}}\",[]]Bn[\"{{{AHO`{CGbAIO`}}NK`}{{AHO`{CGbAIO`}}}}\",[]]A`[\"{BC@lAG`}\",[]]o[\"{IHjCNb}\",[]]o[\"{{}AJ@f}\",[]]0o[\"{{}AHO`}\",[]]Bk[\"{{ABA`{A@b{BABd}}}{{KGd{AIKnBCLn}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Bh[\"{{{AHO`{AHAl}}AJ@f}{{KGh{AHFl}}}}\",[]]A`[\"{AHElCMn}\",[]]Aj[\"{{{AHO`{AILd}}}CMn}\",[]]A`[\"{AHEnCMn}\",[]]Ce[\"{{{AHO`{CGbAINn}}{KFl{KHl}}}{{AHO`{CGbAINn}}}}\",[]]Ce[\"{{{AHO`{CGbAIO`}}{KFl{KHl}}}{{AHO`{CGbAIO`}}}}\",[]]Aa[\"{ABDhAJ@f}\",[]]Aa[\"{ABDjAJ@f}\",[]]Aa[\"{ABDlAJ@f}\",[]]Aa[\"{AJ@fAHCb}\",[]]Aa[\"{AJ@fAHCd}\",[]]o[\"{AAjCMn}\",[]]Bn[\"{{{AHO`{CGbBCOl}}CMn}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}CMn}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHAlCMn}\",[]]Bo[\"{{{AHO`{CGbBCOl}}AB@d}{{AHO`{CGbBCOl}}}}\",[]]Bo[\"{{{AHO`{CGbBCOn}}AB@d}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHAlKFl}\",[]]k[\"{{}}\",[]]0000000000Bh[\"{ABA`c{}}\",[\"IntoFuture::IntoFuture\"]]11Aj[\"{{{AHO`{BAOd}}}DC`}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]o[\"{AAjKCb}\",[]]Ao[\"{{AHO`c}{{NKf{c}}}{}}\",[\"S\"]]0C`[\"{{{AHO`{CGbAILf}}NK`CNbDC`{KFl{CNb}}}ICj}\",[]]Bo[\"{{{AHO`{CGbCNj}}NK`CNbDC`{KFl{CNb}}}ICj}\",[]]C`[\"{{{AHO`{CGbAILh}}NK`CNbDC`{KFl{CNb}}}ICj}\",[]]Bo[\"{{{AHO`{CGbCNl}}NK`CNbDC`{KFl{CNb}}}ICj}\",[]]Bk[\"{{{AHO`{CGbIHl}}NK`{AHO`{BCN`}}}ICj}\",[]]A`[\"{BFBjKFl}\",[]]A`[\"{BFBlKFl}\",[]]Bn[\"{{{AHO`{KI`}}{AHO`{KI`}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{KFn}}{AHO`{KFn}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{AOL`}}{AHO`{AOL`}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{B@Mf}}{AHO`{B@Mf}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{DAl}}{AHO`{DAl}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{A@j}}{AHO`{A@j}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{ABCd}}{AHO`{ABCd}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{KIb}}{AHO`{KIb}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{BFDf}}{AHO`{BFDf}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{KG`}}{AHO`{KG`}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{AHFd}}{AHO`{AHFd}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{BBBl}}{AHO`{BBBl}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{AJAn}}{AHO`{AJAn}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{AOLb}}{AHO`{AOLb}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{BEGb}}{AHO`{BEGb}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{BFCl}}{AHO`{BFCl}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{B@Mh}}{AHO`{B@Mh}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{DAn}}{AHO`{DAn}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{A@l}}{AHO`{A@l}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{ABCf}}{AHO`{ABCf}}}{{KFl{ABBj}}}}\",[]]Ak[\"{{{AHO`{AILb}}}AINh}\",[]]Bk[\"{{{AHO`{AILd}}KG`}{{KGd{AINlAIKj}}}}\",[]]Cc[\"{{{AHO`{CGbAIOd}}KCb}{{KGd{{ABh{Oh}}BC@h}}}}\",[]]Bb[\"{{{AHO`{CGbAILd}}ABCf}AIOf}\",[]]Aj[\"{{{AHO`{AHCl}}}KCb}\",[]]Aj[\"{{{AHO`{AHCn}}}KCb}\",[]]Bb[\"{{{AHO`{CGbAILd}}ABCf}AJ@b}\",[]]Bn[\"{{{AHO`{CGbBCOl}}CNb}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}CNb}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHAlKFl}\",[]]A`[\"{BAOjKFl}\",[]]A`[\"{BAOlKFl}\",[]]Cf[\"{{{AHO`{CGbBABd}}{A@b{AJ@n}}}{{AHO`{CGbBABd}}}}\",[]]Cf[\"{{{AHO`{CGbBABh}}{A@b{AJA`}}}{{AHO`{CGbBABh}}}}\",[]]Cf[\"{{{AHO`{CGbB@Kh}}{A@b{AJAf}}}{{AHO`{CGbB@Kh}}}}\",[]]Cf[\"{{{AHO`{CGbB@Kl}}{A@b{AJAh}}}{{AHO`{CGbB@Kl}}}}\",[]]o[\"{AAjCNb}\",[]]Bn[\"{{{AHO`{CGbBFIf}}CMn}{{AHO`{CGbBFIf}}}}\",[]]Bn[\"{{{AHO`{CGbBFIh}}CMn}{{AHO`{CGbBFIh}}}}\",[]]Ak[\"{{{AHO`{AILb}}}BGJ`}\",[]]Aj[\"{{{AHO`{DA`}}}BCAf}\",[]]Aj[\"{{{A@b{BABf}}}BABd}\",[]]Aj[\"{{{A@b{BABj}}}BABh}\",[]]Aj[\"{{BCNjc}BCNjABCn}\",[\"\"]]Aj[\"{{BCNlc}BCNlABCn}\",[\"\"]]Bm[\"{{{AHO`{CGbAJ@`}}ICn}{{KGd{ICjAJAl}}}}\",[]]n[\"{{}KFb}\",[]]o[\"{NIlNIl}\",[]]0000Bd[\"{{{AHO`{BGOj}}}{{KFl{AB@d}}}}\",[]]Bd[\"{{{AHO`{BGOl}}}{{KFl{AB@d}}}}\",[]]Bd[\"{{{AHO`{BI@n}}}{{KFl{AB@d}}}}\",[]]Bd[\"{{{AHO`{BIAh}}}{{KFl{AB@d}}}}\",[]]Bd[\"{{{AHO`{AILb}}}{{KFl{BCLn}}}}\",[]]Ck[\"{{{AHO`{AB@h}}B@KhAJ@f{AHO`{CMl}}}{{KGd{AIKnB@M`}}}}\",[]]Ak[\"{{{AHO`{BAAl}}}AB@d}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]An[\"{{AIObDC`}{{KGh{KCb}}}}\",[]]Cl[\"{{{AHO`{NMf}}}{{KFl{{KC`{{A@d{AHCl}}{A@d{AHDj}}}}}}}}\",[]]Cm[\"{{{AHO`{AJAb}}}{{KFl{{KC`{{A@d{AHCl}}{A@d{AHDj}}}}}}}}\",[]]Cl[\"{{{AHO`{NMh}}}{{KFl{{KC`{{A@d{AHCn}}{A@d{AHDl}}}}}}}}\",[]]Cm[\"{{{AHO`{AJAd}}}{{KFl{{KC`{{A@d{AHCn}}{A@d{AHDl}}}}}}}}\",[]]A`[\"{BGGnABh}\",[]]A`[\"{BIDnABh}\",[]]A`[\"{BGH`ABh}\",[]]A`[\"{BIE`ABh}\",[]]An[\"{{{AHO`{CGbBGOj}}}B@Mf}\",[]]An[\"{{{AHO`{CGbBGOl}}}B@Mh}\",[]]An[\"{{{AHO`{CGbBI@n}}}B@Mh}\",[]]An[\"{{{AHO`{CGbBIAh}}}B@Mh}\",[]]Ak[\"{{{AHO`{BCAd}}}B@Oj}\",[]]Ba[\"{{{AHO`{CGbAILd}}BCMd}ICj}\",[]]Bo[\"{{{AHO`{CGbAB@j}}BEGbBAOd}{{KFl{BCMd}}}}\",[]]B`[\"{{{AHO`{NMf}}B@MfDAl}D@d}\",[]]Bn[\"{{{AHO`{BABf}}CN`B@Mf}{{KGd{D@dBFLf}}}}\",[]]Ba[\"{{{AHO`{AJAb}}B@MfDAl}D@d}\",[]]Bn[\"{{{AHO`{BEJf}}CN`B@Mf}{{KGd{D@dBFLf}}}}\",[]]B`[\"{{{AHO`{NMh}}B@MhDAn}D@f}\",[]]Bn[\"{{{AHO`{BABj}}CN`B@Mh}{{KGd{D@fBFLl}}}}\",[]]Ba[\"{{{AHO`{AJAd}}B@MhDAn}D@f}\",[]]Bn[\"{{{AHO`{BEJh}}CN`B@Mh}{{KGd{D@fBFLl}}}}\",[]]Bn[\"{{{AHO`{BHKd}}CN`B@Md}{{KGd{D@fBFLl}}}}\",[]]Aj[\"{{}{{NM`{c}}}{}}\",[\"T\"]]0A`[\"{AHElCNb}\",[]]A`[\"{BCMfCNb}\",[]]A`[\"{AHEnCNb}\",[]]A`[\"{BCMhCNb}\",[]]Bn[\"{{{AHO`{CGbBABd}}KCb}{{AHO`{CGbBABd}}}}\",[]]Bn[\"{{{AHO`{CGbBABh}}KCb}{{AHO`{CGbBABh}}}}\",[]]Aj[\"{{{AHO`{BBL`}}}DC`}\",[]]0Aj[\"{{{AHO`{BCAd}}}DC`}\",[]]Bg[\"{{{AHO`{DA`}}}{{KGd{AHCbAIKd}}}}\",[]]Bk[\"{{{AHO`{AILd}}KG`}{{KGd{AHCdAIKj}}}}\",[]]Cc[\"{{{AHO`{BCN`}}{AHO`{AIOb}}CNb}{{A@b{AONh}}}}\",[]]Ak[\"{{{AHO`{ABA`}}}B@Mf}\",[]]Ak[\"{{{AHO`{ABAb}}}B@Mh}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Bf[\"{{{AHO`{CGbAILd}}}{{KFl{NK`}}}}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{A@b{AOLn}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{A@b{AOM`}}}{{AHO`{CGbBCOn}}}}\",[]]43A`[\"{AAjAOMf}\",[]]A`[\"{ABDhKFl}\",[]]A`[\"{ABDjKFl}\",[]]A`[\"{ABDlKFl}\",[]]A`[\"{BFBjKFl}\",[]]A`[\"{BFBlKFl}\",[]]Ai[\"{{{AHO`{NNf}}}KCb}\",[]]Bo[\"{{{AHO`{CGbBI@n}}AB@d}{{AHO`{CGbBI@n}}}}\",[]]Bo[\"{{{AHO`{CGbBIAh}}AB@d}{{AHO`{CGbBIAh}}}}\",[]]Bj[\"{{{AHO`{AJ@`}}CEl}{{KGd{ICjB@Ld}}}}\",[]]Bm[\"{{{AHO`{CGbAJ@b}}CEl}{{KGd{ICjB@Ll}}}}\",[]]?>A`[\"{IHjAONf}\",[]]A`[\"{AOJ`IHj}\",[]]Bj[\"{{{A@b{B@Kf}}IHn}{{KGd{BEJbBGHn}}}}\",[]]Bj[\"{{{A@b{BABb}}IHn}{{KGd{BEJfBGHn}}}}\",[]]Bj[\"{{{A@b{B@Kf}}IHn}{{KGd{BEJdBGI`}}}}\",[]]Bj[\"{{{A@b{BABb}}IHn}{{KGd{BEJhBGI`}}}}\",[]]Dj[\"{{{AHO`{CGbAJ@b}}{AHO`{CGb{AHO`{CGb{K@j{ICn}}}}}}}{{KGd{KCbAJAn}}}}\",[]]Aj[\"{{{AHO`{AILd}}}DC`}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]A`[\"{AHAlDC`}\",[]]Cd[\"{{{AHO`{CGbBBMh}}{A@b{A`}}}{{AHO`{CGbBBMh}}}}\",[]]Cd[\"{{{AHO`{CGbBBMj}}{A@b{A`}}}{{AHO`{CGbBBMj}}}}\",[]]Bk[\"{{{AHO`{BBL`}}}{{ABb{{A@d{AHFj}}}}}}\",[]]o[\"{AAjCNb}\",[]]Aa[\"{AONfAHD`}\",[]]76Aa[\"{AIOjABA`}\",[]]Aa[\"{AIOlABAb}\",[]]Cf[\"{{{AHO`{CGbBFGh}}{KFl{AB@d}}}{{AHO`{CGbBFGh}}}}\",[]]Cf[\"{{{AHO`{CGbBFGj}}{KFl{AB@d}}}{{AHO`{CGbBFGj}}}}\",[]]9;:An[\"{{{AHO`{AONh}}AB@d}ICj}\",[]]B`[\"{{{AHO`{CGbAILf}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbCNj}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbIDh}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbNKl}}CMn}ICj}\",[]]B`[\"{{{AHO`{CGbAILh}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbCNl}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbIDj}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbNKn}}CMn}ICj}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]10Bj[\"{{{AHO`{AILb}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bi[\"{{{AHO`{NMf}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bj[\"{{{AHO`{AJAb}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bi[\"{{{AHO`{NMh}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bj[\"{{{AHO`{AJAd}}}{{KFl{{A@d{A@`}}}}}}\",[]]Db[\"{{{ABb{{AHO`{CGbAJ@`}}}}{AHO`{CGbNIn}}}{{DAh{{KGf{ICj}}}}}}\",[]]Db[\"{{{AHO`{CGbAILd}}NK`B@Oj{AHO`{CGb{ABh{Oh}}}}}{{KFl{ABDj}}}}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{AHO`{CMl}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{AHO`{CMl}}}{{AHO`{CGbBCOn}}}}\",[]]Ak[\"{{{AHO`{AILb}}}BAA`}\",[]]Bj[\"{{{AHO`{AILb}}ICn}{{KGd{ICjBFDd}}}}\",[]]Bn[\"{{{AHO`{CGbBCOl}}DC`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}DC`}{{AHO`{CGbBCOn}}}}\",[]]Ai[\"{{{AHO`{NJj}}}KCb}\",[]]Ai[\"{{{AHO`{NJl}}}KCb}\",[]]Dh[\"{{{AHO`{B@Kj}}CN`{AHO`{CMl}}{AHO`{BGAn}}}{{KGd{{A@d{NMf}}B@M`}}}}\",[]]Cb[\"{{{AHO`{BABf}}CN`{AHO`{BGAn}}}{{A@d{NMf}}}}\",[]]Dh[\"{{{AHO`{BEJb}}CN`{AHO`{CMl}}{AHO`{BGAn}}}{{KGd{{A@d{NMf}}B@M`}}}}\",[]]Cb[\"{{{AHO`{BEJf}}CN`{AHO`{BGAn}}}{{A@d{NMf}}}}\",[]]Dh[\"{{{AHO`{B@Kn}}CN`{AHO`{CMl}}{AHO`{BGAn}}}{{KGd{{A@d{NMh}}B@Mb}}}}\",[]]Cb[\"{{{AHO`{BABj}}CN`{AHO`{BGAn}}}{{A@d{NMh}}}}\",[]]Dh[\"{{{AHO`{BEJd}}CN`{AHO`{CMl}}{AHO`{BGAn}}}{{KGd{{A@d{NMh}}B@Mb}}}}\",[]]Cb[\"{{{AHO`{BEJh}}CN`{AHO`{BGAn}}}{{A@d{NMh}}}}\",[]]Dh[\"{{{AHO`{BHKb}}CN`{AHO`{CMl}}{AHO`{BGAn}}}{{KGd{{A@d{NMh}}B@Ln}}}}\",[]]Cb[\"{{{AHO`{BHKd}}CN`{AHO`{BGAn}}}{{A@d{NMh}}}}\",[]]Dd[\"{{{ABb{{AHO`{CGbc}}}}{AHO`{CGbNIn}}}{{DAh{{KFl{KGd}}}}}{}}\",[\"S\"]]0Bn[\"{{AJ@fBBLbAAj}{{KGh{{KC`{AB@fAIL`}}}}}}\",[]]Bd[\"{{{AHO`{AILd}}}{{AHO`{NMh}}}}\",[]]A`[\"{AOJ`IHj}\",[]]Cb[\"{{{AHO`{AOLn}}DAlAJ@fB@MfNK`}{{KFl{AINn}}}}\",[]]Cb[\"{{{AHO`{BCNj}}DAlAJ@fB@MfNK`}{{KFl{AINn}}}}\",[]]Cb[\"{{{AHO`{AOM`}}DAnAJ@fB@MhNK`}{{KFl{AIO`}}}}\",[]]Cb[\"{{{AHO`{BCNl}}DAnAJ@fB@MhNK`}{{KFl{AIO`}}}}\",[]]B`[\"{{{AHO`{CGbAILd}}NK`}ICj}\",[]]Ca[\"{{{AHO`{CGbAIKn}}}{{KGd{{A@d{A@`}}BCLn}}}}\",[]]Bj[\"{{{AHO`{AILb}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bi[\"{{{AHO`{NMf}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bj[\"{{{AHO`{AJAb}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bi[\"{{{AHO`{NMh}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bj[\"{{{AHO`{AJAd}}}{{KFl{{A@d{A@`}}}}}}\",[]]o[\"{AILnOh}\",[]]o[\"{AIM`Oh}\",[]]Ai[\"{{{AHO`{CMl}}}DC`}\",[]]Aj[\"{{{AHO`{AILf}}}CNb}\",[]]Ai[\"{{{AHO`{CNj}}}CNb}\",[]]Bn[\"{{{AHO`{CGbAIJl}}CNb}{{AHO`{CGbAIJl}}}}\",[]]Ai[\"{{{AHO`{IDh}}}CNb}\",[]]Bn[\"{{{AHO`{CGbAOJn}}CNb}{{AHO`{CGbAOJn}}}}\",[]]Ai[\"{{{AHO`{NKl}}}CNb}\",[]]Bn[\"{{{AHO`{CGbBBAl}}CNb}{{AHO`{CGbBBAl}}}}\",[]]Aj[\"{{{AHO`{AILh}}}CNb}\",[]]Ai[\"{{{AHO`{CNl}}}CNb}\",[]]Bn[\"{{{AHO`{CGbAIJn}}CNb}{{AHO`{CGbAIJn}}}}\",[]]Ai[\"{{{AHO`{IDj}}}CNb}\",[]]Bn[\"{{{AHO`{CGbAOK`}}CNb}{{AHO`{CGbAOK`}}}}\",[]]Ai[\"{{{AHO`{NKn}}}CNb}\",[]]Bn[\"{{{AHO`{CGbBBAn}}CNb}{{AHO`{CGbBBAn}}}}\",[]]Aa[\"{AIOdBGB`}\",[]]Ai[\"{{{AHO`{NMf}}}DC`}\",[]]Aj[\"{{{AHO`{AJAb}}}DC`}\",[]]Aj[\"{{{AHO`{AILd}}}DC`}\",[]]Ai[\"{{{AHO`{NMh}}}DC`}\",[]]Aj[\"{{{AHO`{AJAd}}}DC`}\",[]]Co[\"{{{AHO`{NMf}}B@Mf{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}DC`}\",[]]D`[\"{{{AHO`{AJAb}}B@Mf{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}DC`}\",[]]Co[\"{{{AHO`{NMh}}B@Mh{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}DC`}\",[]]D`[\"{{{AHO`{AJAd}}B@Mh{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}DC`}\",[]]Bn[\"{{{AHO`{CGbBFIf}}CMn}{{AHO`{CGbBFIf}}}}\",[]]Bn[\"{{{AHO`{CGbBFIh}}CMn}{{AHO`{CGbBFIh}}}}\",[]]Cd[\"{{{AHO`{CGbNMf}}}{{KFl{{NKb{{A@d{AHDj}}}}}}}}\",[]]Ce[\"{{{AHO`{CGbAJAb}}}{{KFl{{NKb{{A@d{AHDj}}}}}}}}\",[]]Cd[\"{{{AHO`{CGbNMh}}}{{KFl{{NKd{{A@d{AHDl}}}}}}}}\",[]]Ce[\"{{{AHO`{CGbAJAd}}}{{KFl{{NKd{{A@d{AHDl}}}}}}}}\",[]]Bf[\"{{{AHO`{CGbAILf}}CMnCNbNK`}ICj}\",[]]Be[\"{{{AHO`{CGbCNj}}CMnCNbNK`}ICj}\",[]]Bf[\"{{{AHO`{CGbAILh}}CMnCNbNK`}ICj}\",[]]Be[\"{{{AHO`{CGbCNl}}CMnCNbNK`}ICj}\",[]]Bf[\"{{{AHO`{CGbAILf}}NK`CMnCNb}ICj}\",[]]Be[\"{{{AHO`{CGbCNj}}NK`CMnCNb}ICj}\",[]]Bf[\"{{{AHO`{CGbAILh}}NK`CMnCNb}ICj}\",[]]Be[\"{{{AHO`{CGbCNl}}NK`CMnCNb}ICj}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]De[\"{{{AHO`{CGbBCOl}}c{AHO`{CMl}}}{{AHO`{CGbBCOl}}}{{ICl{D@n}}}}\",[\"\"]]De[\"{{{AHO`{CGbBCOn}}c{AHO`{CMl}}}{{AHO`{CGbBCOn}}}{{ICl{D@n}}}}\",[\"\"]]Ci[\"{{{AHO`{CGbNMf}}{AHO`{{K@j{Oh}}}}}{{KGd{DC`IEj}}}}\",[]]Cj[\"{{{AHO`{CGbAJAb}}{AHO`{{K@j{Oh}}}}}{{KGd{DC`IEj}}}}\",[]]Cj[\"{{{AHO`{CGbNMh}}{AHO`{{K@j{Oh}}}}}{{KGd{DC`BCAb}}}}\",[]]Ck[\"{{{AHO`{CGbAJAd}}{AHO`{{K@j{Oh}}}}}{{KGd{DC`BCAb}}}}\",[]]o[\"{AAjDC`}\",[]]Bn[\"{{{AHO`{CGbBCOl}}KI`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}KIb}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHAlKFl}\",[]]Ca[\"{{{AHO`{CGbAIOd}}}{{KGd{{KFl{KI`}}AIOh}}}}\",[]]Ca[\"{{{AHO`{CGbBGB`}}}{{KGd{{KFl{KI`}}AIOh}}}}\",[]]Ca[\"{{{AHO`{CGbAIOf}}}{{KGd{{KFl{KIb}}B@Ll}}}}\",[]]Ak[\"{{{AHO`{AIKn}}}AJ@f}\",[]]Ak[\"{{{AHO`{ABA`}}}AJ@f}\",[]]Bg[\"{{{AHO`{DA`}}}{{KGd{AJ@fAIKd}}}}\",[]]Ak[\"{{{AHO`{ABAb}}}AJ@f}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]B`[\"{{KI`c}KI`{{D@b{KI`}}}}\",[\"\"]]B`[\"{{KFnc}KFn{{D@b{KFn}}}}\",[\"\"]]B`[\"{{KIbc}KIb{{D@b{KIb}}}}\",[\"\"]]B`[\"{{KG`c}KG`{{D@b{KG`}}}}\",[\"\"]]20Ae[\"{{AB@dCNb}AG`}\",[]]Bn[\"{{{AHO`{CGbBCOl}}AFn}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}AFn}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{IHjAB@d}\",[]]B`[\"{{}{{AHO`{{K@j{BG@n}}}}}}\",[]]An[\"{{AHO`{ONf{CGbOh}}}ICj}\",[]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000An[\"{{}{{KFl{{A@b{NMd}}}}}}\",[]]Bn[\"{{{AHO`{CGbBBMh}}DC`}{{AHO`{CGbBBMh}}}}\",[]]Bn[\"{{{AHO`{CGbBBMj}}DC`}{{AHO`{CGbBBMj}}}}\",[]]Aj[\"{{{AHO`{AHDj}}}CNb}\",[]]Aj[\"{{{AHO`{AHDl}}}CNb}\",[]]A`[\"{ABC`KFl}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]10Bf[\"{{{AHO`{CMl}}}{{KGh{CNbBBB`}}}}\",[]]Bn[\"{{{AHO`{AB@h}}{A@d{BBL`}}}{{KGf{ICj}}}}\",[]]Ai[\"{AIL`{{KGh{ICj}}}}\",[]]Cj[\"{{{AHO`{NMb}}BBLbBBBj{AHO`{AAj}}}{{KC`{AJ@fAB@f}}}}\",[]]Cb[\"{{{AHO`{CGbAILd}}KG`AINl}{{KGd{AINlBFKf}}}}\",[]]Am[\"{{{AHO`{AILb}}CNb}ICj}\",[]]B`[\"{{{AHO`{CGbAILd}}CNb}ICj}\",[]]A`[\"{BC@jDC`}\",[]]B`[\"{{{AHO`{CGbIHj}}BC@l}ICj}\",[]]Aa[\"{AONfAHD`}\",[]]Bn[\"{{{AHO`{NMd}}AHFl}{{KGf{{A@d{BBL`}}}}}}\",[]]Bo[\"{{{AHO`{BACb}}AHFl}{{KGf{{A@d{BBL`}}}}}}\",[]]Bo[\"{{{AHO`{AOMl}}AHFl}{{KGf{{A@d{BBL`}}}}}}\",[]]Ci[\"{{{AHO`{CGbNMf}}{AHO`{CGb{ABh{Oh}}}}}{{KFl{D@d}}}}\",[]]Cj[\"{{{AHO`{CGbAJAb}}{AHO`{CGb{ABh{Oh}}}}}{{KFl{D@d}}}}\",[]]Ci[\"{{{AHO`{CGbNMh}}{AHO`{CGb{ABh{Oh}}}}}{{KFl{D@f}}}}\",[]]Cj[\"{{{AHO`{CGbAJAd}}{AHO`{CGb{ABh{Oh}}}}}{{KFl{D@f}}}}\",[]]Cb[\"{{{AHO`{ABD`}}IChAJ@lAB@d}{{KGd{ICjBCOf}}}}\",[]]Cb[\"{{{AHO`{BANd}}IChAJ@lAB@d}{{KGd{ICjBCOf}}}}\",[]]Cb[\"{{{AHO`{B@Ol}}IChAJ@lAB@d}{{KGd{ICjBCOf}}}}\",[]]Cb[\"{{{AHO`{ABDb}}IChAJ@lAB@d}{{KGd{ICjBCOh}}}}\",[]]Cb[\"{{{AHO`{BANf}}IChAJ@lAB@d}{{KGd{ICjBCOh}}}}\",[]]Cb[\"{{{AHO`{B@On}}IChAJ@lAB@d}{{KGd{ICjBCOh}}}}\",[]]Al[\"{{}{{KGd{B@KdBGI`}}}}\",[]]Bn[\"{{{AHO`{AILb}}KFn}{{KFl{{A@d{AILf}}}}}}\",[]]Bo[\"{{{AHO`{AILd}}KG`}{{KFl{{AHO`{AILh}}}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Aj[\"{{{AHO`{AILb}}}ICj}\",[]]Am[\"{{{AHO`{CGbAILd}}}ICj}\",[]]Bf[\"{{{AHO`{AIJb}}}{{KGd{AAjIEn}}}}\",[]]Bf[\"{{{AHO`{AIJb}}}{{KGd{A@hIEn}}}}\",[]]Bg[\"{{{AHO`{AIJb}}}{{KGd{AAOfIEn}}}}\",[]]Bg[\"{{{AHO`{AIJb}}}{{KGd{AHAlIEn}}}}\",[]]Bf[\"{{{AHO`{AIJb}}}{{KGd{AAlIEn}}}}\",[]]Bf[\"{{{AHO`{AIJb}}}{{KGd{AAnIEn}}}}\",[]]Ak[\"{{{AHO`{BCAd}}}B@Oj}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{KFl{AOL`}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{KFl{AOLb}}}{{AHO`{CGbBCOn}}}}\",[]]<;Aj[\"{{{AHO`{AB@h}}}KCb}\",[]]Aj[\"{{{AHO`{AB@j}}}KCb}\",[]]Bo[\"{{{AHO`{AILb}}cAINj}ABBh{{D@b{AHCb}}}}\",[\"\"]]Co[\"{{{AHO`{CGbAILd}}AHCdAINlNK`}{{KGd{{KC`{KG`DC`}}AHEb}}}}\",[]]Bn[\"{{{AHO`{CGbBCOl}}CN`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}CN`}{{AHO`{CGbBCOn}}}}\",[]]Df[\"{{{AHO`{CGbAIOd}}{AHO`{CGb{K@j{ICn}}}}}{{KGd{{KFl{KCb}}AHFb}}}}\",[]]Ai[\"{AIL`{{KGh{ICj}}}}\",[]]Bg[\"{{{AHO`{BCAd}}AJAj}{{KGf{KCb}}}}\",[]]A`[\"{AHAlCNb}\",[]]10Aj[\"{{{AHO`{AAj}}}BCOj}\",[]]Cf[\"{{{AHO`{CGbBABd}}{A@b{BCOl}}}{{AHO`{CGbBABd}}}}\",[]]Cf[\"{{{AHO`{CGbB@Kh}}{A@b{BCOl}}}{{AHO`{CGbB@Kh}}}}\",[]]Cf[\"{{{AHO`{CGbBABh}}{A@b{BCOn}}}{{AHO`{CGbBABh}}}}\",[]]Cf[\"{{{AHO`{CGbB@Kl}}{A@b{BCOn}}}{{AHO`{CGbB@Kl}}}}\",[]]Aa[\"{BABdBH@l}\",[]]Aa[\"{BABhBH@n}\",[]]Bj[\"{{{ABh{BBLb}}BBBj}{{KGd{BABdIFb}}}}\",[]]Bj[\"{{{ABh{BBLb}}BBBj}{{KGd{BABhIFd}}}}\",[]]Co[\"{{{AHO`{CGbAJ@`}}{AHO`{CGb{K@j{ICn}}}}}{{KGd{ICjAJAl}}}}\",[]]A`[\"{AHElCNb}\",[]]A`[\"{AHEnCNb}\",[]]A`[\"{BFBjCNb}\",[]]A`[\"{BFBlCNb}\",[]]Bc[\"{{{AHO`{AILb}}}{{KFl{KCb}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]10Aj[\"{{{AHO`{AHBb}}}KCb}\",[]]Be[\"{{{AHO`{AB@h}}{KFl{BABd}}}ICj}\",[]]Bo[\"{{{AHO`{CGbAB@j}}{KFl{{A@b{BABh}}}}}ICj}\",[]]Bd[\"{{{AHO`{BG@n}}}{{KFl{BBBh}}}}\",[]]Aj[\"{{{AHO`{AB@h}}}ICj}\",[]]Dj[\"{{{AHO`{CGbAJ@`}}{AHO`{CGb{AHO`{CGb{K@j{ICn}}}}}}}{{KGd{KCbAJAl}}}}\",[]]o[\"{{}AHO`}\",[]]n[\"{{}NIl}\",[]]0000Bn[\"{{{AHO`{CGbBCOl}}KCb}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}KCb}{{AHO`{CGbBCOn}}}}\",[]]o[\"{CNbKI`}\",[]]o[\"{CNbKIb}\",[]]A`[\"{BAOfCNb}\",[]]Bf[\"{{{AHO`{BGJ`}}{AHO`{BGJ`}}}DC`}\",[]]Bo[\"{{{AHO`{CGbBBMh}}AB@d}{{AHO`{CGbBBMh}}}}\",[]]Bo[\"{{{AHO`{CGbBBMj}}AB@d}{{AHO`{CGbBBMj}}}}\",[]]Ae[\"{{KCbCNb}BANd}\",[]]Ae[\"{{KCbCNb}BANf}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]7Bg[\"{{{AHO`{AILd}}KG`}{{KFl{AINl}}}}\",[]]An[\"{{{AHO`{AILb}}ICn}BAAn}\",[]]Bm[\"{{{AHO`{CGbBEJb}}{ABh{{ABh{Oh}}}}}ICj}\",[]]Bm[\"{{{AHO`{CGbBEJf}}{ABh{{ABh{Oh}}}}}ICj}\",[]]Bm[\"{{{AHO`{CGbBEJd}}{ABh{{ABh{Oh}}}}}ICj}\",[]]Bm[\"{{{AHO`{CGbBEJh}}{ABh{{ABh{Oh}}}}}ICj}\",[]]Am[\"{{{AHO`{AILb}}KI`}ICj}\",[]]B`[\"{{{AHO`{CGbAILd}}KIb}ICj}\",[]]Ce[\"{{{AHO`{CGbBBMh}}{ABh{CN`}}}{{AHO`{CGbBBMh}}}}\",[]]Ce[\"{{{AHO`{CGbBBMj}}{ABh{CN`}}}{{AHO`{CGbBBMj}}}}\",[]]Fg[\"{{{AHO`{BHEb}}{AHO`{BBLb}}{AHO`{{K@j{BBLb}}}}{AHO`{AJ@d}}{AHO`{{K@j{Oh}}}}ABEb}{{KGd{BFK`IEl}}}}\",[]]Af[\"{{BCNjNK`}BCNj}\",[]]Af[\"{{BCNlNK`}BCNl}\",[]]A`[\"{BAOfCNb}\",[]]Al[\"{BACd{{KGd{DAlIF`}}}}\",[]]Al[\"{BACd{{KGd{A@jIF`}}}}\",[]]Ai[\"{BACd{{KGj{DAn}}}}\",[]]Ai[\"{BACd{{KGj{A@l}}}}\",[]]o[\"{NIlNIl}\",[]]Bo[\"{{{AHO`{CGbBFIf}}AB@d}{{AHO`{CGbBFIf}}}}\",[]]Bo[\"{{{AHO`{CGbBFIh}}AB@d}{{AHO`{CGbBFIh}}}}\",[]]Bb[\"{{{AHO`{NMf}}}{{KFl{DC`}}}}\",[]]Bc[\"{{{AHO`{AJAb}}}{{KFl{DC`}}}}\",[]]Bb[\"{{{AHO`{NMh}}}{{KFl{DC`}}}}\",[]]Bc[\"{{{AHO`{AJAd}}}{{KFl{DC`}}}}\",[]]Bg[\"{{{AHO`{AILb}}}{{KGd{ICjBCLn}}}}\",[]]Ba[\"{{{AHO`{BFCf}}KFnAHCb}DC`}\",[]]Ba[\"{{{AHO`{BFCh}}KG`AHCd}DC`}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{KFl{AB@d}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{KFl{AB@d}}}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHElCNb}\",[]]A`[\"{AHEnCNb}\",[]]Bo[\"{{{AHO`{CGbAILf}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbCNj}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbIDh}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbNKl}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bo[\"{{{AHO`{CGbAILh}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbCNl}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbIDj}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbNKn}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]A`[\"{BAOfCNb}\",[]]Al[\"{{{AHO`{NNf}}A@l}CNb}\",[]]Bi[\"{{{AHO`{CGbAIOn}}CNb}{{KGh{ICj}}}}\",[]]<;A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]10Cf[\"{{{AHO`{CGbBCOl}}{KFl{BFGh}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{KFl{BFGj}}}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHElCNb}\",[]]A`[\"{AHEnCNb}\",[]]Bi[\"{{{AHO`{CGbAIJb}}}{{KGd{AAjIEn}}}}\",[]]Bi[\"{{{AHO`{CGbAIJb}}}{{KGd{A@hIEn}}}}\",[]]Bj[\"{{{AHO`{CGbAIJb}}}{{KGd{AAOfIEn}}}}\",[]]Bj[\"{{{AHO`{CGbAIJb}}}{{KGd{AHAlIEn}}}}\",[]]Bi[\"{{{AHO`{CGbAIJb}}}{{KGd{AAlIEn}}}}\",[]]Bi[\"{{{AHO`{CGbAIJb}}}{{KGd{AAnIEn}}}}\",[]]Cd[\"{{{A@b{AIL`}}CNbDC`}{{KGh{{KC`{BC@lBC@l}}}}}}\",[]]Bn[\"{{{AHO`{CGbBABd}}CNb}{{AHO`{CGbBABd}}}}\",[]]Bn[\"{{{AHO`{CGbBABh}}CNb}{{AHO`{CGbBABh}}}}\",[]]Aj[\"{{{AHO`{AILb}}}DC`}\",[]]Ak[\"{{{AHO`{BBL`}}}B@Oj}\",[]]0Ci[\"{{{AHO`{CGbBBMh}}CMn}{{KGd{{AHO`{CGbBBMh}}AOJd}}}}\",[]]Ci[\"{{{AHO`{CGbBBMj}}CMn}{{KGd{{AHO`{CGbBBMj}}AOJf}}}}\",[]]A`[\"{AHAlCMn}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{KFl{BFIf}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{KFl{BFIh}}}{{AHO`{CGbBCOn}}}}\",[]]Bg[\"{{{AHO`{CGbAILd}}}{{KFl{BAOd}}}}\",[]]Cf[\"{{{AHO`{CGbBABd}}{KFl{BABl}}}{{AHO`{CGbBABd}}}}\",[]]Cf[\"{{{AHO`{CGbBABh}}{KFl{BABl}}}{{AHO`{CGbBABh}}}}\",[]]Cf[\"{{{AHO`{CGbBABd}}{KFl{BABn}}}{{AHO`{CGbBABd}}}}\",[]]Cf[\"{{{AHO`{CGbBABh}}{KFl{BABn}}}{{AHO`{CGbBABh}}}}\",[]]Cd[\"{{}{{KGd{{KC`{{ABh{BBLb}}BBBj}}{A@d{IEd}}}}}}\",[]]Bn[\"{{{AHO`{CGbBFGh}}KI`}{{AHO`{CGbBFGh}}}}\",[]]Bn[\"{{{AHO`{CGbBFGj}}KIb}{{AHO`{CGbBFGj}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Bo[\"{{{AHO`{CGbBABd}}AB@d}{{AHO`{CGbBABd}}}}\",[]]Bo[\"{{{AHO`{CGbBABh}}AB@d}{{AHO`{CGbBABh}}}}\",[]]Ci[\"{{{AHO`{DA`}}{KFl{AB@d}}}{{KGd{{KFl{AB@d}}AIKd}}}}\",[]]Bj[\"{{{AHO`{BCAd}}AJAjKCb}{{KGf{ICj}}}}\",[]]054Bm[\"{{{AHO`{NMf}}}{{KGd{{KFl{BGAn}}IEj}}}}\",[]]Bn[\"{{{AHO`{AJAb}}}{{KGd{{KFl{BGAn}}IEj}}}}\",[]]Bn[\"{{{AHO`{NMh}}}{{KGd{{KFl{BGAn}}BCAb}}}}\",[]]Bo[\"{{{AHO`{AJAd}}}{{KGd{{KFl{BGAn}}BCAb}}}}\",[]]Aj[\"{{{AHO`{AHDj}}}CNb}\",[]]Aj[\"{{{AHO`{AHDl}}}CNb}\",[]]Bl[\"{{{AHO`{AB@h}}{KFl{{A@b{BFCf}}}}}ICj}\",[]]Cc[\"{{{AHO`{CGbAILd}}{KFl{{AHO`{BFCh}}}}NK`}ICj}\",[]]Aj[\"{{{AHO`{AB@j}}}CNb}\",[]]Bn[\"{{{AHO`{CGbBBAl}}AFn}{{AHO`{CGbBBAl}}}}\",[]]Bn[\"{{{AHO`{CGbBBAn}}AFn}{{AHO`{CGbBBAn}}}}\",[]]Ak[\"{{{AHO`{AHFj}}}B@Oj}\",[]]0Ak[\"{{{AHO`{AILb}}}BGA`}\",[]]Cb[\"{{{AHO`{AILd}}KG`}{{KGd{{KFl{AJ@f}}AIKj}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Bn[\"{{{AHO`{CGbBCOl}}KI`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}KIb}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHAlKFl}\",[]]Bo[\"{{{AHO`{AHAl}}{AHO`{CMl}}}{{KGf{BCOj}}}}\",[]]Em[\"{{{AHO`{AILb}}{AHO`{CGb{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjBHLn}}}}\",[]]El[\"{{{AHO`{NMf}}{AHO`{CGb{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjBHLn}}}}\",[]]Em[\"{{{AHO`{AJAb}}{AHO`{CGb{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjBHLn}}}}\",[]]El[\"{{{AHO`{NMh}}{AHO`{CGb{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjBHM`}}}}\",[]]Em[\"{{{AHO`{AJAd}}{AHO`{CGb{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjBHM`}}}}\",[]]Am[\"{{{AHO`{CGbAHB`}}}ICj}\",[]]0Am[\"{{{AHO`{AILd}}A@l}CNb}\",[]]Ak[\"{{{AHO`{AILb}}}BGIb}\",[]]Bg[\"{{{AHO`{DA`}}}{{KGd{BEFbAIKd}}}}\",[]]>=E`[\"{{{AHO`{BHEb}}{AHO`{{K@j{Oh}}}}{AHO`{BBLb}}{AHO`{BGOn}}}{{KGd{BHGhIEl}}}}\",[]]0Bg[\"{{{A@b{BBCb}}}{{KGd{B@KhBGIn}}}}\",[]]Bg[\"{{{A@b{BBCb}}}{{KGd{B@KlBGIn}}}}\",[]]Bn[\"{{{AHO`{CGbBFGh}}KI`}{{AHO`{CGbBFGh}}}}\",[]]Bn[\"{{{AHO`{CGbBFGj}}KIb}{{AHO`{CGbBFGj}}}}\",[]]o[\"{NIlNIl}\",[]]0000Aj[\"{{{AHO`{AILd}}}DC`}\",[]]0Bd[\"{{{AHO`{CGbAILf}}CNbAB@d}ICj}\",[]]Bc[\"{{{AHO`{CGbCNj}}CNbAB@d}ICj}\",[]]Bd[\"{{{AHO`{CGbAILh}}CNbAB@d}ICj}\",[]]Bc[\"{{{AHO`{CGbCNl}}CNbAB@d}ICj}\",[]]Ci[\"{{{AHO`{DA`}}{KFl{AB@d}}}{{KGd{{KFl{AB@d}}AIKd}}}}\",[]]Cd[\"{{{AHO`{CGbAAj}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Cd[\"{{{AHO`{CGbA@h}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Ce[\"{{{AHO`{CGbAAOf}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Ce[\"{{{AHO`{CGbAHAl}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Cd[\"{{{AHO`{CGbAAl}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Cd[\"{{{AHO`{CGbAAn}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Bo[\"{{{AHO`{CGbBABd}}BH@l}{{AHO`{CGbBABd}}}}\",[]]Bo[\"{{{AHO`{CGbBABh}}BH@n}{{AHO`{CGbBABh}}}}\",[]]Aj[\"{{{AHO`{BBMh}}}CNb}\",[]]Aj[\"{{{AHO`{BBMj}}}CNb}\",[]]Cd[\"{{{AHO`{CGbB@Kh}}{A@b{A`}}}{{AHO`{CGbB@Kh}}}}\",[]]Cd[\"{{{AHO`{CGbB@Kl}}{A@b{A`}}}{{AHO`{CGbB@Kl}}}}\",[]]Cl[\"{{BBMh{KFl{BABd}}{A@d{BBL`}}{A@b{NMd}}}{{KGf{AB@h}}}}\",[]]Aj[\"{{{AHO`{ABA`}}}DC`}\",[]]Aj[\"{{{AHO`{ABAb}}}DC`}\",[]]Ca[\"{{{AHO`{CGbAILd}}NK`AIMn}{{KGd{ICjBHDj}}}}\",[]]Bd[\"{{{AHO`{BHEb}}}{{ABh{BCOb}}}}\",[]]Bj[\"{{{AHO`{AILb}}AJ@f}{{KGd{ICjIFh}}}}\",[]]Bm[\"{{{AHO`{CGbAILd}}AJ@f}{{KGd{ICjIFh}}}}\",[]]Bn[\"{{{AHO`{CGbBCOl}}KCb}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}KCb}{{AHO`{CGbBCOn}}}}\",[]]Bm[\"{{}{{KGd{{KC`{BBLbBFJb}}{A@d{IEd}}}}}}\",[]]Ai[\"{AIL`{{KGh{ICj}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]An[\"{{{AHO`{AB@h}}B@Kh}ICj}\",[]]Dc[\"{{{AHO`{CGbAILd}}NK`KG`{KFl{AB@d}}}{{KGd{{KFl{AB@d}}AIKj}}}}\",[]]o[\"{{}AHO`}\",[]]Aj[\"{{{AHO`{AILb}}}KCb}\",[]]Bn[\"{{{AHO`{CGbBABd}}CNb}{{AHO`{CGbBABd}}}}\",[]]Bn[\"{{{AHO`{CGbBABh}}CNb}{{AHO`{CGbBABh}}}}\",[]]Bn[\"{{{AHO`{CGbBCOl}}KI`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}KIb}{{AHO`{CGbBCOn}}}}\",[]]:Bc[\"{{{AHO`{CGbAILd}}A@lKIb}ICj}\",[]]A`[\"{AHElCNb}\",[]]A`[\"{AHEnCNb}\",[]]Ak[\"{{}{{KGd{B@KhIFb}}}}\",[]]Ak[\"{{}{{KGd{B@KlIFd}}}}\",[]]Bn[\"{{{AHO`{CGbBCOl}}DC`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}DC`}{{AHO`{CGbBCOn}}}}\",[]]87Cg[\"{{{AHO`{CGbAAj}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]Cg[\"{{{AHO`{CGbA@h}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]Ch[\"{{{AHO`{CGbAAOf}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]Ch[\"{{{AHO`{CGbAHAl}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]Cg[\"{{{AHO`{CGbAAl}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]Cg[\"{{{AHO`{CGbAAn}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]n[\"{{}ICj}\",[]]Ce[\"{{{AHO`{CGbBCOl}}{KFl{KCb}}}{{AHO`{CGbBCOl}}}}\",[]]Ce[\"{{{AHO`{CGbBCOn}}{KFl{KCb}}}{{AHO`{CGbBCOn}}}}\",[]]Bn[\"{{{AHO`{AILb}}}{{KGd{{ABh{AJ@f}}IFh}}}}\",[]]Cd[\"{{{AHO`{CGbAILd}}NK`}{{KGd{{ABh{AJ@f}}IFh}}}}\",[]]Am[\"{{{AHO`{CGbAILf}}}ICj}\",[]]Al[\"{{{AHO`{CGbCNj}}}ICj}\",[]]Al[\"{{{AHO`{CGbIDh}}}ICj}\",[]]Am[\"{{{AHO`{CGbAILh}}}ICj}\",[]]Al[\"{{{AHO`{CGbCNl}}}ICj}\",[]]Al[\"{{{AHO`{CGbIDj}}}ICj}\",[]]Ai[\"{AIL`{{KGh{ICj}}}}\",[]]Bj[\"{{{AHO`{AILb}}AJ@f}{{KGd{ICjIFh}}}}\",[]]Bm[\"{{{AHO`{CGbAILd}}AJ@f}{{KGd{ICjIFh}}}}\",[]]D`[\"{{{AHO`{CGbAILd}}KG`{KFl{AB@d}}}{{KGd{{KFl{AB@d}}AIKj}}}}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{A@b{BFBf}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{A@b{BFBh}}}{{AHO`{CGbBCOn}}}}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{KFl{AB@d}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{KFl{AB@d}}}{{AHO`{CGbBCOn}}}}\",[]]Ce[\"{{{AHO`{CGbBCOl}}{KFl{CNb}}}{{AHO`{CGbBCOl}}}}\",[]]Ce[\"{{{AHO`{CGbBCOn}}{KFl{CNb}}}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{BAOjKFl}\",[]]A`[\"{BAOlKFl}\",[]];Bn[\"{{{AHO`{CGbBCOl}}DC`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}DC`}{{AHO`{CGbBCOn}}}}\",[]]Am[\"{{{AHO`{AILb}}KI`}ICj}\",[]]o[\"{NIlNIl}\",[]]Bn[\"{{{AHO`{CGbBCOl}}CN`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}CN`}{{AHO`{CGbBCOn}}}}\",[]]310;:54Bn[\"{{{AHO`{AILb}}}{{KGd{{ABh{AJ@f}}IFh}}}}\",[]]Bn[\"{{{AHO`{AILd}}}{{KGd{{ABh{AJ@f}}IFh}}}}\",[]]10Bm[\"{{{AHO`{CGbBCOl}}Oh}{{AHO`{CGbBCOl}}}}\",[]]Bm[\"{{{AHO`{CGbBCOn}}Oh}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{BGAnKFl}\",[]]") \ No newline at end of file diff --git a/pr/672/docs/search.index/generic_inverted_index/11aba052bb82.js b/pr/672/docs/search.index/generic_inverted_index/11aba052bb82.js new file mode 100644 index 000000000..850cd1cac --- /dev/null +++ b/pr/672/docs/search.index/generic_inverted_index/11aba052bb82.js @@ -0,0 +1 @@ +rb_("QU9gADswAAABAADuAAIAbQPtAFYMAAA7MAAAAQAAawIrACQCGAA+AgwATAIAAE4CAABRAgEAVAICAFoCAABcAgcAaQIDAG4CCAB4AgAAewIGAIMCBACKAgEAjQIAAI8CAwCUAgMAmQICAJ0CDQC0AgAAtwIWANACAADUAgIA2AIRAOsCBADxAgMA9gIEAP0CBQAEAwUACwMFABIDBQAZAwAAGwMFACIDAQAlAwEAKAMHADgDAAA8AwsASQMNAHUJjAACC+0AFRAAAMIQAQA7MAAAAQAA3gEOAJcAAgDNBe0AAgo1ADsKFABRCgQAVwodAHcKAAB6ChQAkQoAAJMKHgCzCgUAugoqAOcKAADqChcAOzAAAAEAACMBCwBtAgAA1wIAAEgDAACEBxMAsgoAALkKAADlCgAA6QoAAJ0OBAEcEAEAUxABADswAAABAACFAQcAYQQAAEAJAQDmCgAA6AoAABgN7QAIDowA1xEFADswAAABAAAGAAIA1gYDANsGAgA7MAAAAQAALAAHAFcDFQCbBQAA0QYEAN8GDAA7CQEAZwwAAPgSAAA7MAAAAQAACgACAH4EAADwCwkAOzAAAAEAAAwAAQBtBAwAOzAAAAEAAAUAAgBsDAMAWBEBAALEEQAAxREAAAAAAALRAQAA2gYAAENuAAAAOzAAAAEAAO0ADAACCjUAOwoUAFEKBABXCh0AdwoAAHoKFACRCgAAkwoeALMKBQC6CioA5woAAOoKFwA=") \ No newline at end of file diff --git a/pr/672/docs/search.index/name/7f055d9d7409.js b/pr/672/docs/search.index/name/7f055d9d7409.js new file mode 100644 index 000000000..b6de69805 --- /dev/null +++ b/pr/672/docs/search.index/name/7f055d9d7409.js @@ -0,0 +1 @@ +rd_("b()b->bBi0bCe00bFnbIdbOkb[]beq000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bid000000000000000000brtbtybu8cAck0cAddcAnycArccBoxcBufcClicDir0cErrcMAX000cNotcOpt00cOrdcPincUni0cVeccadd00000ccmp0000000000000000000cdir000cecn000cf32cf64cfmt00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cget0ci32cimpcios0clen000clog0cmincmutcnew00000000000000000000000000000000000cnoqcnot0cnow000000crtt000crun0cstrcu16cu32cu64cudpdArgsdAsFddBbr300dDropdEct000dEct100dFromdHashdIntodKeys0dLong0dNonedPath00dPing0dPolldReaddSide0dZERO0dacks0daddr00dbbr3dbooldbulkdcodedcwnd0ddataddrop000000dfrom000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dhash000000000000000000000dinto000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dmain0000dnext0dopen00dpath0dperfdping000dpoll00000000000000dqlogdread00drecv00dseal0dsend00dsent0dside0dsign0dsize00dsmoldspindstop00dtake00000du128duniteAsRefeByteseChunk0eCloneeCloseeCubic00eDebugeDerefeError00000000000eEvent00eKnowneOther0eRangeeReset000eRetry0eShort0eSizedeStats0eSuiteeTokioeWriteealpns0earrayebenchebuild00000000ebytes000ecauseeclone00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eclose000ecubicederef0eerroreframe0eindex0eis_ce0elocal0eparse0epaths0eprint00ereset00eretry0esliceespawn00estats0000000000etitle0etokenetokioetupleeusizeewrite00fAcceptfAckEcn0fAes128fAes256fBackup0fBorrowfBufMutfChunksfClient00fClosed0fCrypto0fCursorfFuturefHasherfIpAddrfLaggedfOpenBifOpened0fOptionfPathId0fResult00000fServer00fStream000fStringfTypeIdfVarInt0faccept000fborrow000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fcipherfclient0000fclosedfcoding0fconfig0fcrypto0000000000fdecode000000000000fdst_ipfencode000000000000ffinish00fhandlefheader0fignore0finsert00000fkeylogfoffset0fon_ack0000000fpacket00frandomfreason00000000frebindfrefuse0fremote000frustls00000fserver0000fsharedfsource00000000fsrc_ip00fstatus00fstream0fstridefudp_rx000fudp_tx000fvarintfverify0fwindow0000000gBlocked00gCommandgContextgDefaultgDisplaygFromStrgGeneral0gHmacKey0gInitialgInstantgKeyPair0gLayeredgMaxData0gNewReno00gOpenUnigPadding0gPathAck0gPathBufgReadBufgRequestgRuntime0gSession0gStopped00000gStreamsgToOwnedgTooLonggTryFromgTryIntogUnknowngZeroRttgcid_len000gclientsgcommand00000gcompare0000000000000000000gconnect0gdecrypt00000gdefault000000000000000000000000000000000000000000000000000gdrainedgdst_cid0000000gencrypt000gis_0rtt0gmetrics0000000gmin_mtu0gnoq_udpgon_sent0gopen_bigpath_idgpointergruntimegsrc_cid0000gstopped0gstreams000gtag_len0gtype_id000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000gupgrade0gversion00000hAcceptBihBytesMuthChacha20hCommandshDatagram00hDerefMuthDisabled0hDurationhEndpoint00hFinished0hIncoming0hIteratorhLayerExthLongTypehMAX_SIZE0hNO_ERROR0hNewToken0hOnClosedhOpenPathhOrderinghReadable0hRecvMetahResponsehStreamId0hTimedOut00hToStringhTokenLog0hTooLarge0hTransmit00hUdpStats0hUnixTimehWritable0hcong_alghcontentshdatagram0hdurationhendpoint0hfinalizehframe_rx000hframe_tx000hfrom_buf0hfrom_env0hfrom_keyhfrom_str0hfrom_u320hfrom_u640hgot_type000hgroup_id0000hhas_0rtthincominghinterval0hinto_any0000000his_alivehlifetime0hlocal_ip00000hmax_data0hmax_sizehnew_renohnoq_perfhon_byteshopen_unihpriority0hprotocol0hqlog_dirhresponsehrng_seed0hssthresh0hto_owned00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_from000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_into000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_poll000000000htry_sendhvalidate000iAbandoned000iAcceptUniiAddAssigniArbitraryiAsyncRead0iAvailable000iBorrowMutiCommonOptiConnectediCriterioniDatagramsiDecodableiDiscarded000iEncodableiFormatteriFourTuple0iFrameType0iHandshakeiHeaderKey0iHistogramiMalformediMaxPathId0iMultipathiPacketKey0iPartialEqiPathError0iPathEvent000iPathStats0iReadError00iTryFutureiTryStreamiUdpSenderiUdpSocketiValueEnumiaccept_biiarbitrary0000000ias_rustlsiassemblericlone_box0000000idatagrams000ideref_mut0ifrom_bits00iinitiator0iinto_0rttiinto_iteriis_client0iis_closediis_server0imay_retry0imigration0inew_timer00inew_token0inoq_protoion_closediopen_path0ipath_acks0iping_pathipoll_next000ipoll_read00ipoll_recvipoll_sendireach_out0ireferenceireset_key0iretry_tag0000isize_hint000istable_idito_string0000000000000000000000000000000000000000000000000000000000000000000itoken_key0itoken_positransport00iwait_idleiwrite_alljArgMatchesjAsyncTimerjAsyncWrite0jBATCH_SIZEjBbr3Config0jClosedPath00000jComparablejConnectingjConnection00jController0jEquivalent0jFrameStats0jIntoFuturejInvalidCid0jIoSliceMutjMaybeFrame0jNonZeroU32jOtherErrorjPartialOrdjPathAckEcn0jPathEventsjPathStatus0jQlogConfig0jRecvStream00jResetErrorjRetryError0jSendStream00jServerNamejSocketAddrjStreamInfojSubcommandjSystemTimejTimeSource0jTlsSession0jTokenStore0jUdpSockRefjWriteError0jaccept_unijadd_assign00000jallow_spin0jborrow_mut000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jbytes_read0jclone_into00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jclose_pathjcongestion000jconn_statsjconnection0jequivalent00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jerror_code000000jframe_type0jinto_inner0jis_drained0jlocal_addr0jlost_bytes000jnew_senderjpad_to_mtu0jpath_stats0000jpoll_closejpoll_flush0jpoll_write00jprint_jsonjread_chunk0jread_exactjset_statusjstart_time0jthroughputjtotal_sizekAcceptErrorkCLIENT_ADDRkCipherSuitekClientStatskCloseReasonkConfigError0kContentTypekCryptoError0kCubicConfig0kDataBlocked0kEstablished000kFinishErrorkIdleTimeout0kIntoRequestkOutOfBounds0kPathAbandon0kQlogFactory0kResetStream0kRuntimeTypekSERVER_ADDRkSERVER_NAMEkSmolRuntimekStopSending0kStreamEvent0kStreamStats0kaccept_withkadd_address0kbind_socketkcertificatekcurrent_mtu00kdescription0kdestination00kfrom_remote0kinitial_mtu000kinitial_rtt00kinto_future0000000000000kis_drainingkmax_path_id0kmax_streamsknamed_layer0kon_end_acks000kon_intervalkpacing_rate0kpartial_cmp0000000000000000000kpath_eventskpath_statuskread_to_endkrecv_stream0ksample_size0ksend_bufferksend_stream0ksend_window00kserver_name0ktime_source0ktoken_store0kupload_sizekupper_bound0kweak_handle0kwith_crypto0kwith_prefix0kwrite_chunklAckFrequency0lAddressAddedlClientConfig00000lClosedStream000000lConnectError00lConnectionId00lDatagramInfolDecryptError0lEcnCodepoint00lEncryptError0lIllegalValuelImmediateAck0lIntoIteratorlLOCALHOST_V4lLastOpenPath0lNatTraversallNonZeroUsizelNoneTokenLog0lObservedAddr000lPathResponse0lPathsBlocked0lReadDatagramlRemoteStatus000lRttEstimator0lSendDatagramlServerConfig00000lSocketAddrV4lSocketAddrV6lStoppedErrorlTokioRuntimelUnstructuredlaugment_args0000lcid_lifetime000lclose_reasonlconnect_withlconservativeldata_blocked0ldrain_streamlearly_crypto000levent_streamlexpect_types000lgenerate_cid000lgro_segmentslhandle_event0linitial_keys00000000linto_request0llost_packets000lmax_incoming0lmay_fragment00lnetwork_path0lnew_receiverlnoprotection0lorig_dst_cid0lpath_abandon0lpoll_timeoutlqlog_factory0lreset_stream0lruntime_typelsegment_size00lsend_quantum0lsend_streamslset_lifetime0lset_priority0lstop_sending0lstream_statslupload_statslwith_initial000lwrite_chunksmAlertReceived0mBloomTokenLog0mCidsExhausted000mCloneToUninitmDatagramEventmEndpointEventmEndpointStatsmFinishedEarlymHandshakeData0mHandshakeDone0mHandshakeTypemINVALID_TOKEN0mIncomingAlpns0mInvalidHeadermLocallyClosed0mMaxStreamData0mMaxStreamsUni0mNewConnectionmNewRenoConfig0mParseIntErrormPartialDecodemPathChallenge0mPossibleValuemPrivateKeyDermReadableErrormRemoveAddress0mRootCertStoremStdSystemTime0mUnexpectedEndmaccepted_0rttmack_frequency00mbench_exports0mcid_generator00mcreate_sendermdata_transfermdownload_sizemduration_histmimmediate_ack0minto_incoming0mmax_ack_delay0mno_protectionmobserved_addr0mon_first_bytemon_mtu_update0000000mpath_response0mpaths_blocked0mpeer_identity0000mpoll_shutdownmpoll_transmitmqlog_from_env0mread_datagrammsend_datagrammsend_fairness0msignature_len0mstart_session000000000mtry_poll_next0nAddIpv4Address0nAddIpv6Address0nAddressRemovednAsyncUdpSocketnCertificateDernClosePathError0nCommandFactorynConnectionLost000000nEndpointConfig0nFromArgMatchesnINTERNAL_ERROR0nIncomingFuturenInvalidFrameIdnInvalidMessage00nMaxStreamsBidi0nNO_VIABLE_PATH0nNoneTokenStore0nPeerMisbehaved00nReachOutAtIpv40nReachOutAtIpv60nReadExactErrornReadToEndErrornShouldTransmitnTransferResultnTransportError00nUdpSocketStatenWeakPathHandlenconnect_clientncrypto_sessionndownload_statsnfor_connection000nhandle_timeoutnhandshake_data00000nhandshake_done0nhas_subcommandninitial_window0000000000000ninto_unorderednis_handshaking0000nis_valid_retry000nminimum_change0nnext_1rtt_keys000non_packet_lost000non_packet_sent000npath_challenge0nqlog_from_path0nread_handshake000nread_unorderednreceive_window00nreceived_reset00nremote_address000nremove_address0nsaturating_add000nsaturating_sub0nthroughput_bpsntime_threshold0ntotal_durationnvalue_variantsoConnectionClose000oConnectionError0oConnectionEvent0oConnectionStats0oGetRandomFailedoNewConnectionId0oOpenStreamStatsoPathCidsBlocked0oProtectedHeader0oQlogFileFactory0oRemoteAbandoned0oSignatureSchemeoSystemTimeErroroTokenReuseError0oTransportConfig00oTryFromIntErroroVersionMismatch0oZeroRttAcceptedoZeroRttRejected000obloom_token_logoclone_to_uninit00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000odefault_runtimeogrease_quic_bit0ointegrity_limit0ointerface_indexomax_stream_data0omax_streams_uni0oparse_byte_sizeorebind_abstractosend_unreliableoserver_endpointoset_path_statusoset_send_window0oshould_transmitostream_finishedothroughput_histowrap_udp_socket00owrite_handshake000A`AddressDiscoveryA`ApplicationClose000A`CertificateErrorA`ConnectionClosed0A`ConnectionHandleA`DatagramReceivedA`DecryptedInitial0A`EndpointStopping0A`FINAL_SIZE_ERROR0A`InconsistentKeys00A`KEY_UPDATE_ERROR0A`MaxPathIdReached0A`ObservedIpv4Addr0A`ObservedIpv6Addr0A`PathStatusBackup0A`PeerIncompatible00A`QuicClientConfig0A`QuicServerConfig0A`TokenMemoryCache0A`TooManyAddressesA`ValidationFailed00A`VersionNegotiate0A`check_and_insert00000A`configure_clientA`congestion_state0A`connection_close0A`force_key_update0A`from_arg_matches00000A`max_gso_segmentsA`max_idle_timeout0A`max_streams_bidi0A`n0_nat_traversal0A`open_connections0A`open_path_ensure0A`packet_threshold0A`read_many_chunksA`receive_datagramA`recv_buffer_size0A`send_buffer_size0A`transport_config0000A`validation_token0A`with_single_cert0A`write_all_chunksAaAPPLICATION_ERROR0AaApplicationClosed000AaControllerFactory0AaControllerMetrics0AaHandshakeTokenKey0AaInvalidServerName0AaNetworkChangeHint0AaPacketDecodeError0AaPathAbandonReason00AaSendDatagramError0AaStreamDataBlocked0AaStreamsBlockedUni0AaUnsupportedByPeer0Aacongestion_events0Aacongestion_window0Aamax_datagram_sizeAanew_connection_id0Aapath_cids_blocked0Aasend_buffer_spaceAaset_server_config0Aaset_up_connectionAato_possible_valueAawait_all_drainingAawrite_many_chunksAbAEAD_LIMIT_REACHED0AbAckFrequencyConfig0AbBadMaxFragmentSize0AbCONNECTION_REFUSED0AbConnectionIdParserAbDatagramsUnblockedAbFLOW_CONTROL_ERROR0AbHandshakeConfirmedAbHandshakeDataReadyAbIllegalOrderedReadAbInvalidCertificate0AbMtuDiscoveryConfig0AbNotEnoughAddressesAbPERF_CIPHER_SUITESAbPROTOCOL_VIOLATION0AbPrivatePkcs8KeyDerAbRetireConnectionId0AbSTREAM_LIMIT_ERROR0AbSTREAM_STATE_ERROR0AbServerCertVerifiedAbServerCertVerifierAbSetPathStatusError0AbStreamsBlockedBidi0AbTokioCurrentThreadAbTransportErrorCode0AbUnsupportedVersion00000Abcommand_for_update0000Abcrypto_buffer_size0Abfrom_u64_unchecked0Abignored_handshakesAbis_same_connectionAbmin_reset_interval0Abnew_expected_items0Abpath_status_backup0Abrefused_handshakesAbremote_path_statusAbsend_datagram_waitAbset_alpn_protocols000Abset_receive_window0Absupported_versions0Abtoken_memory_cacheAbverify_server_certAbwith_start_instant0AcCongestionAlgorithmAcNatTraversalUpdatesAcPathNewConnectionId0AcPathStatusAvailable0AcRemoteCidsExhausted0AcTransportParametersAcUnorderedRecvStreamAcUnsupportedNameType0AcWrongConnectionSideAcaccepted_handshakesAcarbitrary_take_rest000Acaugment_subcommandsAcblack_hole_cooldown0Acearly_data_accepted000Achandshake_confirmedAcis_path_recoverable0Ackeep_alive_interval0Aclost_plpmtud_probes0Acon_congestion_event0000000Acoutgoing_handshakesAcremote_open_streamsAcsend_buffer_benches00Acsend_data_on_streamAcsent_plpmtud_probes0Acstream_data_blocked0Acstreams_blocked_uni0AdFRAME_ENCODING_ERROR0AdHandshakeNotComplete0AdInappropriateMessage000AdInvalidRemoteAddress000AdNoInitialCipherSuite0AdObservedExternalAddrAdServerSideNotAllowed0AdSupportedCipherSuiteAdVarIntBoundsExceeded0AdVerifierBuilderErrorAdWeakConnectionHandleAdack_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_limit0Aehandle_network_change0Aeincoming_buffer_bytesAeloss_reduction_factor0Aemax_transmit_segments0Aenat_traversal_updatesAepath_observed_addressAepath_status_available0Aestream_receive_window00AfCRYPTO_BUFFER_EXCEEDED0AfExtensionNotNegotiatedAfFailedToGetCurrentTime0AfFailedToGetRandomBytes0AfMultipathNotNegotiated000000AfPathRetireConnectionId0AfProtectedInitialHeaderAfSkipServerVerificationAfbuild_transport_configAfexport_keying_material0000Afget_into_many_segmentsAfget_loop_many_segmentsAfmax_concurrent_streamsAfobserved_external_addr0Afpath_new_connection_id0Afverify_tls12_signatureAfverify_tls13_signatureAfwith_root_certificates0AgCertRevocationListErrorAgHandshakeSignatureValidAgNoCertificatesPresented0AgPeerSentOversizedRecord0Agack_eliciting_threshold0Agaugment_args_for_update0000Aghas_pending_retransmitsAgis_multipath_negotiatedAgon_ack_frequency_update000Agset_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_VERSIONSAjUnusableAfterNetworkChangeAjdatagram_send_buffer_spaceAjincoming_buffer_size_total0Ajmax_concurrent_uni_streams0Ajopen_unidirectional_streamAjset_max_concurrent_streamsAjspurious_congestion_events0Ajtry_with_platform_verifier0AkHashedConnectionIdGeneratorAkInvalidEncryptedClientHello0AkPATH_RESOURCE_LIMIT_REACHED0AkRandomConnectionIdGeneratorAkenable_segmentation_offload0Akmax_concurrent_bidi_streams0Akupdate_from_arg_matches_mut00000Alconfigure_tracing_subscriberAldatagram_receive_buffer_size0Alinitiate_nat_traversal_round0Alon_spurious_congestion_event00000Alreceive_bidirectional_streamAlremove_nat_traversal_address0Alset_path_keep_alive_intervalAmFixedLengthConnectionIdParserAmInappropriateHandshakeMessage000Amcongestion_controller_factory0Amdefault_path_max_idle_timeout0Ammax_outgoing_bytes_per_second0Amnegotiated_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_addresses00") \ No newline at end of file diff --git a/pr/672/docs/search.index/normalizedName/c413c0680ead.js b/pr/672/docs/search.index/normalizedName/c413c0680ead.js new file mode 100644 index 000000000..02af014dd --- /dev/null +++ b/pr/672/docs/search.index/normalizedName/c413c0680ead.js @@ -0,0 +1 @@ +rd_("b()b->bbi0bce00bfnbidbokb[]beq0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003333333333333333333brtbtybu8cack0caddcanycarccboxcbufcclicdir0cerrcmax000cnotcopt00cordcpincuni0cvec>>>>>>ccmp00000000000000000009999cecn000cf32cf64cfmt00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cget0ci32cimpcios0clen000clog0cmincmutcnew00000000000000000000000000000000000cnoqcnot0cnow000000crtt000crun0cstrcu16cu32cu64cudpdargsdasfddbbr300ddropdect000dect100dfromdhashdintodkeys0dlong0dnonedpath00dping0dpolldreaddside0dzero0dacks0daddr00dbbr3dbooldbulkdcodedcwnd0ddataddrop000000dfrom000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dhash000000000000000000000dinto000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dmain0000dnext0dopen00dpath0dperfdping000dpoll00000000000000dqlogdread00drecv00dseal0dsend00dsent0dside0dsign0dsize00dsmoldspindstop00dtake00000du128duniteasrefebytesechunk0ecloneecloseecubic00edebugederefeerror00000000000eevent00eknowneother0erangeereset000eretry0eshort0esizedestats0esuiteetokioewriteealpns0earrayebenchebuild00000000ebytes000ecauseeclone00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eclose000ecubicederef0eerroreframe0eindex0disce0elocal0eparse0epaths0eprint00ereset00eretry0esliceespawn00estats0000000000etitle0etokenetokioetupleeusizeewrite00facceptfackecn0faes128faes256fbackup0fborrowfbufmutfchunksfclient00fclosed0fcrypto0fcursorffuturefhasherfipaddrflaggedfopenbifopened0foptionfpathid0fresult00000fserver00fstream000fstringftypeidfvarint0faccept000fborrow000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fcipherfclient0000fclosedfcoding0fconfig0fcrypto0000000000fdecode000000000000edstipfencode000000000000ffinish00fhandlefheader0fignore0finsert00000fkeylogfoffset0eonack0000000fpacket00frandomfreason00000000frebindfrefuse0fremote000frustls00000fserver0000fsharedfsource00000000esrcip00fstatus00fstream0fstrideeudprx000eudptx000fvarintfverify0fwindow0000000gblocked00gcommandgcontextgdefaultgdisplaygfromstrggeneral0ghmackey0ginitialginstantgkeypair0glayeredgmaxdata0gnewreno00gopenunigpadding0gpathack0gpathbufgreadbufgrequestgruntime0gsession0gstopped00000gstreamsgtoownedgtoolonggtryfromgtryintogunknowngzerorttfcidlen000gclientsgcommand00000gcompare0000000000000000000gconnect0gdecrypt00000gdefault000000000000000000000000000000000000000000000000000gdrainedfdstcid0000000gencrypt000fis0rtt0gmetrics0000000fminmtu0fnoqudpfonsent0fopenbifpathidgpointergruntimefsrccid0000gstopped0gstreams000ftaglen0ftypeid000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000gupgrade0gversion00000hacceptbihbytesmuthchacha20hcommandshdatagram00hderefmuthdisabled0hdurationhendpoint00hfinished0hincoming0hiteratorhlayerexthlongtypegmaxsize0gnoerror0hnewtoken0honclosedhopenpathhorderinghreadable0hrecvmetahresponsehstreamid0htimedout00htostringhtokenlog0htoolarge0htransmit00hudpstats0hunixtimehwritable0gcongalghcontentshdatagram0hdurationhendpoint0hfinalizegframerx000gframetx000gfrombuf0gfromenv0gfromkeygfromstr0gfromu320gfromu640ggottype000ggroupid0000ghas0rtthincominghinterval0gintoany0000000gisalivehlifetime0glocalip00000gmaxdata0gmaxsizegnewrenognoqperfgonbytesgopenunihpriority0hprotocol0gqlogdirhresponsegrngseed0hssthresh0gtoowned00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000gtryfrom000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000gtryinto000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000gtrypoll000000000gtrysendhvalidate000iabandoned000iacceptuniiaddassigniarbitraryiasyncread0iavailable000iborrowmuticommonopticonnectedicriterionidatagramsidecodableidiscarded000iencodableiformatterifourtuple0iframetype0ihandshakeiheaderkey0ihistogramimalformedimaxpathid0imultipathipacketkey0ipartialeqipatherror0ipathevent000ipathstats0ireaderror00itryfutureitrystreamiudpsenderiudpsocketivalueenumhacceptbiiarbitrary0000000hasrustlsiassemblerhclonebox0000000idatagrams000hderefmut0hfrombits00iinitiator0hinto0rtthintoiterhisclient0hisclosedhisserver0hmayretry0imigration0hnewtimer00hnewtoken0hnoqprotohonclosedhopenpath0hpathacks0hpingpathhpollnext000hpollread00hpollrecvhpollsendhreachout0ireferencehresetkey0hretrytag0000hsizehint000hstableidhtostring0000000000000000000000000000000000000000000000000000000000000000000htokenkey0htokenpositransport00hwaitidlehwritealljargmatchesjasynctimerjasyncwrite0ibatchsizejbbr3config0jclosedpath00000jcomparablejconnectingjconnection00jcontroller0jequivalent0jframestats0jintofuturejinvalidcid0jioslicemutjmaybeframe0jnonzerou32jothererrorjpartialordjpathackecn0jpatheventsjpathstatus0jqlogconfig0jrecvstream00jreseterrorjretryerror0jsendstream00jservernamejsocketaddrjstreaminfojsubcommandjsystemtimejtimesource0jtlssession0jtokenstore0judpsockrefjwriteerror0iacceptuniiaddassign00000iallowspin0iborrowmut000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ibytesread0icloneinto00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000iclosepathjcongestion000iconnstatsjconnection0jequivalent00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ierrorcode000000iframetype0iintoinner0iisdrained0ilocaladdr0ilostbytes000inewsenderhpadtomtu0ipathstats0000ipollcloseipollflush0ipollwrite00iprintjsonireadchunk0ireadexactisetstatusistarttime0jthroughputitotalsizekaccepterrorjclientaddrkciphersuitekclientstatskclosereasonkconfigerror0kcontenttypekcryptoerror0kcubicconfig0kdatablocked0kestablished000kfinisherrorkidletimeout0kintorequestkoutofbounds0kpathabandon0kqlogfactory0kresetstream0kruntimetypejserveraddrjservernameksmolruntimekstopsending0kstreamevent0kstreamstats0jacceptwithjaddaddress0jbindsocketkcertificatejcurrentmtu00kdescription0kdestination00jfromremote0jinitialmtu000jinitialrtt00jintofuture0000000000000jisdrainingimaxpathid0jmaxstreamsjnamedlayer0ionendacks000jonintervaljpacingrate0jpartialcmp0000000000000000000jpatheventsjpathstatusireadtoendjrecvstream0jsamplesize0jsendbufferjsendstream0jsendwindow00jservername0jtimesource0jtokenstore0juploadsizejupperbound0jweakhandle0jwithcrypto0jwithprefix0jwritechunklackfrequency0laddressaddedlclientconfig00000lclosedstream000000lconnecterror00lconnectionid00ldatagraminfoldecrypterror0lecncodepoint00lencrypterror0lillegalvaluelimmediateack0lintoiteratorklocalhostv4llastopenpath0lnattraversallnonzerousizelnonetokenlog0lobservedaddr000lpathresponse0lpathsblocked0lreaddatagramlremotestatus000lrttestimator0lsenddatagramlserverconfig00000lsocketaddrv4lsocketaddrv6lstoppederrorltokioruntimelunstructuredkaugmentargs0000kcidlifetime000kclosereasonkconnectwithlconservativekdatablocked0kdrainstreamkearlycrypto000keventstreamkexpecttypes000kgeneratecid000kgrosegmentskhandleevent0kinitialkeys00000000kintorequest0klostpackets000kmaxincoming0kmayfragment00knetworkpath0knewreceiverlnoprotection0jorigdstcid0kpathabandon0kpolltimeoutkqlogfactory0kresetstream0kruntimetypeksegmentsize00ksendquantum0ksendstreamsksetlifetime0ksetpriority0kstopsending0kstreamstatskuploadstatskwithinitial000kwritechunksmalertreceived0mbloomtokenlog0mcidsexhausted000mclonetouninitmdatagrameventmendpointeventmendpointstatsmfinishedearlymhandshakedata0mhandshakedone0mhandshaketypelinvalidtoken0mincomingalpns0minvalidheadermlocallyclosed0mmaxstreamdata0mmaxstreamsuni0mnewconnectionmnewrenoconfig0mparseinterrormpartialdecodempathchallenge0mpossiblevaluemprivatekeydermreadableerrormremoveaddress0mrootcertstoremstdsystemtime0munexpectedendlaccepted0rttlackfrequency00lbenchexports0lcidgenerator00lcreatesenderldatatransferldownloadsizeldurationhistlimmediateack0lintoincoming0kmaxackdelay0lnoprotectionlobservedaddr0konfirstbytekonmtuupdate0000000lpathresponse0lpathsblocked0lpeeridentity0000lpollshutdownlpolltransmitkqlogfromenv0lreaddatagramlsenddatagramlsendfairness0lsignaturelen0lstartsession000000000ktrypollnext0naddipv4address0naddipv6address0naddressremovednasyncudpsocketncertificatedernclosepatherror0ncommandfactorynconnectionlost000000nendpointconfig0nfromargmatchesminternalerror0nincomingfutureninvalidframeidninvalidmessage00nmaxstreamsbidi0lnoviablepath0nnonetokenstore0npeermisbehaved00nreachoutatipv40nreachoutatipv60nreadexacterrornreadtoenderrornshouldtransmitntransferresultntransporterror00nudpsocketstatenweakpathhandlemconnectclientmcryptosessionmdownloadstatsmforconnection000mhandletimeoutmhandshakedata00000mhandshakedone0mhassubcommandminitialwindow0000000000000mintounorderedmishandshaking0000lisvalidretry000mminimumchange0lnext1rttkeys000lonpacketlost000lonpacketsent000mpathchallenge0lqlogfrompath0mreadhandshake000mreadunorderedmreceivewindow00mreceivedreset00mremoteaddress000mremoveaddress0msaturatingadd000msaturatingsub0mthroughputbpsmtimethreshold0mtotaldurationmvaluevariantsoconnectionclose000oconnectionerror0oconnectionevent0oconnectionstats0ogetrandomfailedonewconnectionid0oopenstreamstatsopathcidsblocked0oprotectedheader0oqlogfilefactory0oremoteabandoned0osignatureschemeosystemtimeerrorotokenreuseerror0otransportconfig00otryfrominterroroversionmismatch0ozerorttacceptedozerorttrejected000mbloomtokenlogmclonetouninit00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ndefaultruntimemgreasequicbit0nintegritylimit0ninterfaceindexmmaxstreamdata0mmaxstreamsuni0mparsebytesizenrebindabstractnsendunreliablenserverendpointmsetpathstatusmsetsendwindow0nshouldtransmitnstreamfinishednthroughputhistmwrapudpsocket00nwritehandshake000A`addressdiscoveryA`applicationclose000A`certificateerrorA`connectionclosed0A`connectionhandleA`datagramreceivedA`decryptedinitial0A`endpointstopping0nfinalsizeerror0A`inconsistentkeys00nkeyupdateerror0A`maxpathidreached0A`observedipv4addr0A`observedipv6addr0A`pathstatusbackup0A`peerincompatible00A`quicclientconfig0A`quicserverconfig0A`tokenmemorycache0A`toomanyaddressesA`validationfailed00A`versionnegotiate0ncheckandinsert00000oconfigureclientocongestionstate0oconnectionclose0nforcekeyupdate0nfromargmatches00000nmaxgsosegmentsnmaxidletimeout0nmaxstreamsbidi0nn0nattraversal0oopenconnections0nopenpathensure0opacketthreshold0nreadmanychunksoreceivedatagramnrecvbuffersize0nsendbuffersize0otransportconfig0000ovalidationtoken0nwithsinglecert0nwriteallchunksA`applicationerror0Aaapplicationclosed000Aacontrollerfactory0Aacontrollermetrics0Aahandshaketokenkey0Aainvalidservername0Aanetworkchangehint0Aapacketdecodeerror0Aapathabandonreason00Aasenddatagramerror0Aastreamdatablocked0Aastreamsblockeduni0Aaunsupportedbypeer0A`congestionevents0A`congestionwindow0omaxdatagramsizeonewconnectionid0opathcidsblocked0osendbufferspaceosetserverconfig0osetupconnectionotopossiblevalueowaitalldrainingowritemanychunksA`aeadlimitreached0Abackfrequencyconfig0Abbadmaxfragmentsize0Aaconnectionrefused0AbconnectionidparserAbdatagramsunblockedA`flowcontrolerror0AbhandshakeconfirmedAbhandshakedatareadyAbillegalorderedreadAbinvalidcertificate0Abmtudiscoveryconfig0AbnotenoughaddressesA`perfciphersuitesAaprotocolviolation0Abprivatepkcs8keyderAbretireconnectionid0A`streamlimiterror0A`streamstateerror0AbservercertverifiedAbservercertverifierAbsetpathstatuserror0Abstreamsblockedbidi0AbtokiocurrentthreadAbtransporterrorcode0Abunsupportedversion00000A`commandforupdate0000A`cryptobuffersize0A`fromu64unchecked0AaignoredhandshakesA`issameconnectionA`minresetinterval0A`newexpecteditems0A`pathstatusbackup0AarefusedhandshakesA`remotepathstatusA`senddatagramwaitA`setalpnprotocols000A`setreceivewindow0Aasupportedversions0A`tokenmemorycacheA`verifyservercertA`withstartinstant0AccongestionalgorithmAcnattraversalupdatesAcpathnewconnectionid0Acpathstatusavailable0Acremotecidsexhausted0ActransportparametersAcunorderedrecvstreamAcunsupportednametype0AcwrongconnectionsideAbacceptedhandshakesAaarbitrarytakerest000AbaugmentsubcommandsAablackholecooldown0Aaearlydataaccepted000AbhandshakeconfirmedAaispathrecoverable0Aakeepaliveinterval0Aalostplpmtudprobes0Aaoncongestionevent0000000AboutgoinghandshakesAaremoteopenstreamsAasendbufferbenches00A`senddataonstreamAasentplpmtudprobes0Aastreamdatablocked0Aastreamsblockeduni0Abframeencodingerror0Adhandshakenotcomplete0Adinappropriatemessage000Adinvalidremoteaddress000Adnoinitialciphersuite0AdobservedexternaladdrAdserversidenotallowed0AdsupportedciphersuiteAdvarintboundsexceeded0AdverifierbuildererrorAdweakconnectionhandleAbackfrequencyconfig0Abblackholesdetected0Aafromargmatchesmut00000AbhandleclientstreamAbincomingbuffersize0AbismultipathenabledAbmaxreceivesegments0Aamaxudppayloadsize00Abmtudiscoveryconfig0AbpollendpointeventsAbpreferredaddressv40Abpreferredaddressv60AareadcertsfromfileAcreorderingthreshold0Abretireconnectionid0Abretrytokenlifetime0AasetmaxidletimeoutAasetrecvbuffersizeAasetsendbuffersizeAbstreamsblockedbidi0Actransportparameters00000Aeconnectionidgenerator0AedigitallysignedstructAenoapplicationprotocol0Aenodefaultclientconfig0Abpathunstableorpoor0Aevalidationtokenconfig0Adconfidentialitylimit0Achandlenetworkchange0AcincomingbufferbytesAclossreductionfactor0Acmaxtransmitsegments0AcnattraversalupdatesAcpathobservedaddressAcpathstatusavailable0Acstreamreceivewindow00Adcryptobufferexceeded0AfextensionnotnegotiatedAffailedtogetcurrenttime0Affailedtogetrandombytes0Afmultipathnotnegotiated000000Afpathretireconnectionid0AfprotectedinitialheaderAfskipserververificationAdbuildtransportconfigAdexportkeyingmaterial0000AcgetintomanysegmentsAcgetloopmanysegmentsAdmaxconcurrentstreamsAdobservedexternaladdr0Acpathnewconnectionid0Adverifytls12signatureAdverifytls13signatureAdwithrootcertificates0AgcertrevocationlisterrorAghandshakesignaturevalidAgnocertificatespresented0Agpeersentoversizedrecord0Aeackelicitingthreshold0Adaugmentargsforupdate0000AehaspendingretransmitsAeismultipathnegotiatedAdonackfrequencyupdate000AdsetkeepaliveintervalAdupdatefromargmatches00000Aevalidationtokenconfig0Afapplicationabandonpath0AhnoprotectionclientconfigAhnoprotectionserverconfigAdgetmaxudppayloadsize0Aeinitialdstcidprovider0AenewwithabstractsocketAfremoteaddressvalidated0AesetmaxconcurrentpathsAfsupportedverifyschemesAfconnectionidlimiterror0AiencryptedclienthelloerrorAiexportkeyingmaterialerror0Aiinvalidcertrevocationlist0Aenocidavailableforpath0Agtransportparametererror0AiunsupportedoperationerrorAfaddnattraversaladdress0Afdatagramsendbuffersize0AfgenerateselfsignedcertAgopenbidirectionalstreamAfpathretireconnectionid0AfsetdefaultclientconfigAesetpathmaxidletimeoutAhdefaultsupportedversionsAjunusableafternetworkchangeAgdatagramsendbufferspaceAgincomingbuffersizetotal0Agmaxconcurrentunistreams0AhopenunidirectionalstreamAgsetmaxconcurrentstreamsAhspuriouscongestionevents0Agtrywithplatformverifier0AkhashedconnectionidgeneratorAkinvalidencryptedclienthello0Ahpathresourcelimitreached0AkrandomconnectionidgeneratorAienablesegmentationoffload0Ahmaxconcurrentbidistreams0Agupdatefromargmatchesmut00000AjconfiguretracingsubscriberAidatagramreceivebuffersize0Aiinitiatenattraversalround0Aionspuriouscongestionevent00000AjreceivebidirectionalstreamAiremovenattraversaladdress0AhsetpathkeepaliveintervalAmfixedlengthconnectionidparserAminappropriatehandshakemessage000Akcongestioncontrollerfactory0Aidefaultpathmaxidletimeout0Aimaxoutgoingbytespersecond0Ajnegotiatedkeyexchangegroup0AkreceiveunidirectionalstreamAjsendobservedaddressreports0AisetmaxconcurrentbistreamsAkaugmentsubcommandsforupdateAkmaxconcurrentmultipathpaths0AjsetmaxconcurrentunistreamsAmpersistentcongestionthreshold0Aldefaultpathkeepaliveinterval0Amreceiveobservedaddressreports0Amgetlocalnattraversaladdresses0Angetremotenattraversaladdresses0Anmaxremotenattraversaladdresses00") \ No newline at end of file diff --git a/pr/672/docs/search.index/path/813227ed5be5.js b/pr/672/docs/search.index/path/813227ed5be5.js new file mode 100644 index 000000000..268bb3306 --- /dev/null +++ b/pr/672/docs/search.index/path/813227ed5be5.js @@ -0,0 +1 @@ +rd_("f[1,\"\"]0Al[10,\"core::ops\",\"core::ops\"]Ce[5,\"clap_builder::util::id\",\"clap_builder::util::id\"]22Ag[10,\"core::ops::arith\"]A`[10,\"core::any\"]Ao[5,\"alloc::sync\",\"alloc::sync\"]Ba[5,\"alloc::boxed\",\"alloc::boxed\"]Cb[10,\"bytes::buf::buf_impl\",\"bytes::buf::buf_impl\"]Ai[5,\"noq_perf\",\"noq_perf\"]i[6,\"noq\"]Ak[6,\"noq_proto\",\"noq_proto\"]Ae[10,\"core::ops::bit\"]Ac[5,\"bench\",\"bench\"]Ba[5,\"perf::client\",\"perf::client\"]Ba[5,\"perf::server\",\"perf::server\"]A`[10,\"core::cmp\"]Ak[5,\"core::pin\",\"core::pin\"]Am[5,\"alloc::vec\",\"alloc::vec\"]f[1,\"\"]00m[2,\"noq_udp\"]f[0,\"\"]f[3,\"\"]3333Ak[10,\"clap_builder::derive\"]Bn[10,\"std::os::fd::owned\",\"std::os::fd::owned\"]Ae[5,\"noq::congestion\"]Ci[5,\"noq_proto::congestion\",\"noq_proto::congestion::bbr3\"]Af[10,\"core::ops::drop\"]Ad[10,\"core::convert\"]Aa[10,\"core::hash\"]1Aa[5,\"noq::crypto\"]Bk[5,\"noq_proto::crypto\",\"noq_proto::crypto\"]Ba[15,\"noq_proto::ProtectedHeader\"]Ak[5,\"std::path\",\"std::path\"]Ae[5,\"noq\",\"noq::path\"]Bi[6,\"core::task::poll\",\"core::task::poll\"]i[6,\"noq\"]Ak[6,\"noq_proto\",\"noq_proto\"]Ak[2,\"noq_proto::congestion\"]f[1,\"\"]f[3,\"\"]i[2,\"noq\"]1Ag[2,\"noq_proto::config\"]Bd[2,\"noq_proto::connection::streams\"]0Ab[2,\"noq::runtime\"]55Bd[10,\"core::convert\",\"core::convert\"]Ba[5,\"bytes::bytes\",\"bytes::bytes\"]i[5,\"noq\"]Cb[5,\"noq_proto\",\"noq_proto::connection::assembler\"]Ab[10,\"core::clone\"]Bi[6,\"noq_proto::frame\",\"noq_proto::frame\"]Ae[5,\"noq::congestion\"]Cj[5,\"noq_proto::congestion\",\"noq_proto::congestion::cubic\"]A`[10,\"core::fmt\"]Ag[10,\"core::ops::deref\"]Ak[5,\"core::fmt\",\"core::fmt\"]Ab[10,\"core::error\"]Ai[2,\"noq::crypto::rustls\"]Ao[2,\"noq_proto::crypto::rustls\"]Cm[5,\"noq_proto::transport_error\",\"noq_proto::transport_error\"]Bc[6,\"rustls::error\",\"rustls::error\"]Ba[8,\"clap_builder\",\"clap_builder\"]Bi[6,\"arbitrary::error\",\"arbitrary::error\"]Ai[6,\"noq::crypto::rustls\"]Ao[6,\"noq_proto::crypto::rustls\"]Dg[6,\"noq_proto::transport_parameters\",\"noq_proto::transport_parameters\"]Co[6,\"noq_proto::n0_nat_traversal\",\"noq_proto::n0_nat_traversal\"]o[2,\"noq_proto\"]Bg[6,\"noq_proto\",\"noq_proto::connection\"]2Bi[5,\"core::ops::range\",\"core::ops::range\"]Ba[15,\"noq_proto::ProtectedHeader\"]0Bb[10,\"core::marker\",\"core::marker\"]Ba[5,\"bench::stats\",\"bench::stats\"]Ao[5,\"perf::stats\",\"perf::stats\"]Ba[5,\"rustls::quic\",\"rustls::quic\"]Ah[10,\"std::io\",\"std::io\"]f[1,\"\"]f[3,\"\"]Ak[2,\"noq_proto::congestion\"]l[2,\"rustls\"]Bi[5,\"bytes::bytes_mut\",\"bytes::bytes_mut\"]Ai[6,\"noq_perf\",\"noq_perf\"]Bb[5,\"noq_proto\",\"noq_proto::frame\"]Ag[10,\"core::ops::deref\"]Am[5,\"core::time\",\"core::time\"]Bc[5,\"noq::endpoint\",\"noq::endpoint\"]Ai[5,\"noq\",\"noq::endpoint\"]Be[5,\"noq_proto\",\"noq_proto::endpoint\"]Am[15,\"noq_proto::StreamEvent\"]Ai[5,\"noq\",\"noq::incoming\"]2Bc[10,\"core::iter::traits::iterator\"]An[10,\"tonic::service::layered\"]Bc[6,\"noq_proto\",\"noq_proto::packet\"]Ak[5,\"noq\",\"noq::connection\"]Ae[5,\"noq\",\"noq::path\"]Ak[6,\"core::cmp\",\"core::cmp\"]7Ag[5,\"noq_udp\",\"noq_udp\"]i[5,\"noq\"]Ak[5,\"noq_proto\",\"noq_proto\"]Ad[10,\"alloc::string\"]j[10,\"noq\"]Bc[10,\"noq_proto\",\"noq_proto::token\"]4354Bn[5,\"noq_proto\",\"noq_proto::connection::stats\"]Bi[5,\"rustls_pki_types\",\"rustls_pki_types\"]?i[2,\"noq\"]o[2,\"noq_proto\"]1Ak[2,\"noq_proto::congestion\"]f[3,\"\"]Ae[15,\"noq::PathEvent\"]Ak[15,\"noq_proto::PathEvent\"]Ak[5,\"noq\",\"noq::connection\"]Ag[10,\"core::ops::arith\"]A`[10,\"arbitrary\"]Al[10,\"tokio::io::async_read\"]Ai[10,\"futures_io::if_std\"]Am[15,\"noq_proto::StreamEvent\"]Ac[10,\"core::borrow\"]Aa[5,\"perf\",\"perf\"]Ak[5,\"criterion\",\"criterion\"]Cb[5,\"noq_proto\",\"noq_proto::connection::datagrams\"]Bl[10,\"noq_proto::coding\",\"noq_proto::coding\"]<;0Ak[5,\"core::fmt\",\"core::fmt\"]i[5,\"noq\"]Ak[5,\"noq_proto\",\"noq_proto\"]i[6,\"noq\"]Bb[6,\"noq_proto\",\"noq_proto::frame\"]Ab[10,\"noq::crypto\"]Bl[10,\"noq_proto::crypto\",\"noq_proto::crypto\"]Ba[5,\"hdrhistogram\",\"hdrhistogram\"]21A`[10,\"core::cmp\"]5Bg[6,\"noq_proto\",\"noq_proto::connection\"]i[2,\"noq\"]o[2,\"noq_proto\"]8Bn[6,\"noq_proto\",\"noq_proto::connection::paths\"];Bn[5,\"noq_proto\",\"noq_proto::connection::stats\"]Al[6,\"noq\",\"noq::recv_stream\"]Cf[6,\"noq_proto\",\"noq_proto::connection::streams::recv\"]Ak[10,\"futures_core::future\"]Ak[10,\"futures_core::stream\"]Ai[10,\"noq\",\"noq::runtime\"]Bc[5,\"std::net::udp\",\"std::net::udp\"]Ak[10,\"clap_builder::derive\"]Ak[2,\"noq_proto::connection\"]0f[3,\"\"]f[1,\"\"]Ag[2,\"noq_proto::config\"]Em[5,\"clap_builder::parser::matches::arg_matches\",\"clap_builder::parser::matches::arg_matches\"]7Am[10,\"tokio::io::async_write\"]Ai[10,\"futures_io::if_std\"]Ae[5,\"noq::congestion\"]Ci[5,\"noq_proto::congestion\",\"noq_proto::congestion::bbr3\"]i[5,\"noq\"]Bn[5,\"noq_proto\",\"noq_proto::connection::paths\"]Aa[10,\"equivalent\"]Ak[5,\"noq\",\"noq::connection\"]Bg[5,\"noq::connection\",\"noq::connection\"]1Bg[5,\"noq_proto\",\"noq_proto::connection\"]Af[10,\"noq::congestion\"]Cd[10,\"noq_proto::congestion\",\"noq_proto::congestion\"]A`[10,\"hashbrown\"]68Bn[5,\"noq_proto\",\"noq_proto::connection::stats\"]B`[10,\"core::future::into_future\"]:Bj[5,\"noq_proto\",\"noq_proto::cid_generator\"]Ag[5,\"std::io\",\"std::io\"]Bi[6,\"noq_proto::frame\",\"noq_proto::frame\"]Bb[6,\"noq_proto\",\"noq_proto::frame\"]Bm[8,\"core::num::nonzero\",\"core::num::nonzero\"]Cm[5,\"rustls::error::other_error\",\"rustls::error::other_error\"]A`[10,\"core::cmp\"]Am[5,\"noq\",\"noq::event_stream\"]i[6,\"noq\"]Bn[6,\"noq_proto\",\"noq_proto::connection::paths\"]i[5,\"noq\"]Bi[5,\"noq_proto\",\"noq_proto::config::qlog\"]Bi[5,\"noq::recv_stream\",\"noq::recv_stream\"]Al[5,\"noq\",\"noq::recv_stream\"]C`[5,\"noq_proto\",\"noq_proto::connection::streams\"]Al[6,\"noq\",\"noq::recv_stream\"]Ai[5,\"noq\",\"noq::incoming\"]Be[5,\"noq_proto\",\"noq_proto::endpoint\"]Bi[5,\"noq::send_stream\",\"noq::send_stream\"]Al[5,\"noq\",\"noq::send_stream\"]5Dc[6,\"rustls_pki_types::server_name\",\"rustls_pki_types::server_name\"]Ce[6,\"core::net::socket_addr\",\"core::net::socket_addr\"]Bb[5,\"noq_proto\",\"noq_proto::frame\"]Ak[10,\"clap_builder::derive\"]Ak[5,\"std::time\",\"std::time\"]j[10,\"noq\"]Bd[10,\"noq_proto\",\"noq_proto::config\"]Ai[5,\"noq::crypto::rustls\"]Ck[5,\"noq_proto::crypto::rustls\",\"noq_proto::crypto::rustls\"]3Bc[10,\"noq_proto\",\"noq_proto::token\"]Ag[5,\"noq_udp\",\"noq_udp\"]Al[6,\"noq\",\"noq::send_stream\"]Cf[6,\"noq_proto\",\"noq_proto::connection::streams::send\"]i[2,\"noq\"]o[2,\"noq_proto\"]10Be[5,\"noq_proto\",\"noq_proto::endpoint\"]Ac[6,\"bench\",\"bench\"]Aa[5,\"bulk\",\"bulk\"]Da[6,\"noq_proto::connection::state\",\"noq_proto::connection::state\"]i[6,\"noq\"]Bc[6,\"noq_proto\",\"noq_proto::config\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Aa[5,\"noq::crypto\"]Bk[5,\"noq_proto::crypto\",\"noq_proto::crypto\"]Ae[5,\"noq::congestion\"]Cj[5,\"noq_proto::congestion\",\"noq_proto::congestion::cubic\"]Ae[15,\"noq::PathEvent\"]Ak[15,\"noq_proto::PathEvent\"]?i[5,\"noq\"]Bn[5,\"noq_proto\",\"noq_proto::config::transport\"]Ae[10,\"tonic::request\"]j[10,\"noq\"]Bj[10,\"noq_proto\",\"noq_proto::config::qlog\"]Ac[6,\"bench\",\"bench\"]An[5,\"noq\",\"noq::runtime::smol\"]o[2,\"noq_proto\"]C`[6,\"noq_proto\",\"noq_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]Ao[5,\"perf::stats\",\"perf::stats\"]f[3,\"\"]i[2,\"noq\"]Ak[2,\"noq_proto::connection\"]1Bk[5,\"noq_proto::config\",\"noq_proto::config\"]Co[5,\"rustls::client::client_conn\",\"rustls::client::client_conn\"]?Ab[10,\"noq::crypto\"]Bc[5,\"noq_proto\",\"noq_proto::config\"]Bl[10,\"noq_proto::crypto\",\"noq_proto::crypto\"]i[5,\"noq\"]C`[5,\"noq_proto\",\"noq_proto::connection::streams\"]Bo[6,\"noq_proto::endpoint\",\"noq_proto::endpoint\"]i[6,\"noq\"]Be[6,\"noq_proto\",\"noq_proto::endpoint\"]Bk[5,\"noq_proto::shared\",\"noq_proto::shared\"]5Bc[5,\"noq_proto\",\"noq_proto::shared\"]Bb[5,\"noq_proto\",\"noq_proto::frame\"]4Bc[6,\"noq_proto\",\"noq_proto::shared\"]Ag[6,\"noq_udp\",\"noq_udp\"]Bb[10,\"core::iter::traits::collect\"]Bm[8,\"core::num::nonzero\",\"core::num::nonzero\"];Bb[5,\"noq_proto\",\"noq_proto::token\"]Ae[15,\"noq::PathEvent\"]Ak[15,\"noq_proto::PathEvent\"]Ak[5,\"noq\",\"noq::connection\"]21Da[5,\"noq_proto::connection::paths\",\"noq_proto::connection::paths\"]Bn[5,\"noq_proto\",\"noq_proto::connection::paths\"]2Bk[5,\"noq_proto::config\",\"noq_proto::config\"]Co[5,\"rustls::server::server_conn\",\"rustls::server::server_conn\"]i[5,\"noq\"]Ab[10,\"noq::crypto\"]Bc[5,\"noq_proto\",\"noq_proto::config\"]Bl[10,\"noq_proto::crypto\",\"noq_proto::crypto\"]Ce[5,\"core::net::socket_addr\",\"core::net::socket_addr\"]0Al[6,\"noq\",\"noq::send_stream\"]Ao[5,\"noq\",\"noq::runtime::tokio\"]Cg[5,\"arbitrary::unstructured\",\"arbitrary::unstructured\"]i[2,\"noq\"]j[2,\"perf\"]9Bl[5,\"noq_proto\",\"noq_proto::bloom_token_log\"]Ab[10,\"core::clone\"]Be[6,\"noq_proto\",\"noq_proto::endpoint\"]Bc[5,\"noq_proto\",\"noq_proto::shared\"]Ai[5,\"noq\",\"noq::endpoint\"]Ai[5,\"noq::crypto::rustls\"]Ck[5,\"noq_proto::crypto::rustls\",\"noq_proto::crypto::rustls\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Bo[5,\"noq_proto::endpoint\",\"noq_proto::endpoint\"]Be[5,\"noq_proto\",\"noq_proto::endpoint\"]Ae[5,\"noq::congestion\"]Cm[5,\"noq_proto::congestion\",\"noq_proto::congestion::new_reno\"]Bi[5,\"core::num::error\",\"core::num::error\"]Bc[5,\"noq_proto\",\"noq_proto::packet\"]Ec[5,\"clap_builder::builder::possible_value\",\"clap_builder::builder::possible_value\"]Bi[6,\"rustls_pki_types\",\"rustls_pki_types\"]Cf[6,\"noq_proto\",\"noq_proto::connection::streams::recv\"]Cg[5,\"rustls::webpki::anchors\",\"rustls::webpki::anchors\"]i[5,\"noq\"]Bc[5,\"noq_proto\",\"noq_proto::config\"]Bk[5,\"noq_proto::coding\",\"noq_proto::coding\"]o[2,\"noq_proto\"]0f[3,\"\"]Ai[10,\"noq\",\"noq::runtime\"]Bi[5,\"rustls_pki_types\",\"rustls_pki_types\"]i[6,\"noq\"]Bg[6,\"noq_proto\",\"noq_proto::connection\"]Ak[10,\"clap_builder::derive\"]Ag[15,\"noq_proto::Event\"]:91Ai[5,\"noq\",\"noq::incoming\"]Bb[5,\"noq_proto\",\"noq_proto::frame\"]Bc[6,\"rustls::error\",\"rustls::error\"]=Bb[5,\"noq_proto\",\"noq_proto::token\"]1Al[6,\"noq\",\"noq::recv_stream\"]0C`[5,\"noq_proto\",\"noq_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]o[5,\"noq_proto\"]Al[5,\"noq_udp\",\"noq_udp::imp\"]Ae[5,\"noq\",\"noq::path\"]i[5,\"noq\"]9>=Bc[5,\"noq_proto\",\"noq_proto::shared\"]1Bn[5,\"noq_proto\",\"noq_proto::connection::stats\"]Ba[5,\"rustls::rand\",\"rustls::rand\"]Ao[5,\"perf::stats\",\"perf::stats\"]o[2,\"noq_proto\"]Bc[6,\"noq_proto\",\"noq_proto::packet\"]6Bi[5,\"noq_proto\",\"noq_proto::config::qlog\"]Bc[15,\"noq_proto::PathAbandonReason\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Ak[5,\"std::time\",\"std::time\"]:Bb[5,\"noq_proto\",\"noq_proto::token\"]Da[5,\"noq_proto::config::transport\",\"noq_proto::config::transport\"]List of all items in this crate

All

\ No newline at end of file diff --git a/pr/672/docs/set_up_connection/constant.CLIENT_ADDR.html b/pr/672/docs/set_up_connection/constant.CLIENT_ADDR.html new file mode 100644 index 000000000..480b4b634 --- /dev/null +++ b/pr/672/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/672/docs/set_up_connection/constant.LOCALHOST_V4.html b/pr/672/docs/set_up_connection/constant.LOCALHOST_V4.html new file mode 100644 index 000000000..49f7c0f99 --- /dev/null +++ b/pr/672/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/672/docs/set_up_connection/constant.SERVER_ADDR.html b/pr/672/docs/set_up_connection/constant.SERVER_ADDR.html new file mode 100644 index 000000000..046828f6b --- /dev/null +++ b/pr/672/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/672/docs/set_up_connection/constant.SERVER_NAME.html b/pr/672/docs/set_up_connection/constant.SERVER_NAME.html new file mode 100644 index 000000000..84fc92733 --- /dev/null +++ b/pr/672/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/672/docs/set_up_connection/fn.client.html b/pr/672/docs/set_up_connection/fn.client.html new file mode 100644 index 000000000..0b2708bd9 --- /dev/null +++ b/pr/672/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/672/docs/set_up_connection/fn.main.html b/pr/672/docs/set_up_connection/fn.main.html new file mode 100644 index 000000000..23244837f --- /dev/null +++ b/pr/672/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/672/docs/set_up_connection/fn.server.html b/pr/672/docs/set_up_connection/fn.server.html new file mode 100644 index 000000000..5c49aee37 --- /dev/null +++ b/pr/672/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/672/docs/set_up_connection/index.html b/pr/672/docs/set_up_connection/index.html new file mode 100644 index 000000000..9d78e0b7f --- /dev/null +++ b/pr/672/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/672/docs/set_up_connection/sidebar-items.js b/pr/672/docs/set_up_connection/sidebar-items.js new file mode 100644 index 000000000..3f14772bc --- /dev/null +++ b/pr/672/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/672/docs/settings.html b/pr/672/docs/settings.html new file mode 100644 index 000000000..eb1ebf9ed --- /dev/null +++ b/pr/672/docs/settings.html @@ -0,0 +1 @@ +Settings

All

Rustdoc settings

Back
\ No newline at end of file diff --git a/pr/672/docs/src-files.js b/pr/672/docs/src-files.js new file mode 100644 index 000000000..25a9d0216 --- /dev/null +++ b/pr/672/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"]]],["noq",["",[["runtime",[],["mod.rs","smol.rs","tokio.rs"]]],["connection.rs","endpoint.rs","event_stream.rs","incoming.rs","lib.rs","mutex.rs","path.rs","recv_stream.rs","send_stream.rs","work_limiter.rs"]]],["noq_perf",["",[],["perf.rs"]]],["noq_proto",["",[["config",[],["mod.rs","qlog.rs","transport.rs"]],["congestion",[["bbr3",[],["max_filter.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","lib.rs","n0_nat_traversal.rs","packet.rs","shared.rs","token.rs","token_memory_cache.rs","transport_error.rs","transport_parameters.rs","varint.rs"]]],["noq_udp",["",[["cmsg",[],["mod.rs","unix.rs"]]],["lib.rs","unix.rs"]]],["perf",["",[],["client.rs","lib.rs","noprotection.rs","server.rs","stats.rs"]]],["set_up_connection",["",[],["set-up-connection.rs"]]]]'); +//{"start":19,"fragment_lengths":[39,29,43,47,208,33,870,73,81,55]} \ No newline at end of file diff --git a/pr/672/docs/src/bench/lib.rs.html b/pr/672/docs/src/bench/lib.rs.html new file mode 100644 index 000000000..0ff1245ef --- /dev/null +++ b/pr/672/docs/src/bench/lib.rs.html @@ -0,0 +1,294 @@ +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 noq::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, noq::Endpoint) {
+39    let cert_chain = vec![cert];
+40    let mut server_config = noq::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        noq::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<(noq::Endpoint, noq::Connection)> {
+61    let endpoint =
+62        noq::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 = noq::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: noq::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_many_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 noq::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_type: RuntimeType) -> Runtime {
+151    match runtime_type {
+152        RuntimeType::Tokio => {
+153            let counter = std::sync::atomic::AtomicUsize::new(0);
+154            Builder::new_multi_thread()
+155                .thread_name_fn(move || {
+156                    format!(
+157                        "tokio-runtime-{}",
+158                        counter.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
+159                    )
+160                })
+161                .enable_all()
+162                .build()
+163                .unwrap()
+164        }
+165        RuntimeType::TokioCurrentThread => {
+166            Builder::new_current_thread().enable_all().build().unwrap()
+167        }
+168    }
+169}
+170
+171pub fn transport_config(opt: &Opt) -> noq::TransportConfig {
+172    // High stream windows are chosen because the amount of concurrent streams
+173    // is configurable as a parameter.
+174    let mut config = noq::TransportConfig::default();
+175    config.max_concurrent_uni_streams(opt.max_streams.try_into().unwrap());
+176    config.initial_mtu(opt.initial_mtu);
+177
+178    let mut acks = noq::AckFrequencyConfig::default();
+179    acks.ack_eliciting_threshold(10u32.into());
+180    config.ack_frequency_config(Some(acks));
+181
+182    config
+183}
+184
+185#[derive(Parser, Debug, Clone, Copy)]
+186#[clap(name = "bulk")]
+187pub struct Opt {
+188    /// The total number of clients which should be created
+189    #[clap(long = "clients", short = 'c', default_value = "1")]
+190    pub clients: usize,
+191    /// The total number of streams which should be created
+192    #[clap(long = "streams", short = 'n', default_value = "1")]
+193    pub streams: usize,
+194    /// The amount of concurrent streams which should be used
+195    #[clap(long = "max_streams", short = 'm', default_value = "1")]
+196    pub max_streams: usize,
+197    /// Number of bytes to transmit from server to client
+198    ///
+199    /// This can use SI suffixes for sizes. For example, 1M will transfer
+200    /// 1MiB, 10G will transfer 10GiB.
+201    #[clap(long, default_value = "1G", value_parser = parse_byte_size)]
+202    pub download_size: u64,
+203    /// Number of bytes to transmit from client to server
+204    ///
+205    /// This can use SI suffixes for sizes. For example, 1M will transfer
+206    /// 1MiB, 10G will transfer 10GiB.
+207    #[clap(long, default_value = "0", value_parser = parse_byte_size)]
+208    pub upload_size: u64,
+209    /// Show connection stats the at the end of the benchmark
+210    #[clap(long = "stats")]
+211    pub stats: bool,
+212    /// Whether to use the unordered read API
+213    #[clap(long = "unordered")]
+214    pub read_unordered: bool,
+215    /// Allows to configure the desired cipher suite
+216    ///
+217    /// Valid options are: aes128, aes256, chacha20
+218    #[clap(long = "cipher", default_value = "aes128")]
+219    pub cipher: CipherSuite,
+220    /// Starting guess for maximum UDP payload size
+221    #[clap(long, default_value = "1200")]
+222    pub initial_mtu: u16,
+223    /// The runtime type to use
+224    #[clap(long, default_value = "tokio")]
+225    pub runtime_type: RuntimeType,
+226}
+227
+228#[derive(Debug, Clone, Copy)]
+229pub enum RuntimeType {
+230    Tokio,
+231    TokioCurrentThread,
+232}
+233
+234impl FromStr for RuntimeType {
+235    type Err = anyhow::Error;
+236
+237    fn from_str(s: &str) -> Result<Self, Self::Err> {
+238        match s.to_lowercase().as_str() {
+239            "tokio" => Ok(Self::Tokio),
+240            "tokio-current-thread" => Ok(Self::TokioCurrentThread),
+241            _ => Err(anyhow::anyhow!("Unknown runtime type {}", s)),
+242        }
+243    }
+244}
+245
+246fn parse_byte_size(s: &str) -> Result<u64, ParseIntError> {
+247    let s = s.trim();
+248
+249    let multiplier = match s.chars().last() {
+250        Some('T') => 1024 * 1024 * 1024 * 1024,
+251        Some('G') => 1024 * 1024 * 1024,
+252        Some('M') => 1024 * 1024,
+253        Some('k') => 1024,
+254        _ => 1,
+255    };
+256
+257    let s = match multiplier {
+258        1 => s,
+259        _ => &s[..s.len() - 1],
+260    };
+261
+262    Ok(u64::from_str(s)? * multiplier)
+263}
+264
+265#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+266pub enum CipherSuite {
+267    Aes128,
+268    Aes256,
+269    Chacha20,
+270}
+271
+272impl CipherSuite {
+273    pub fn as_rustls(self) -> rustls::SupportedCipherSuite {
+274        use rustls::crypto::ring::cipher_suite;
+275        match self {
+276            Self::Aes128 => cipher_suite::TLS13_AES_128_GCM_SHA256,
+277            Self::Aes256 => cipher_suite::TLS13_AES_256_GCM_SHA384,
+278            Self::Chacha20 => cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
+279        }
+280    }
+281}
+282
+283impl FromStr for CipherSuite {
+284    type Err = anyhow::Error;
+285
+286    fn from_str(s: &str) -> Result<Self, Self::Err> {
+287        match s.to_lowercase().as_str() {
+288            "aes128" => Ok(Self::Aes128),
+289            "aes256" => Ok(Self::Aes256),
+290            "chacha20" => Ok(Self::Chacha20),
+291            _ => Err(anyhow::anyhow!("Unknown cipher suite {}", s)),
+292        }
+293    }
+294}
\ No newline at end of file diff --git a/pr/672/docs/src/bench/stats.rs.html b/pr/672/docs/src/bench/stats.rs.html new file mode 100644 index 000000000..b8c881d8d --- /dev/null +++ b/pr/672/docs/src/bench/stats.rs.html @@ -0,0 +1,95 @@ +stats.rs - source

bench/
stats.rs

1use std::time::Duration;
+2
+3use hdrhistogram::Histogram;
+4
+5#[derive(Default)]
+6pub struct Stats {
+7    pub total_size: u64,
+8    pub total_duration: Duration,
+9    pub streams: usize,
+10    pub stream_stats: StreamStats,
+11}
+12
+13impl Stats {
+14    pub fn stream_finished(&mut self, stream_result: TransferResult) {
+15        self.total_size += stream_result.size;
+16        self.streams += 1;
+17
+18        self.stream_stats
+19            .duration_hist
+20            .record(stream_result.duration.as_millis() as u64)
+21            .unwrap();
+22        self.stream_stats
+23            .throughput_hist
+24            .record(stream_result.throughput as u64)
+25            .unwrap();
+26    }
+27
+28    pub fn print(&self, stat_name: &str) {
+29        println!("Overall {stat_name} stats:\n");
+30        println!(
+31            "Transferred {} bytes on {} streams in {:4.2?} ({:.2} MiB/s)\n",
+32            self.total_size,
+33            self.streams,
+34            self.total_duration,
+35            throughput_bps(self.total_duration, self.total_size) / 1024.0 / 1024.0
+36        );
+37
+38        println!("Stream {stat_name} metrics:\n");
+39
+40        println!("      │  Throughput   │ Duration ");
+41        println!("──────┼───────────────┼──────────");
+42
+43        let print_metric = |label: &'static str, get_metric: fn(&Histogram<u64>) -> u64| {
+44            println!(
+45                " {} │ {:7.2} MiB/s │ {:>9.2?}",
+46                label,
+47                get_metric(&self.stream_stats.throughput_hist) as f64 / 1024.0 / 1024.0,
+48                Duration::from_millis(get_metric(&self.stream_stats.duration_hist))
+49            );
+50        };
+51
+52        print_metric("AVG ", |hist| hist.mean() as u64);
+53        print_metric("P0  ", |hist| hist.value_at_quantile(0.00));
+54        print_metric("P10 ", |hist| hist.value_at_quantile(0.10));
+55        print_metric("P50 ", |hist| hist.value_at_quantile(0.50));
+56        print_metric("P90 ", |hist| hist.value_at_quantile(0.90));
+57        print_metric("P100", |hist| hist.value_at_quantile(1.00));
+58    }
+59}
+60
+61pub struct StreamStats {
+62    pub duration_hist: Histogram<u64>,
+63    pub throughput_hist: Histogram<u64>,
+64}
+65
+66impl Default for StreamStats {
+67    fn default() -> Self {
+68        Self {
+69            duration_hist: Histogram::<u64>::new(3).unwrap(),
+70            throughput_hist: Histogram::<u64>::new(3).unwrap(),
+71        }
+72    }
+73}
+74
+75#[derive(Debug)]
+76pub struct TransferResult {
+77    pub duration: Duration,
+78    pub size: u64,
+79    pub throughput: f64,
+80}
+81
+82impl TransferResult {
+83    pub fn new(duration: Duration, size: u64) -> Self {
+84        let throughput = throughput_bps(duration, size);
+85        Self {
+86            duration,
+87            size,
+88            throughput,
+89        }
+90    }
+91}
+92
+93pub fn throughput_bps(duration: Duration, size: u64) -> f64 {
+94    (size as f64) / (duration.as_secs_f64())
+95}
\ No newline at end of file diff --git a/pr/672/docs/src/bulk/bulk.rs.html b/pr/672/docs/src/bulk/bulk.rs.html new file mode 100644 index 000000000..074ccbeae --- /dev/null +++ b/pr/672/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(opt.runtime_type);
+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(opt.runtime_type);
+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: noq::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(noq::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_all_draining().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<noq::Connection>,
+179    upload_size: u64,
+180    read_unordered: bool,
+181) -> Result<(TransferResult, TransferResult)> {
+182    let start = Instant::now();
+183
+184    let (mut send_stream, recv_stream) = connection
+185        .open_bi()
+186        .await
+187        .context("failed to open stream")?;
+188
+189    send_data_on_stream(&mut send_stream, upload_size).await?;
+190
+191    let upload_result = TransferResult::new(start.elapsed(), upload_size);
+192
+193    let start = Instant::now();
+194    let size = drain_stream(recv_stream, read_unordered).await?;
+195    let download_result = TransferResult::new(start.elapsed(), size as u64);
+196
+197    Ok((upload_result, download_result))
+198}
+199
+200#[derive(Default)]
+201struct ClientStats {
+202    upload_stats: Stats,
+203    download_stats: Stats,
+204}
+205
+206impl ClientStats {
+207    fn print(&self, client_id: usize) {
+208        println!();
+209        println!("Client {client_id} stats:");
+210
+211        if self.upload_stats.total_size != 0 {
+212            self.upload_stats.print("upload");
+213        }
+214
+215        if self.download_stats.total_size != 0 {
+216            self.download_stats.print("download");
+217        }
+218    }
+219}
\ No newline at end of file diff --git a/pr/672/docs/src/certificate/certificate.rs.html b/pr/672/docs/src/certificate/certificate.rs.html new file mode 100644 index 000000000..b22a50d14 --- /dev/null +++ b/pr/672/docs/src/certificate/certificate.rs.html @@ -0,0 +1,106 @@ +certificate.rs - source

certificate/
certificate.rs

1use std::{error::Error, sync::Arc};
+2
+3use noq::{
+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 = noq::ServerConfig::with_single_cert(certs, key);
+21    let client_config = noq::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/672/docs/src/data_transfer/data-transfer.rs.html b/pr/672/docs/src/data_transfer/data-transfer.rs.html new file mode 100644 index 000000000..2b1581d24 --- /dev/null +++ b/pr/672/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 noq::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/672/docs/src/noq/connection.rs.html b/pr/672/docs/src/noq/connection.rs.html new file mode 100644 index 000000000..ba06d5aa5 --- /dev/null +++ b/pr/672/docs/src/noq/connection.rs.html @@ -0,0 +1,1911 @@ +connection.rs - source

noq/
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::{
+10        Arc, Weak,
+11        atomic::{AtomicUsize, Ordering},
+12    },
+13    task::{Context, Poll, Waker, ready},
+14};
+15
+16use bytes::Bytes;
+17use pin_project_lite::pin_project;
+18use rustc_hash::FxHashMap;
+19use thiserror::Error;
+20use tokio::sync::{Notify, futures::Notified, mpsc, oneshot, watch};
+21use tracing::{Instrument, Span, debug_span};
+22
+23use crate::{
+24    ConnectionEvent, Duration, Instant, Path, VarInt,
+25    endpoint::ensure_ipv6,
+26    mutex::{Mutex, MutexGuard},
+27    path::{OpenPath, PathRef, PathRefOwner},
+28    recv_stream::RecvStream,
+29    runtime::{AsyncTimer, Runtime, UdpSender},
+30    send_stream::SendStream,
+31    udp_transmit,
+32};
+33use proto::{
+34    ConnectionError, ConnectionHandle, ConnectionStats, Dir, EndpointEvent, FourTuple, PathError,
+35    PathEvent, PathId, PathStats, PathStatus, Side, StreamEvent, StreamId, TransportError,
+36    TransportErrorCode, congestion::Controller, n0_nat_traversal,
+37};
+38
+39/// In-progress connection attempt future
+40#[derive(Debug)]
+41pub struct Connecting {
+42    conn: Option<ConnectionRef>,
+43    connected: oneshot::Receiver<bool>,
+44    handshake_data_ready: Option<oneshot::Receiver<()>>,
+45}
+46
+47impl Connecting {
+48    pub(crate) fn new(
+49        handle: ConnectionHandle,
+50        conn: proto::Connection,
+51        endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+52        conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
+53        sender: Pin<Box<dyn UdpSender>>,
+54        runtime: Arc<dyn Runtime>,
+55    ) -> Self {
+56        let (on_handshake_data_send, on_handshake_data_recv) = oneshot::channel();
+57        let (on_connected_send, on_connected_recv) = oneshot::channel();
+58
+59        let conn = ConnectionRef(Arc::new(Arc::new(ConnectionInner {
+60            state: Mutex::new(State::new(
+61                conn,
+62                handle,
+63                endpoint_events,
+64                conn_events,
+65                on_handshake_data_send,
+66                on_connected_send,
+67                sender,
+68                runtime.clone(),
+69            )),
+70            shared: Shared::default(),
+71        })));
+72
+73        let driver = ConnectionDriver(conn.clone());
+74        runtime.spawn(Box::pin(
+75            async {
+76                if let Err(e) = driver.await {
+77                    tracing::error!("I/O error: {e}");
+78                }
+79            }
+80            .instrument(Span::current()),
+81        ));
+82
+83        Self {
+84            conn: Some(conn),
+85            connected: on_connected_recv,
+86            handshake_data_ready: Some(on_handshake_data_recv),
+87        }
+88    }
+89
+90    /// Convert into a 0-RTT or 0.5-RTT connection at the cost of weakened security
+91    ///
+92    /// Returns `Ok` immediately if the local endpoint is able to attempt sending 0/0.5-RTT data.
+93    /// If so, the returned [`Connection`] can be used to send application data without waiting for
+94    /// the rest of the handshake to complete, at the cost of weakened cryptographic security
+95    /// guarantees. The returned [`ZeroRttAccepted`] future resolves when the handshake does
+96    /// complete, at which point subsequently opened streams and written data will have full
+97    /// cryptographic protection.
+98    ///
+99    /// ## Outgoing
+100    ///
+101    /// For outgoing connections, the initial attempt to convert to a [`Connection`] which sends
+102    /// 0-RTT data will proceed if the [`crypto::ClientConfig`][crate::crypto::ClientConfig]
+103    /// attempts to resume a previous TLS session. However, **the remote endpoint may not actually
+104    /// _accept_ the 0-RTT data**--yet still accept the connection attempt in general. This
+105    /// possibility is conveyed through the [`ZeroRttAccepted`] future--when the handshake
+106    /// completes, it resolves to true if the 0-RTT data was accepted and false if it was rejected.
+107    /// If it was rejected, the existence of streams opened and other application data sent prior
+108    /// to the handshake completing will not be conveyed to the remote application, and local
+109    /// operations on them will return `ZeroRttRejected` errors.
+110    ///
+111    /// A server may reject 0-RTT data at its discretion, but accepting 0-RTT data requires the
+112    /// relevant resumption state to be stored in the server, which servers may limit or lose for
+113    /// various reasons including not persisting resumption state across server restarts.
+114    ///
+115    /// If manually providing a [`crypto::ClientConfig`][crate::crypto::ClientConfig], check your
+116    /// implementation's docs for 0-RTT pitfalls.
+117    ///
+118    /// ## Incoming
+119    ///
+120    /// For incoming connections, conversion to 0.5-RTT will always fully succeed. `into_0rtt` will
+121    /// always return `Ok` and the [`ZeroRttAccepted`] will always resolve to true.
+122    ///
+123    /// If manually providing a [`crypto::ServerConfig`][crate::crypto::ServerConfig], check your
+124    /// implementation's docs for 0-RTT pitfalls.
+125    ///
+126    /// ## Security
+127    ///
+128    /// On outgoing connections, this enables transmission of 0-RTT data, which is vulnerable to
+129    /// replay attacks, and should therefore never invoke non-idempotent operations.
+130    ///
+131    /// On incoming connections, this enables transmission of 0.5-RTT data, which may be sent
+132    /// before TLS client authentication has occurred, and should therefore not be used to send
+133    /// data for which client authentication is being used.
+134    pub fn into_0rtt(mut self) -> Result<(Connection, ZeroRttAccepted), Self> {
+135        // This lock borrows `self` and would normally be dropped at the end of this scope, so we'll
+136        // have to release it explicitly before returning `self` by value.
+137        let conn = (self.conn.as_mut().unwrap()).lock_without_waking("into_0rtt");
+138
+139        let is_ok = conn.inner.has_0rtt() || conn.inner.side().is_server();
+140        drop(conn);
+141
+142        if is_ok {
+143            let conn = self.conn.take().unwrap();
+144            Ok((Connection(conn), ZeroRttAccepted(self.connected)))
+145        } else {
+146            Err(self)
+147        }
+148    }
+149
+150    /// Parameters negotiated during the handshake
+151    ///
+152    /// The dynamic type returned is determined by the configured
+153    /// [`Session`](proto::crypto::Session). For the default `rustls` session, the return value can
+154    /// be [`downcast`](Box::downcast) to a
+155    /// [`crypto::rustls::HandshakeData`](crate::crypto::rustls::HandshakeData).
+156    pub async fn handshake_data(&mut self) -> Result<Box<dyn Any>, ConnectionError> {
+157        // Taking &mut self allows us to use a single oneshot channel rather than dealing with
+158        // potentially many tasks waiting on the same event. It's a bit of a hack, but keeps things
+159        // simple.
+160        if let Some(x) = self.handshake_data_ready.take() {
+161            let _ = x.await;
+162        }
+163        let conn = self.conn.as_ref().unwrap();
+164        let inner = conn.lock_without_waking("handshake");
+165        inner
+166            .inner
+167            .crypto_session()
+168            .handshake_data()
+169            .ok_or_else(|| {
+170                inner
+171                    .error
+172                    .clone()
+173                    .expect("spurious handshake data ready notification")
+174            })
+175    }
+176
+177    /// The local IP address which was used when the peer established
+178    /// the connection
+179    ///
+180    /// This can be different from the address the endpoint is bound to, in case
+181    /// the endpoint is bound to a wildcard address like `0.0.0.0` or `::`.
+182    ///
+183    /// This will return `None` for clients, or when the platform does not expose this
+184    /// information. See [`noq_udp::RecvMeta::dst_ip`](udp::RecvMeta::dst_ip) for a list of
+185    /// supported platforms when using [`noq_udp`](udp) for I/O, which is the default.
+186    ///
+187    /// Will panic if called after `poll` has returned `Ready`.
+188    pub fn local_ip(&self) -> Option<IpAddr> {
+189        let conn = self.conn.as_ref().expect("used after yielding Ready");
+190        let inner = conn.lock_without_waking("local_ip");
+191
+192        inner
+193            .inner
+194            .network_path(PathId::ZERO)
+195            .expect("PathId::ZERO is the only path during the handshake")
+196            .local_ip()
+197    }
+198
+199    /// The peer's UDP addresses
+200    ///
+201    /// Will panic if called after `poll` has returned `Ready`.
+202    pub fn remote_address(&self) -> SocketAddr {
+203        let conn_ref: &ConnectionRef = self.conn.as_ref().expect("used after yielding Ready");
+204        conn_ref
+205            .lock_without_waking("remote_address")
+206            .inner
+207            .network_path(PathId::ZERO)
+208            .expect("PathId::ZERO is the only path during the handshake")
+209            .remote()
+210    }
+211}
+212
+213impl Future for Connecting {
+214    type Output = Result<Connection, ConnectionError>;
+215    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+216        Pin::new(&mut self.connected).poll(cx).map(|_| {
+217            let conn = self.conn.take().unwrap();
+218            let inner = conn.lock_without_waking("connecting");
+219            if inner.connected {
+220                drop(inner);
+221                Ok(Connection(conn))
+222            } else {
+223                Err(inner
+224                    .error
+225                    .clone()
+226                    .expect("connected signaled without connection success or error"))
+227            }
+228        })
+229    }
+230}
+231
+232/// Future that completes when a connection is fully established
+233///
+234/// For clients, the resulting value indicates if 0-RTT was accepted. For servers, the resulting
+235/// value is meaningless.
+236pub struct ZeroRttAccepted(oneshot::Receiver<bool>);
+237
+238impl Future for ZeroRttAccepted {
+239    type Output = bool;
+240    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+241        Pin::new(&mut self.0).poll(cx).map(|x| x.unwrap_or(false))
+242    }
+243}
+244
+245/// A future that drives protocol logic for a connection
+246///
+247/// This future handles the protocol logic for a single connection, routing events from the
+248/// `Connection` API object to the `Endpoint` task and the related stream-related interfaces.
+249/// It also keeps track of outstanding timeouts for the `Connection`.
+250///
+251/// If the connection encounters an error condition, this future will yield an error. It will
+252/// terminate (yielding `Ok(())`) if the connection was closed without error. Unlike other
+253/// connection-related futures, this waits for the draining period to complete to ensure that
+254/// packets still in flight from the peer are handled gracefully.
+255#[must_use = "connection drivers must be spawned for their connections to function"]
+256#[derive(Debug)]
+257struct ConnectionDriver(ConnectionRef);
+258
+259impl Future for ConnectionDriver {
+260    type Output = Result<(), io::Error>;
+261
+262    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+263        let conn = &mut *self.0.lock_without_waking("poll");
+264
+265        let span = debug_span!("drive", id = conn.handle.0);
+266        let _guard = span.enter();
+267
+268        if let Err(e) = conn.process_conn_events(&self.0.shared, cx) {
+269            conn.terminate(e, &self.0.shared);
+270            return Poll::Ready(Ok(()));
+271        }
+272        let mut keep_going = conn.drive_transmit(cx)?;
+273        // If a timer expires, there might be more to transmit. When we transmit something, we
+274        // might need to reset a timer. Hence, we must loop until neither happens.
+275        keep_going |= conn.drive_timer(cx);
+276        conn.forward_endpoint_events();
+277        conn.forward_app_events(&self.0.shared);
+278
+279        if !conn.inner.is_drained() {
+280            if keep_going {
+281                // If the connection hasn't processed all tasks, schedule it again
+282                cx.waker().wake_by_ref();
+283            } else {
+284                conn.driver = Some(cx.waker().clone());
+285            }
+286            return Poll::Pending;
+287        }
+288        if conn.error.is_none() {
+289            unreachable!("drained connections always have an error");
+290        }
+291        Poll::Ready(Ok(()))
+292    }
+293}
+294
+295/// A QUIC connection.
+296///
+297/// If all references to a connection (including every clone of the `Connection` handle, streams of
+298/// incoming streams, and the various stream types) have been dropped, then the connection will be
+299/// automatically closed with an `error_code` of 0 and an empty `reason`. You can also close the
+300/// connection explicitly by calling [`Connection::close()`].
+301///
+302/// Closing the connection immediately abandons efforts to deliver data to the peer.  Upon
+303/// receiving CONNECTION_CLOSE the peer *may* drop any stream data not yet delivered to the
+304/// application. [`Connection::close()`] describes in more detail how to gracefully close a
+305/// connection without losing application data.
+306///
+307/// May be cloned to obtain another handle to the same connection.
+308///
+309/// [`Connection::close()`]: Connection::close
+310#[derive(Debug, Clone)]
+311pub struct Connection(ConnectionRef);
+312
+313impl Connection {
+314    /// Returns a weak reference to the inner connection struct.
+315    pub fn weak_handle(&self) -> WeakConnectionHandle {
+316        self.0.weak_handle()
+317    }
+318
+319    /// Initiate a new outgoing unidirectional stream.
+320    ///
+321    /// Streams are cheap and instantaneous to open unless blocked by flow control. As a
+322    /// consequence, the peer won't be notified that a stream has been opened until the stream is
+323    /// actually used.
+324    pub fn open_uni(&self) -> OpenUni<'_> {
+325        OpenUni {
+326            conn: &self.0,
+327            notify: self.0.shared.stream_budget_available[Dir::Uni as usize].notified(),
+328        }
+329    }
+330
+331    /// Initiate a new outgoing bidirectional stream.
+332    ///
+333    /// Streams are cheap and instantaneous to open unless blocked by flow control. As a
+334    /// consequence, the peer won't be notified that a stream has been opened until the stream is
+335    /// actually used. Calling [`open_bi()`] then waiting on the [`RecvStream`] without writing
+336    /// anything to [`SendStream`] will never succeed.
+337    ///
+338    /// [`open_bi()`]: crate::Connection::open_bi
+339    /// [`SendStream`]: crate::SendStream
+340    /// [`RecvStream`]: crate::RecvStream
+341    pub fn open_bi(&self) -> OpenBi<'_> {
+342        OpenBi {
+343            conn: &self.0,
+344            notify: self.0.shared.stream_budget_available[Dir::Bi as usize].notified(),
+345        }
+346    }
+347
+348    /// Accept the next incoming uni-directional stream
+349    pub fn accept_uni(&self) -> AcceptUni<'_> {
+350        AcceptUni {
+351            conn: &self.0,
+352            notify: self.0.shared.stream_incoming[Dir::Uni as usize].notified(),
+353        }
+354    }
+355
+356    /// Accept the next incoming bidirectional stream
+357    ///
+358    /// **Important Note**: The `Connection` that calls [`open_bi()`] must write to its [`SendStream`]
+359    /// before the other `Connection` is able to `accept_bi()`. Calling [`open_bi()`] then
+360    /// waiting on the [`RecvStream`] without writing anything to [`SendStream`] will never succeed.
+361    ///
+362    /// [`accept_bi()`]: crate::Connection::accept_bi
+363    /// [`open_bi()`]: crate::Connection::open_bi
+364    /// [`SendStream`]: crate::SendStream
+365    /// [`RecvStream`]: crate::RecvStream
+366    pub fn accept_bi(&self) -> AcceptBi<'_> {
+367        AcceptBi {
+368            conn: &self.0,
+369            notify: self.0.shared.stream_incoming[Dir::Bi as usize].notified(),
+370        }
+371    }
+372
+373    /// Receive an application datagram
+374    pub fn read_datagram(&self) -> ReadDatagram<'_> {
+375        ReadDatagram {
+376            conn: &self.0,
+377            notify: self.0.shared.datagram_received.notified(),
+378        }
+379    }
+380
+381    /// Opens a new path if no path exists yet for `network_path`.
+382    ///
+383    /// If `network_path` has no local IP set, then this will open a new path
+384    /// if no path exists for this remote address, independent of the existing
+385    /// path's local IP. If a local IP is set, it will match against the full
+386    /// four-tuple of existing paths.
+387    ///
+388    /// Otherwise behaves exactly as [`open_path`].
+389    ///
+390    /// [`open_path`]: Self::open_path
+391    pub fn open_path_ensure(
+392        &self,
+393        network_path: impl Into<FourTuple>,
+394        initial_status: PathStatus,
+395    ) -> OpenPath {
+396        let network_path = network_path.into();
+397        let mut state = self.0.lock_and_wake("open_path");
+398
+399        let network_path = match normalize_network_path(network_path, &state.inner) {
+400            Ok(network_path) => network_path,
+401            Err(err) => return OpenPath::rejected(err),
+402        };
+403
+404        let now = state.runtime.now();
+405        let open_res = state
+406            .inner
+407            .open_path_ensure(network_path, initial_status, now);
+408        match open_res {
+409            Ok((path_id, existed)) if existed => {
+410                let recv = state.open_path.get(&path_id).map(|tx| tx.subscribe());
+411                drop(state);
+412                match recv {
+413                    Some(recv) => OpenPath::new(path_id, recv, self.0.clone()),
+414                    None => OpenPath::ready(path_id, self.0.clone()),
+415                }
+416            }
+417            Ok((path_id, _)) => {
+418                let (tx, rx) = watch::channel(Ok(()));
+419                state.open_path.insert(path_id, tx);
+420                drop(state);
+421                OpenPath::new(path_id, rx, self.0.clone())
+422            }
+423            Err(err) => OpenPath::rejected(err),
+424        }
+425    }
+426
+427    /// Opens an additional path if the multipath extension is negotiated.
+428    ///
+429    /// This function takes a [`FourTuple`], which contains the remote address and an optional
+430    /// local IP. If the local IP is set, the path will be opened with this source address,
+431    /// and the endpoint must support sending from that IP address. You can also pass a
+432    /// [`SocketAddr`] to only set the remote address and leave the local IP interface unspecified.
+433    ///
+434    /// The returned future completes once the path is either fully opened and ready to
+435    /// carry application data, or if there was an error.
+436    ///
+437    /// Dropping the returned future does not cancel the opening of the path, the
+438    /// [`PathEvent::Established`] event will still be emitted from [`Self::path_events`] if
+439    /// the path opens.  The [`PathId`] for the events can be extracted from
+440    /// [`OpenPath::path_id`].
+441    ///
+442    /// Failure to open a path can either occur immediately, before polling the returned
+443    /// future, or at a later time.  If the failure is immediate [`OpenPath::path_id`] will
+444    /// return `None` and the future will be ready immediately.  If the failure happens
+445    /// later, a [`PathEvent`] will be emitted.
+446    pub fn open_path(
+447        &self,
+448        network_path: impl Into<FourTuple>,
+449        initial_status: PathStatus,
+450    ) -> OpenPath {
+451        let network_path = network_path.into();
+452        let mut state = self.0.lock_and_wake("open_path");
+453
+454        let network_path = match normalize_network_path(network_path, &state.inner) {
+455            Ok(network_path) => network_path,
+456            Err(err) => return OpenPath::rejected(err),
+457        };
+458
+459        let (on_open_path_send, on_open_path_recv) = watch::channel(Ok(()));
+460        let now = state.runtime.now();
+461        let open_res = state.inner.open_path(network_path, initial_status, now);
+462        match open_res {
+463            Ok(path_id) => {
+464                state.open_path.insert(path_id, on_open_path_send);
+465                drop(state);
+466                OpenPath::new(path_id, on_open_path_recv, self.0.clone())
+467            }
+468            Err(err) => OpenPath::rejected(err),
+469        }
+470    }
+471
+472    /// Returns the [`Path`] structure of an open path
+473    pub fn path(&self, id: PathId) -> Option<Path> {
+474        Path::new(&self.0, id)
+475    }
+476
+477    /// A stream of [`PathEvent`]s for all paths in this connection.
+478    ///
+479    /// The stream will yield a [`PathEvent`] whenever there is a change in the state of any path in this connection.
+480    /// The events need to be processed immediately, since there isn't an unbounded buffer for them.
+481    ///
+482    /// If processing of events lags behind too much, you will get an error of type [`crate::Lagged`] indicating
+483    /// how many events were lost. The stream continues after a lag, delivering the oldest retained message next.
+484    pub fn path_events(&self) -> crate::PathEvents {
+485        crate::PathEvents::new(
+486            self.0
+487                .lock_without_waking("path_events")
+488                .path_events
+489                .subscribe(),
+490        )
+491    }
+492
+493    /// A stream of NAT traversal updates for this connection.
+494    ///
+495    /// The events need to be processed immediately, since there isn't an unbounded buffer for them.
+496    ///
+497    /// If processing of events lags behind too much, you will get an error of type [`crate::Lagged`] indicating
+498    /// how many events were lost. The stream continues after a lag, delivering the oldest retained message next.
+499    pub fn nat_traversal_updates(&self) -> crate::NatTraversalUpdates {
+500        crate::NatTraversalUpdates::new(
+501            self.0
+502                .lock_without_waking("nat_traversal_updates")
+503                .nat_traversal_updates
+504                .subscribe(),
+505        )
+506    }
+507
+508    /// Wait for the connection to be closed for any reason
+509    ///
+510    /// Despite the return type's name, closed connections are often not an error condition at the
+511    /// application layer. Cases that might be routine include [`ConnectionError::LocallyClosed`]
+512    /// and [`ConnectionError::ApplicationClosed`].
+513    pub async fn closed(&self) -> ConnectionError {
+514        {
+515            let conn = self.0.lock_without_waking("closed");
+516            if let Some(error) = conn.error.as_ref() {
+517                return error.clone();
+518            }
+519            // Construct the future while the lock is held to ensure we can't miss a wakeup if
+520            // the `Notify` is signaled immediately after we release the lock. `await` it after
+521            // the lock guard is out of scope.
+522            self.0.shared.closed.notified()
+523        }
+524        .await;
+525        self.0
+526            .lock_without_waking("closed")
+527            .error
+528            .as_ref()
+529            .expect("closed without an error")
+530            .clone()
+531    }
+532
+533    /// Wait for the connection to be closed without keeping a strong reference to the connection
+534    ///
+535    /// Returns a future that resolves, once the connection is closed, to a [`Closed`] struct
+536    /// describing the close reason and final connection and per-path statistics.
+537    ///
+538    /// Calling [`Self::closed`] keeps the connection alive until it is either closed locally via [`Connection::close`]
+539    /// or closed by the remote peer. This function instead does not keep the connection itself alive,
+540    /// so if all *other* clones of the connection are dropped, the connection will be closed implicitly even
+541    /// if there are futures returned from this function still being awaited.
+542    pub fn on_closed(&self) -> OnClosed {
+543        let (tx, rx) = oneshot::channel();
+544        let mut state = self.0.lock_without_waking("on_closed");
+545        if let Some(reason) = state.error.clone() {
+546            // Connection already closed, send immediately
+547            let _ = tx.send(Closed::new(&mut state, reason));
+548        } else {
+549            state.on_closed.push(tx);
+550        }
+551        drop(state);
+552        OnClosed {
+553            conn: self.weak_handle(),
+554            rx,
+555        }
+556    }
+557
+558    /// Whether the connection is closed, and why.
+559    ///
+560    /// The close_reason is always set to `Some(ConnectionError)` when a socket is
+561    /// closed; whether it was closed manually by calling [`Connection::close()`] or due to
+562    /// an internal error (such as an idle timeout or the peer closing the
+563    /// connection).
+564    ///
+565    /// Note: when the connection is closed, `connection.close_reason().is_some()` will always be true.
+566    pub fn close_reason(&self) -> Option<ConnectionError> {
+567        self.0.lock_without_waking("close_reason").error.clone()
+568    }
+569
+570    /// Close the connection immediately.
+571    ///
+572    /// Pending operations will fail immediately with [`ConnectionError::LocallyClosed`]. No
+573    /// more data is sent to the peer and the peer may drop buffered data upon receiving
+574    /// the CONNECTION_CLOSE frame.
+575    ///
+576    /// `error_code` and `reason` are not interpreted, and are provided directly to the peer.
+577    ///
+578    /// `reason` will be truncated to fit in a single packet with overhead; to improve odds that it
+579    /// is preserved in full, it should be kept under 1KiB.
+580    ///
+581    /// # Gracefully closing a connection
+582    ///
+583    /// Only the peer last receiving application data can be certain that all data is
+584    /// delivered. The only reliable action it can then take is to close the connection,
+585    /// potentially with a custom error code. The delivery of the final CONNECTION_CLOSE
+586    /// frame is very likely if both endpoints stay online long enough, and
+587    /// [`Endpoint::wait_idle()`] can be used to provide sufficient time. Otherwise, the
+588    /// remote peer will time out the connection, provided that the idle timeout is not
+589    /// disabled.
+590    ///
+591    /// The sending side can not guarantee all stream data is delivered to the remote
+592    /// application. It only knows the data is delivered to the QUIC stack of the remote
+593    /// endpoint. Once the local side sends a CONNECTION_CLOSE frame in response to calling
+594    /// [`close()`] the remote endpoint may drop any data it received but is as yet
+595    /// undelivered to the application, including data that was acknowledged as received to
+596    /// the local endpoint.
+597    ///
+598    /// [`ConnectionError::LocallyClosed`]: crate::ConnectionError::LocallyClosed
+599    /// [`Endpoint::wait_idle()`]: crate::Endpoint::wait_idle
+600    /// [`close()`]: Connection::close
+601    pub fn close(&self, error_code: VarInt, reason: &[u8]) {
+602        let conn = &mut *self.0.lock_without_waking("close"); // conn.close self-wakes
+603        conn.close(error_code, Bytes::copy_from_slice(reason), &self.0.shared);
+604    }
+605
+606    /// Wait for the handshake to be confirmed.
+607    ///
+608    /// As a server, who must be authenticated by clients,
+609    /// this happens when the handshake completes
+610    /// upon receiving a TLS Finished message from the client.
+611    /// In return, the server send a HANDSHAKE_DONE frame.
+612    ///
+613    /// As a client, this happens when receiving a HANDSHAKE_DONE frame.
+614    /// At this point, the server has either accepted our authentication,
+615    /// or, if client authentication is not required, accepted our lack of authentication.
+616    pub async fn handshake_confirmed(&self) -> Result<(), ConnectionError> {
+617        {
+618            let conn = self.0.lock_without_waking("handshake_confirmed");
+619            if let Some(error) = conn.error.as_ref() {
+620                return Err(error.clone());
+621            }
+622            if conn.handshake_confirmed {
+623                return Ok(());
+624            }
+625            // Construct the future while the lock is held to ensure we can't miss a wakeup if
+626            // the `Notify` is signaled immediately after we release the lock. `await` it after
+627            // the lock guard is out of scope.
+628            self.0.shared.handshake_confirmed.notified()
+629        }
+630        .await;
+631        if let Some(error) = self
+632            .0
+633            .lock_without_waking("handshake_confirmed")
+634            .error
+635            .as_ref()
+636        {
+637            Err(error.clone())
+638        } else {
+639            Ok(())
+640        }
+641    }
+642
+643    /// Transmit `data` as an unreliable, unordered application datagram
+644    ///
+645    /// Application datagrams are a low-level primitive. They may be lost or delivered out of order,
+646    /// and `data` must both fit inside a single QUIC packet and be smaller than the maximum
+647    /// dictated by the peer.
+648    ///
+649    /// Previously queued datagrams which are still unsent may be discarded to make space for this
+650    /// datagram, in order of oldest to newest.
+651    pub fn send_datagram(&self, data: Bytes) -> Result<(), SendDatagramError> {
+652        let conn = &mut *self.0.lock_and_wake("send_datagram");
+653        if let Some(ref x) = conn.error {
+654            return Err(SendDatagramError::ConnectionLost(x.clone()));
+655        }
+656        use proto::SendDatagramError::*;
+657        match conn.inner.datagrams().send(data, true) {
+658            Ok(()) => Ok(()),
+659            Err(e) => Err(match e {
+660                Blocked(..) => unreachable!(),
+661                UnsupportedByPeer => SendDatagramError::UnsupportedByPeer,
+662                Disabled => SendDatagramError::Disabled,
+663                TooLarge => SendDatagramError::TooLarge,
+664            }),
+665        }
+666    }
+667
+668    /// Transmit `data` as an unreliable, unordered application datagram
+669    ///
+670    /// Unlike [`send_datagram()`], this method will wait for buffer space during congestion
+671    /// conditions, which effectively prioritizes old datagrams over new datagrams.
+672    ///
+673    /// See [`send_datagram()`] for details.
+674    ///
+675    /// [`send_datagram()`]: Connection::send_datagram
+676    pub fn send_datagram_wait(&self, data: Bytes) -> SendDatagram<'_> {
+677        SendDatagram {
+678            conn: &self.0,
+679            data: Some(data),
+680            notify: self.0.shared.datagrams_unblocked.notified(),
+681        }
+682    }
+683
+684    /// Compute the maximum size of datagrams that may be passed to [`send_datagram()`].
+685    ///
+686    /// Returns `None` if datagrams are unsupported by the peer or disabled locally.
+687    ///
+688    /// This may change over the lifetime of a connection according to variation in the path MTU
+689    /// estimate. The peer can also enforce an arbitrarily small fixed limit, but if the peer's
+690    /// limit is large this is guaranteed to be a little over a kilobyte at minimum.
+691    ///
+692    /// Not necessarily the maximum size of received datagrams.
+693    ///
+694    /// [`send_datagram()`]: Connection::send_datagram
+695    pub fn max_datagram_size(&self) -> Option<usize> {
+696        self.0
+697            .lock_without_waking("max_datagram_size")
+698            .inner
+699            .datagrams()
+700            .max_size()
+701    }
+702
+703    /// Bytes available in the outgoing datagram buffer
+704    ///
+705    /// When greater than zero, calling [`send_datagram()`](Self::send_datagram) with a datagram of
+706    /// at most this size is guaranteed not to cause older datagrams to be dropped.
+707    pub fn datagram_send_buffer_space(&self) -> usize {
+708        self.0
+709            .lock_without_waking("datagram_send_buffer_space")
+710            .inner
+711            .datagrams()
+712            .send_buffer_space()
+713    }
+714
+715    /// The side of the connection (client or server)
+716    pub fn side(&self) -> Side {
+717        self.0.lock_without_waking("side").inner.side()
+718    }
+719
+720    /// Current best estimate of this connection's latency (round-trip-time)
+721    pub fn rtt(&self, path_id: PathId) -> Option<Duration> {
+722        self.0.lock_without_waking("rtt").inner.rtt(path_id)
+723    }
+724
+725    /// Returns connection statistics
+726    pub fn stats(&self) -> ConnectionStats {
+727        self.0.lock_without_waking("stats").inner.stats()
+728    }
+729
+730    /// Returns path statistics
+731    pub fn path_stats(&self, path_id: PathId) -> Option<PathStats> {
+732        self.0.lock_without_waking("path_stats").path_stats(path_id)
+733    }
+734
+735    /// Current state of the congestion control algorithm, for debugging purposes
+736    pub fn congestion_state(&self, path_id: PathId) -> Option<Box<dyn Controller>> {
+737        self.0
+738            .lock_without_waking("congestion_state")
+739            .inner
+740            .congestion_state(path_id)
+741            .map(|c| c.clone_box())
+742    }
+743
+744    /// Parameters negotiated during the handshake
+745    ///
+746    /// Guaranteed to return `Some` on fully established connections or after
+747    /// [`Connecting::handshake_data()`] succeeds. See that method's documentations for details on
+748    /// the returned value.
+749    ///
+750    /// [`Connection::handshake_data()`]: crate::Connecting::handshake_data
+751    pub fn handshake_data(&self) -> Option<Box<dyn Any>> {
+752        self.0
+753            .lock_without_waking("handshake_data")
+754            .inner
+755            .crypto_session()
+756            .handshake_data()
+757    }
+758
+759    /// Cryptographic identity of the peer
+760    ///
+761    /// The dynamic type returned is determined by the configured
+762    /// [`Session`](proto::crypto::Session). For the default `rustls` session, the return value can
+763    /// be [`downcast`](Box::downcast) to a <code>Vec<[rustls::pki_types::CertificateDer]></code>
+764    pub fn peer_identity(&self) -> Option<Box<dyn Any>> {
+765        self.0
+766            .lock_without_waking("peer_identity")
+767            .inner
+768            .crypto_session()
+769            .peer_identity()
+770    }
+771
+772    /// A stable identifier for this connection
+773    ///
+774    /// Peer addresses and connection IDs can change, but this value will remain
+775    /// fixed for the lifetime of the connection.
+776    pub fn stable_id(&self) -> usize {
+777        self.0.stable_id()
+778    }
+779
+780    /// Update traffic keys spontaneously
+781    ///
+782    /// This primarily exists for testing purposes.
+783    pub fn force_key_update(&self) {
+784        self.0
+785            .lock_and_wake("force_key_update")
+786            .inner
+787            .force_key_update()
+788    }
+789
+790    /// Derive keying material from this connection's TLS session secrets.
+791    ///
+792    /// When both peers call this method with the same `label` and `context`
+793    /// arguments and `output` buffers of equal length, they will get the
+794    /// same sequence of bytes in `output`. These bytes are cryptographically
+795    /// strong and pseudorandom, and are suitable for use as keying material.
+796    ///
+797    /// See [RFC5705](https://tools.ietf.org/html/rfc5705) for more information.
+798    pub fn export_keying_material(
+799        &self,
+800        output: &mut [u8],
+801        label: &[u8],
+802        context: &[u8],
+803    ) -> Result<(), proto::crypto::ExportKeyingMaterialError> {
+804        self.0
+805            .lock_without_waking("export_keying_material")
+806            .inner
+807            .crypto_session()
+808            .export_keying_material(output, label, context)
+809    }
+810
+811    /// Modify the number of remotely initiated unidirectional streams that may be concurrently open
+812    ///
+813    /// No streams may be opened by the peer unless fewer than `count` are already open. Large
+814    /// `count`s increase both minimum and worst-case memory consumption.
+815    pub fn set_max_concurrent_uni_streams(&self, count: VarInt) {
+816        let mut conn = self.0.lock_and_wake("set_max_concurrent_uni_streams");
+817        conn.inner.set_max_concurrent_streams(Dir::Uni, count);
+818    }
+819
+820    /// See [`proto::TransportConfig::send_window()`]
+821    pub fn set_send_window(&self, send_window: u64) {
+822        let mut conn = self.0.lock_and_wake("set_send_window");
+823        conn.inner.set_send_window(send_window);
+824    }
+825
+826    /// See [`proto::TransportConfig::receive_window()`]
+827    pub fn set_receive_window(&self, receive_window: VarInt) {
+828        let mut conn = self.0.lock_and_wake("set_receive_window");
+829        conn.inner.set_receive_window(receive_window);
+830    }
+831
+832    /// Modify the number of remotely initiated bidirectional streams that may be concurrently open
+833    ///
+834    /// No streams may be opened by the peer unless fewer than `count` are already open. Large
+835    /// `count`s increase both minimum and worst-case memory consumption.
+836    pub fn set_max_concurrent_bi_streams(&self, count: VarInt) {
+837        let mut conn = self.0.lock_and_wake("set_max_concurrent_bi_streams");
+838        conn.inner.set_max_concurrent_streams(Dir::Bi, count);
+839    }
+840
+841    /// Track changes on our external address as reported by the peer.
+842    pub fn observed_external_addr(&self) -> crate::ObservedExternalAddr {
+843        let conn = self.0.lock_without_waking("external_addr");
+844        crate::ObservedExternalAddr::new(conn.observed_external_addr.subscribe())
+845    }
+846
+847    /// Is multipath enabled?
+848    // TODO(flub): not a useful API, once we do real things with multipath we can remove
+849    // this again.
+850    pub fn is_multipath_enabled(&self) -> bool {
+851        let conn = self.0.lock_without_waking("is_multipath_enabled");
+852        conn.inner.is_multipath_negotiated()
+853    }
+854
+855    /// Registers one address at which this endpoint might be reachable
+856    ///
+857    /// When the NAT traversal extension is negotiated, servers send these addresses to clients in
+858    /// `ADD_ADDRESS` frames. This allows clients to obtain server address candidates to initiate
+859    /// NAT traversal attempts. Clients provide their own reachable addresses in `REACH_OUT` frames
+860    /// when [`Self::initiate_nat_traversal_round`] is called.
+861    pub fn add_nat_traversal_address(
+862        &self,
+863        address: SocketAddr,
+864    ) -> Result<(), n0_nat_traversal::Error> {
+865        let mut conn = self.0.lock_and_wake("add_nat_traversal_addresses");
+866        conn.inner.add_nat_traversal_address(address)
+867    }
+868
+869    /// Removes one or more addresses from the set of addresses at which this endpoint is reachable
+870    ///
+871    /// When the NAT traversal extension is negotiated, servers send address removals to
+872    /// clients in `REMOVE_ADDRESS` frames. This allows clients to stop using outdated
+873    /// server address candidates that are no longer valid for NAT traversal.
+874    ///
+875    /// For clients, removed addresses will no longer be advertised in `REACH_OUT` frames.
+876    ///
+877    /// Addresses not present in the set will be silently ignored.
+878    pub fn remove_nat_traversal_address(
+879        &self,
+880        address: SocketAddr,
+881    ) -> Result<(), n0_nat_traversal::Error> {
+882        let mut conn = self.0.lock_and_wake("remove_nat_traversal_addresses");
+883        conn.inner.remove_nat_traversal_address(address)
+884    }
+885
+886    /// Get the current local nat traversal addresses
+887    pub fn get_local_nat_traversal_addresses(
+888        &self,
+889    ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> {
+890        let conn = self
+891            .0
+892            .lock_without_waking("get_local_nat_traversal_addresses");
+893        conn.inner.get_local_nat_traversal_addresses()
+894    }
+895
+896    /// Get the currently advertised nat traversal addresses by the server
+897    pub fn get_remote_nat_traversal_addresses(
+898        &self,
+899    ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> {
+900        let conn = self
+901            .0
+902            .lock_without_waking("get_remote_nat_traversal_addresses");
+903        conn.inner.get_remote_nat_traversal_addresses()
+904    }
+905
+906    /// Initiates a new nat traversal round
+907    ///
+908    /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT`
+909    /// frames, and initiating probing of the known remote addresses. When a new round is
+910    /// initiated, the previous one is cancelled, and paths that have not been opened are closed.
+911    ///
+912    /// Returns the server addresses that are now being probed.
+913    pub fn initiate_nat_traversal_round(&self) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> {
+914        let mut conn = self.0.lock_and_wake("initiate_nat_traversal_round");
+915        let now = conn.runtime.now();
+916        conn.inner.initiate_nat_traversal_round(now)
+917    }
+918}
+919
+920/// Normalizes a [`FourTuple`] against the connection's address family.
+921///
+922/// If the connection already uses IPv6 paths, the remote is canonicalised via
+923/// [`ensure_ipv6`]. If it uses IPv4 and the requested remote is IPv6, this returns
+924/// [`PathError::InvalidRemoteAddress`].
+925fn normalize_network_path(
+926    network_path: FourTuple,
+927    conn: &proto::Connection,
+928) -> Result<FourTuple, PathError> {
+929    // If endpoint::State::ipv6 is true we want to keep all our IP addresses as IPv6.
+930    // If not, we do not support IPv6.  We can not access endpoint::State from here
+931    // however, but either all our paths use an IPv6 address, or all our paths use an
+932    // IPv4 address.  So we can use that information.
+933    let ipv6 = conn
+934        .paths()
+935        .iter()
+936        .filter_map(|id| {
+937            conn.network_path(*id)
+938                .map(|addrs| addrs.remote().is_ipv6())
+939                .ok()
+940        })
+941        .next()
+942        .unwrap_or_default();
+943    let remote = network_path.remote();
+944    if remote.is_ipv6() && !ipv6 {
+945        Err(PathError::InvalidRemoteAddress(remote))
+946    } else if ipv6 {
+947        let remote = SocketAddr::V6(ensure_ipv6(remote));
+948        Ok(FourTuple::new(remote, network_path.local_ip()))
+949    } else {
+950        Ok(network_path)
+951    }
+952}
+953
+954pin_project! {
+955    /// Future produced by [`Connection::open_uni`]
+956    pub struct OpenUni<'a> {
+957        conn: &'a ConnectionRef,
+958        #[pin]
+959        notify: Notified<'a>,
+960    }
+961}
+962
+963impl Future for OpenUni<'_> {
+964    type Output = Result<SendStream, ConnectionError>;
+965    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+966        let this = self.project();
+967        let (conn, id, is_0rtt) = ready!(poll_open(ctx, this.conn, this.notify, Dir::Uni))?;
+968        Poll::Ready(Ok(SendStream::new(conn, id, is_0rtt)))
+969    }
+970}
+971
+972pin_project! {
+973    /// Future produced by [`Connection::open_bi`]
+974    pub struct OpenBi<'a> {
+975        conn: &'a ConnectionRef,
+976        #[pin]
+977        notify: Notified<'a>,
+978    }
+979}
+980
+981impl Future for OpenBi<'_> {
+982    type Output = Result<(SendStream, RecvStream), ConnectionError>;
+983    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+984        let this = self.project();
+985        let (conn, id, is_0rtt) = ready!(poll_open(ctx, this.conn, this.notify, Dir::Bi))?;
+986
+987        Poll::Ready(Ok((
+988            SendStream::new(conn.clone(), id, is_0rtt),
+989            RecvStream::new(conn, id, is_0rtt),
+990        )))
+991    }
+992}
+993
+994fn poll_open<'a>(
+995    ctx: &mut Context<'_>,
+996    conn: &'a ConnectionRef,
+997    mut notify: Pin<&mut Notified<'a>>,
+998    dir: Dir,
+999) -> Poll<Result<(ConnectionRef, StreamId, bool), ConnectionError>> {
+1000    let mut state = conn.lock_without_waking("poll_open");
+1001    if let Some(ref e) = state.error {
+1002        return Poll::Ready(Err(e.clone()));
+1003    } else if let Some(id) = state.inner.streams().open(dir) {
+1004        let is_0rtt = state.inner.side().is_client() && state.inner.is_handshaking();
+1005        drop(state); // Release the lock so clone can take it
+1006        return Poll::Ready(Ok((conn.clone(), id, is_0rtt)));
+1007    }
+1008    loop {
+1009        match notify.as_mut().poll(ctx) {
+1010            // `state` lock ensures we didn't race with readiness
+1011            Poll::Pending => return Poll::Pending,
+1012            // Spurious wakeup, get a new future
+1013            Poll::Ready(()) => {
+1014                notify.set(conn.shared.stream_budget_available[dir as usize].notified())
+1015            }
+1016        }
+1017    }
+1018}
+1019
+1020pin_project! {
+1021    /// Future produced by [`Connection::accept_uni`]
+1022    pub struct AcceptUni<'a> {
+1023        conn: &'a ConnectionRef,
+1024        #[pin]
+1025        notify: Notified<'a>,
+1026    }
+1027}
+1028
+1029impl Future for AcceptUni<'_> {
+1030    type Output = Result<RecvStream, ConnectionError>;
+1031
+1032    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1033        let this = self.project();
+1034        let (conn, id, is_0rtt) = ready!(poll_accept(ctx, this.conn, this.notify, Dir::Uni))?;
+1035        Poll::Ready(Ok(RecvStream::new(conn, id, is_0rtt)))
+1036    }
+1037}
+1038
+1039pin_project! {
+1040    /// Future produced by [`Connection::accept_bi`]
+1041    pub struct AcceptBi<'a> {
+1042        conn: &'a ConnectionRef,
+1043        #[pin]
+1044        notify: Notified<'a>,
+1045    }
+1046}
+1047
+1048impl Future for AcceptBi<'_> {
+1049    type Output = Result<(SendStream, RecvStream), ConnectionError>;
+1050
+1051    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1052        let this = self.project();
+1053        let (conn, id, is_0rtt) = ready!(poll_accept(ctx, this.conn, this.notify, Dir::Bi))?;
+1054        Poll::Ready(Ok((
+1055            SendStream::new(conn.clone(), id, is_0rtt),
+1056            RecvStream::new(conn, id, is_0rtt),
+1057        )))
+1058    }
+1059}
+1060
+1061fn poll_accept<'a>(
+1062    ctx: &mut Context<'_>,
+1063    conn: &'a ConnectionRef,
+1064    mut notify: Pin<&mut Notified<'a>>,
+1065    dir: Dir,
+1066) -> Poll<Result<(ConnectionRef, StreamId, bool), ConnectionError>> {
+1067    let mut state = conn.lock_and_wake("poll_accept");
+1068    // Check for incoming streams before checking `state.error` so that already-received streams,
+1069    // which are necessarily finite, can be drained from a closed connection.
+1070    if let Some(id) = state.inner.streams().accept(dir) {
+1071        let is_0rtt = state.inner.is_handshaking();
+1072        drop(state); // Release the lock (wake on drop) so clone can take it
+1073        return Poll::Ready(Ok((conn.clone(), id, is_0rtt)));
+1074    } else if let Some(ref e) = state.error {
+1075        return Poll::Ready(Err(e.clone()));
+1076    }
+1077    loop {
+1078        match notify.as_mut().poll(ctx) {
+1079            // `state` lock ensures we didn't race with readiness
+1080            Poll::Pending => return Poll::Pending,
+1081            // Spurious wakeup, get a new future
+1082            Poll::Ready(()) => notify.set(conn.shared.stream_incoming[dir as usize].notified()),
+1083        }
+1084    }
+1085}
+1086
+1087pin_project! {
+1088    /// Future produced by [`Connection::read_datagram`]
+1089    pub struct ReadDatagram<'a> {
+1090        conn: &'a ConnectionRef,
+1091        #[pin]
+1092        notify: Notified<'a>,
+1093    }
+1094}
+1095
+1096impl Future for ReadDatagram<'_> {
+1097    type Output = Result<Bytes, ConnectionError>;
+1098    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1099        let mut this = self.project();
+1100        let mut state = this.conn.lock_without_waking("ReadDatagram::poll");
+1101        // Check for buffered datagrams before checking `state.error` so that already-received
+1102        // datagrams, which are necessarily finite, can be drained from a closed connection.
+1103        if let Some(x) = state.inner.datagrams().recv() {
+1104            return Poll::Ready(Ok(x));
+1105        } else if let Some(ref e) = state.error {
+1106            return Poll::Ready(Err(e.clone()));
+1107        }
+1108        loop {
+1109            match this.notify.as_mut().poll(ctx) {
+1110                // `state` lock ensures we didn't race with readiness
+1111                Poll::Pending => return Poll::Pending,
+1112                // Spurious wakeup, get a new future
+1113                Poll::Ready(()) => this
+1114                    .notify
+1115                    .set(this.conn.shared.datagram_received.notified()),
+1116            }
+1117        }
+1118    }
+1119}
+1120
+1121pin_project! {
+1122    /// Future produced by [`Connection::send_datagram_wait`]
+1123    pub struct SendDatagram<'a> {
+1124        conn: &'a ConnectionRef,
+1125        data: Option<Bytes>,
+1126        #[pin]
+1127        notify: Notified<'a>,
+1128    }
+1129}
+1130
+1131impl Future for SendDatagram<'_> {
+1132    type Output = Result<(), SendDatagramError>;
+1133    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1134        let mut this = self.project();
+1135        let mut state = this.conn.lock_and_wake("SendDatagram::poll");
+1136        if let Some(ref e) = state.error {
+1137            return Poll::Ready(Err(SendDatagramError::ConnectionLost(e.clone())));
+1138        }
+1139        use proto::SendDatagramError::*;
+1140        match state
+1141            .inner
+1142            .datagrams()
+1143            .send(this.data.take().unwrap(), false)
+1144        {
+1145            Ok(()) => Poll::Ready(Ok(())),
+1146            Err(e) => Poll::Ready(Err(match e {
+1147                Blocked(data) => {
+1148                    this.data.replace(data);
+1149                    loop {
+1150                        match this.notify.as_mut().poll(ctx) {
+1151                            Poll::Pending => return Poll::Pending,
+1152                            // Spurious wakeup, get a new future
+1153                            Poll::Ready(()) => this
+1154                                .notify
+1155                                .set(this.conn.shared.datagrams_unblocked.notified()),
+1156                        }
+1157                    }
+1158                }
+1159                UnsupportedByPeer => SendDatagramError::UnsupportedByPeer,
+1160                Disabled => SendDatagramError::Disabled,
+1161                TooLarge => SendDatagramError::TooLarge,
+1162            })),
+1163        }
+1164    }
+1165}
+1166
+1167/// State of a [`Connection`] at the moment it was closed.
+1168///
+1169/// Returned by the [`OnClosed`] future from [`Connection::on_closed`].
+1170#[derive(Debug, Clone)]
+1171#[non_exhaustive]
+1172pub struct Closed {
+1173    /// The reason the connection was closed.
+1174    pub reason: ConnectionError,
+1175    /// Aggregate connection statistics at the moment of close.
+1176    pub stats: ConnectionStats,
+1177    /// Per-path statistics for every path the connection knew about at close time.
+1178    ///
+1179    /// This includes paths that haven't been discarded at close time, plus any
+1180    /// already-discarded paths whose final stats had been retained because a [`Path`]
+1181    /// or [`WeakPathHandle`] handle was kept alive.
+1182    ///
+1183    /// [`WeakPathHandle`]: crate::WeakPathHandle
+1184    pub path_stats: Vec<(PathId, PathStats)>,
+1185}
+1186
+1187impl Closed {
+1188    /// Snapshot the current connection state into a [`Closed`] value.
+1189    ///
+1190    /// Must only be called once `state.error` has been set.
+1191    pub(crate) fn new(state: &mut State, reason: ConnectionError) -> Self {
+1192        let stats = state.inner.stats();
+1193
+1194        let non_discarded_paths = state.inner.paths();
+1195        let mut path_stats =
+1196            Vec::with_capacity(non_discarded_paths.len() + state.final_path_stats.len());
+1197
+1198        // Non-discarded paths are tracked by proto::Connection.
+1199        path_stats.extend(
+1200            non_discarded_paths
+1201                .into_iter()
+1202                .filter_map(|id| state.inner.path_stats(id).map(|stats| (id, stats))),
+1203        );
+1204        // Already-discarded paths whose final stats we kept around.
+1205        path_stats.extend(
+1206            state
+1207                .final_path_stats
+1208                .iter()
+1209                .map(|(id, stats)| (*id, *stats)),
+1210        );
+1211        Self {
+1212            reason,
+1213            stats,
+1214            path_stats,
+1215        }
+1216    }
+1217}
+1218
+1219/// Future returned by [`Connection::on_closed`]
+1220///
+1221/// Resolves to [`Closed`].
+1222pub struct OnClosed {
+1223    rx: oneshot::Receiver<Closed>,
+1224    conn: WeakConnectionHandle,
+1225}
+1226
+1227impl Drop for OnClosed {
+1228    fn drop(&mut self) {
+1229        if self.rx.is_terminated() {
+1230            return;
+1231        };
+1232        if let Some(conn) = self.conn.upgrade() {
+1233            self.rx.close();
+1234            conn.0
+1235                .lock_without_waking("OnClosed::drop")
+1236                .on_closed
+1237                .retain(|tx| !tx.is_closed());
+1238        }
+1239    }
+1240}
+1241
+1242impl Future for OnClosed {
+1243    type Output = Closed;
+1244
+1245    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+1246        let this = self.get_mut();
+1247        // The `expect` is safe because `State::drop` ensures that all senders are triggered
+1248        // before being dropped.
+1249        Pin::new(&mut this.rx)
+1250            .poll(cx)
+1251            .map(|x| x.expect("on_close sender is never dropped before sending"))
+1252    }
+1253}
+1254
+1255#[derive(Debug)]
+1256#[allow(clippy::redundant_allocation)]
+1257pub(crate) struct ConnectionRef(Arc<Arc<ConnectionInner>>);
+1258
+1259impl ConnectionRef {
+1260    #[allow(clippy::redundant_allocation)]
+1261    fn from_arc(inner: Arc<Arc<ConnectionInner>>) -> Self {
+1262        inner.shared.ref_count.fetch_add(1, Ordering::Relaxed);
+1263        Self(inner)
+1264    }
+1265
+1266    pub(crate) fn stable_id(&self) -> usize {
+1267        &*self.0 as *const _ as usize
+1268    }
+1269
+1270    pub(crate) fn weak_handle(&self) -> WeakConnectionHandle {
+1271        WeakConnectionHandle(Arc::downgrade(&self.0))
+1272    }
+1273}
+1274
+1275impl Clone for ConnectionRef {
+1276    fn clone(&self) -> Self {
+1277        Self::from_arc(Arc::clone(&self.0))
+1278    }
+1279}
+1280
+1281impl Drop for ConnectionRef {
+1282    fn drop(&mut self) {
+1283        if self.shared.ref_count.fetch_sub(1, Ordering::Relaxed) > 1 {
+1284            return;
+1285        }
+1286
+1287        let conn = &mut *self.lock_without_waking("drop");
+1288
+1289        if !conn.inner.is_closed() {
+1290            // If the driver is alive, it's just it and us, so we'd better shut it down. If it's
+1291            // not, we can't do any harm. If there were any streams being opened, then either
+1292            // the connection will be closed for an unrelated reason or a fresh reference will
+1293            // be constructed for the newly opened stream.
+1294            conn.implicit_close(&self.shared);
+1295        }
+1296    }
+1297}
+1298
+1299impl std::ops::Deref for ConnectionRef {
+1300    type Target = ConnectionInner;
+1301    fn deref(&self) -> &Self::Target {
+1302        &self.0
+1303    }
+1304}
+1305
+1306#[derive(Debug)]
+1307pub(crate) struct ConnectionInner {
+1308    /// Kept private intentionally, use [`Self::lock_and_wake`].
+1309    state: Mutex<State>,
+1310    pub(crate) shared: Shared,
+1311}
+1312
+1313impl ConnectionInner {
+1314    /// Lock the state and return a guard that wakes the connection driver on drop.
+1315    ///
+1316    /// Use this for operations that may queue frames. The wake ensures the driver sends queued frames.
+1317    /// If that's not needed, use [`Self::lock_without_waking`].
+1318    pub(crate) fn lock_and_wake(&self, purpose: &'static str) -> WakeGuard<'_> {
+1319        WakeGuard {
+1320            guard: self.state.lock(purpose),
+1321            wake: true,
+1322        }
+1323    }
+1324
+1325    /// Lock the state and return a guard that unlocks once dropped.
+1326    ///
+1327    /// Use this for operations that don't require any action from the connection driver.
+1328    /// Otherwise, use [`Self::lock_and_wake`] instead.
+1329    pub(crate) fn lock_without_waking(&self, purpose: &'static str) -> WakeGuard<'_> {
+1330        WakeGuard {
+1331            guard: self.state.lock(purpose),
+1332            wake: false,
+1333        }
+1334    }
+1335}
+1336
+1337/// [`MutexGuard`] wrapper that calls [`State::wake`] on drop.
+1338#[derive(derive_more::Deref, derive_more::DerefMut)]
+1339pub(crate) struct WakeGuard<'a> {
+1340    #[deref]
+1341    #[deref_mut]
+1342    guard: MutexGuard<'a, State>,
+1343    wake: bool,
+1344}
+1345
+1346impl WakeGuard<'_> {
+1347    pub(crate) fn skip_waking(&mut self) {
+1348        self.wake = false;
+1349    }
+1350}
+1351
+1352impl Drop for WakeGuard<'_> {
+1353    fn drop(&mut self) {
+1354        if self.wake {
+1355            self.guard.wake();
+1356        }
+1357    }
+1358}
+1359
+1360/// A handle to some connection internals, use with care.
+1361///
+1362/// This contains a weak reference to the connection so will not itself keep the connection
+1363/// alive.
+1364#[derive(Debug, Clone)]
+1365pub struct WeakConnectionHandle(Weak<Arc<ConnectionInner>>);
+1366
+1367impl WeakConnectionHandle {
+1368    /// Returns `true` if the [`Connection`] associated with this handle is still alive.
+1369    pub fn is_alive(&self) -> bool {
+1370        self.0.upgrade().is_some()
+1371    }
+1372
+1373    /// Upgrade the handle to a full `Connection`
+1374    pub fn upgrade(&self) -> Option<Connection> {
+1375        self.upgrade_to_ref().map(Connection)
+1376    }
+1377
+1378    pub(crate) fn upgrade_to_ref(&self) -> Option<ConnectionRef> {
+1379        self.0.upgrade().map(ConnectionRef::from_arc)
+1380    }
+1381
+1382    /// Returns `true` if the two [`WeakConnectionHandle`] point at the same connection.
+1383    pub fn is_same_connection(&self, other: &Self) -> bool {
+1384        self.0.ptr_eq(&other.0)
+1385    }
+1386}
+1387
+1388#[derive(Debug, Default)]
+1389pub(crate) struct Shared {
+1390    handshake_confirmed: Notify,
+1391    /// Notified when new streams may be locally initiated due to an increase in stream ID flow
+1392    /// control budget
+1393    stream_budget_available: [Notify; 2],
+1394    /// Notified when the peer has initiated a new stream
+1395    stream_incoming: [Notify; 2],
+1396    datagram_received: Notify,
+1397    datagrams_unblocked: Notify,
+1398    closed: Notify,
+1399    /// Number of live handles that can be used to initiate or handle I/O; excludes the driver
+1400    ref_count: AtomicUsize,
+1401}
+1402
+1403pub(crate) struct State {
+1404    pub(crate) inner: proto::Connection,
+1405    driver: Option<Waker>,
+1406    handle: ConnectionHandle,
+1407    on_handshake_data: Option<oneshot::Sender<()>>,
+1408    on_connected: Option<oneshot::Sender<bool>>,
+1409    connected: bool,
+1410    handshake_confirmed: bool,
+1411    timer: Option<Pin<Box<dyn AsyncTimer>>>,
+1412    timer_deadline: Option<Instant>,
+1413    conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
+1414    endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+1415    pub(crate) blocked_writers: FxHashMap<StreamId, Waker>,
+1416    pub(crate) blocked_readers: FxHashMap<StreamId, Waker>,
+1417    pub(crate) stopped: FxHashMap<StreamId, Arc<Notify>>,
+1418    /// Always set to Some before the connection becomes drained
+1419    pub(crate) error: Option<ConnectionError>,
+1420    /// Tracks paths being opened
+1421    open_path: FxHashMap<PathId, watch::Sender<Result<(), PathError>>>,
+1422    /// Tracks reference counts for paths.
+1423    ///
+1424    /// I.e. how many [`Path`] and [`WeakPathHandle`] structs are alive for a path.
+1425    /// Each entry's [`PathRefOwner`] holds an [`AtomicUsize`] so that cloning or
+1426    /// dropping a [`PathRef`] (held by [`Path`] or [`WeakPathHandle`]) does not need
+1427    /// to lock this state.
+1428    ///
+1429    /// [`WeakPathHandle`]: crate::path::WeakPathHandle
+1430    pub(crate) path_refs: FxHashMap<PathId, PathRefOwner>,
+1431    /// Final path stats for discarded paths.
+1432    ///
+1433    /// We only insert entries if the discarded path has a non-zero reference count in [`Self::path_refs`].
+1434    /// When the last reference to a path is dropped its entry is removed from both maps.
+1435    pub(crate) final_path_stats: FxHashMap<PathId, PathStats>,
+1436    pub(crate) path_events: tokio::sync::broadcast::Sender<PathEvent>,
+1437    sender: Pin<Box<dyn UdpSender>>,
+1438    pub(crate) runtime: Arc<dyn Runtime>,
+1439    send_buffer: Vec<u8>,
+1440    /// We buffer a transmit when the underlying I/O would block
+1441    buffered_transmit: Option<proto::Transmit>,
+1442    /// Our last external address reported by the peer. When multipath is enabled, this will be the
+1443    /// last report across all paths.
+1444    pub(crate) observed_external_addr: watch::Sender<Option<SocketAddr>>,
+1445    pub(crate) nat_traversal_updates: tokio::sync::broadcast::Sender<n0_nat_traversal::Event>,
+1446    on_closed: Vec<oneshot::Sender<Closed>>,
+1447}
+1448
+1449impl State {
+1450    #[allow(clippy::too_many_arguments)]
+1451    fn new(
+1452        inner: proto::Connection,
+1453        handle: ConnectionHandle,
+1454        endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+1455        conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
+1456        on_handshake_data: oneshot::Sender<()>,
+1457        on_connected: oneshot::Sender<bool>,
+1458        sender: Pin<Box<dyn UdpSender>>,
+1459        runtime: Arc<dyn Runtime>,
+1460    ) -> Self {
+1461        Self {
+1462            inner,
+1463            driver: None,
+1464            handle,
+1465            on_handshake_data: Some(on_handshake_data),
+1466            on_connected: Some(on_connected),
+1467            connected: false,
+1468            handshake_confirmed: false,
+1469            timer: None,
+1470            timer_deadline: None,
+1471            conn_events,
+1472            endpoint_events,
+1473            blocked_writers: FxHashMap::default(),
+1474            blocked_readers: FxHashMap::default(),
+1475            stopped: FxHashMap::default(),
+1476            open_path: FxHashMap::default(),
+1477            error: None,
+1478            sender,
+1479            runtime,
+1480            send_buffer: Vec::new(),
+1481            buffered_transmit: None,
+1482            path_events: tokio::sync::broadcast::channel(32).0,
+1483            observed_external_addr: watch::Sender::new(None),
+1484            nat_traversal_updates: tokio::sync::broadcast::channel(32).0,
+1485            on_closed: Vec::new(),
+1486            final_path_stats: Default::default(),
+1487            path_refs: Default::default(),
+1488        }
+1489    }
+1490
+1491    fn drive_transmit(&mut self, cx: &mut Context<'_>) -> io::Result<bool> {
+1492        let now = self.runtime.now();
+1493        let mut transmits = 0;
+1494
+1495        let max_datagrams = self
+1496            .sender
+1497            .max_transmit_segments()
+1498            .min(MAX_TRANSMIT_SEGMENTS);
+1499
+1500        loop {
+1501            // Retry the last transmit, or get a new one.
+1502            let t = match self.buffered_transmit.take() {
+1503                Some(t) => t,
+1504                None => {
+1505                    self.send_buffer.clear();
+1506                    match self
+1507                        .inner
+1508                        .poll_transmit(now, max_datagrams, &mut self.send_buffer)
+1509                    {
+1510                        Some(t) => {
+1511                            transmits += match t.segment_size {
+1512                                None => 1,
+1513                                Some(s) => t.size.div_ceil(s), // round up
+1514                            };
+1515                            t
+1516                        }
+1517                        None => break,
+1518                    }
+1519                }
+1520            };
+1521
+1522            let len = t.size;
+1523            match self
+1524                .sender
+1525                .as_mut()
+1526                .poll_send(&udp_transmit(&t, &self.send_buffer[..len]), cx)
+1527            {
+1528                Poll::Pending => {
+1529                    self.buffered_transmit = Some(t);
+1530                    return Ok(false);
+1531                }
+1532                Poll::Ready(Err(e)) => return Err(e),
+1533                Poll::Ready(Ok(())) => {}
+1534            }
+1535
+1536            if transmits >= MAX_TRANSMIT_DATAGRAMS {
+1537                // TODO: What isn't ideal here yet is that if we don't poll all
+1538                // datagrams that could be sent we don't go into the `app_limited`
+1539                // state and CWND continues to grow until we get here the next time.
+1540                // See https://github.com/quinn-rs/quinn/issues/1126
+1541                return Ok(true);
+1542            }
+1543        }
+1544
+1545        Ok(false)
+1546    }
+1547
+1548    fn forward_endpoint_events(&mut self) {
+1549        while let Some(event) = self.inner.poll_endpoint_events() {
+1550            // If the endpoint driver is gone, noop.
+1551            let _ = self.endpoint_events.send((self.handle, event));
+1552        }
+1553    }
+1554
+1555    /// If this returns `Err`, the endpoint is dead, so the driver should exit immediately.
+1556    fn process_conn_events(
+1557        &mut self,
+1558        shared: &Shared,
+1559        cx: &mut Context<'_>,
+1560    ) -> Result<(), ConnectionError> {
+1561        loop {
+1562            match self.conn_events.poll_recv(cx) {
+1563                Poll::Ready(Some(ConnectionEvent::Rebind(sender))) => {
+1564                    self.sender = sender;
+1565                    self.inner.handle_network_change(None, self.runtime.now());
+1566                }
+1567                Poll::Ready(Some(ConnectionEvent::LocalAddressChanged(hint))) => {
+1568                    self.inner
+1569                        .handle_network_change(hint.as_deref().map(|x| x as _), self.runtime.now());
+1570                }
+1571                Poll::Ready(Some(ConnectionEvent::Proto(event))) => {
+1572                    self.inner.handle_event(event);
+1573                }
+1574                Poll::Ready(Some(ConnectionEvent::Close { reason, error_code })) => {
+1575                    self.close(error_code, reason, shared);
+1576                }
+1577                Poll::Ready(None) => {
+1578                    return Err(ConnectionError::TransportError(TransportError::new(
+1579                        TransportErrorCode::INTERNAL_ERROR,
+1580                        "endpoint driver future was dropped".to_string(),
+1581                    )));
+1582                }
+1583                Poll::Pending => {
+1584                    return Ok(());
+1585                }
+1586            }
+1587        }
+1588    }
+1589
+1590    fn forward_app_events(&mut self, shared: &Shared) {
+1591        while let Some(event) = self.inner.poll() {
+1592            use proto::Event::*;
+1593            match event {
+1594                HandshakeDataReady => {
+1595                    if let Some(x) = self.on_handshake_data.take() {
+1596                        let _ = x.send(());
+1597                    }
+1598                }
+1599                Connected => {
+1600                    self.connected = true;
+1601                    if let Some(x) = self.on_connected.take() {
+1602                        // We don't care if the on-connected future was dropped
+1603                        let _ = x.send(self.inner.accepted_0rtt());
+1604                    }
+1605                    if self.inner.side().is_client() && !self.inner.accepted_0rtt() {
+1606                        // Wake up rejected 0-RTT streams so they can fail immediately with
+1607                        // `ZeroRttRejected` errors.
+1608                        wake_all(&mut self.blocked_writers);
+1609                        wake_all(&mut self.blocked_readers);
+1610                        wake_all_notify(&mut self.stopped);
+1611                    }
+1612                }
+1613                HandshakeConfirmed => {
+1614                    self.handshake_confirmed = true;
+1615                    shared.handshake_confirmed.notify_waiters();
+1616                }
+1617                ConnectionLost { reason } => {
+1618                    self.terminate(reason, shared);
+1619                }
+1620                Stream(StreamEvent::Writable { id }) => wake_stream(id, &mut self.blocked_writers),
+1621                Stream(StreamEvent::Opened { dir: Dir::Uni }) => {
+1622                    shared.stream_incoming[Dir::Uni as usize].notify_waiters();
+1623                }
+1624                Stream(StreamEvent::Opened { dir: Dir::Bi }) => {
+1625                    shared.stream_incoming[Dir::Bi as usize].notify_waiters();
+1626                }
+1627                DatagramReceived => {
+1628                    shared.datagram_received.notify_waiters();
+1629                }
+1630                DatagramsUnblocked => {
+1631                    shared.datagrams_unblocked.notify_waiters();
+1632                }
+1633                Stream(StreamEvent::Readable { id }) => wake_stream(id, &mut self.blocked_readers),
+1634                Stream(StreamEvent::Available { dir }) => {
+1635                    // Might mean any number of streams are ready, so we wake up everyone
+1636                    shared.stream_budget_available[dir as usize].notify_waiters();
+1637                }
+1638                Stream(StreamEvent::Finished { id }) => wake_stream_notify(id, &mut self.stopped),
+1639                Stream(StreamEvent::Stopped { id, .. }) => {
+1640                    wake_stream_notify(id, &mut self.stopped);
+1641                    wake_stream(id, &mut self.blocked_writers);
+1642                }
+1643                Path(ref evt @ PathEvent::ObservedAddr { addr: observed, .. }) => {
+1644                    self.path_events.send(evt.clone()).ok();
+1645                    self.observed_external_addr.send_if_modified(|addr| {
+1646                        let old = addr.replace(observed);
+1647                        old != *addr
+1648                    });
+1649                }
+1650                Path(ref evt @ PathEvent::Established { id, .. }) => {
+1651                    self.path_events.send(evt.clone()).ok();
+1652                    if let Some(sender) = self.open_path.remove(&id) {
+1653                        sender.send_modify(|value| *value = Ok(()));
+1654                    }
+1655                }
+1656                Path(
+1657                    ref evt @ PathEvent::Discarded {
+1658                        id, ref path_stats, ..
+1659                    },
+1660                ) => {
+1661                    if self.path_refs.contains_key(&id) {
+1662                        self.final_path_stats.insert(id, *path_stats.clone());
+1663                    }
+1664                    self.path_events.send(evt.clone()).ok();
+1665                }
+1666                Path(ref evt @ PathEvent::Abandoned { id, .. }) => {
+1667                    if let Some(sender) = self.open_path.remove(&id) {
+1668                        // We don't care for the reason why this path was closed here, because semantically
+1669                        // all close reasons for a path that has not yet been opened equals to `ValidationFailed`.
+1670                        // With the noq API, there is no way to application-close a not-yet-opened path, so
+1671                        // `ApplicationClosed` cannot occur. And all other variants will only occur for paths
+1672                        // that have already been opened.
+1673                        // The previous iteration of this code had another event `PathEvent::LocallyClosed` which
+1674                        // contained a `PathError`, but that was only ever set to `ValidationFailed`.
+1675                        let error = PathError::ValidationFailed;
+1676                        sender.send_modify(|value| *value = Err(error));
+1677                    }
+1678                    // this will happen also for already opened paths
+1679                    self.path_events.send(evt.clone()).ok();
+1680                }
+1681                Path(evt @ PathEvent::RemoteStatus { .. }) => {
+1682                    self.path_events.send(evt).ok();
+1683                }
+1684                NatTraversal(update) => {
+1685                    self.nat_traversal_updates.send(update).ok();
+1686                }
+1687                _ => {
+1688                    // PathEvent is #[non_exhaustive].
+1689                    // It's possible that noq is built against a newer noq-proto version.
+1690                    // In that case, we need to ignore path events we can't handle yet.
+1691                    // But for tests, we expect noq and noq-proto to be in sync, so we
+1692                    // should panic in case we don't actually handle new cases.
+1693                    #[cfg(test)]
+1694                    panic!("Unhandled PathEvent variant: {event:?}");
+1695                }
+1696            }
+1697        }
+1698    }
+1699
+1700    fn drive_timer(&mut self, cx: &mut Context<'_>) -> bool {
+1701        // Check whether we need to (re)set the timer. If so, we must poll again to ensure the
+1702        // timer is registered with the runtime (and check whether it's already
+1703        // expired).
+1704        match self.inner.poll_timeout() {
+1705            Some(deadline) => {
+1706                if let Some(delay) = &mut self.timer {
+1707                    // There is no need to reset the tokio timer if the deadline
+1708                    // did not change
+1709                    if self
+1710                        .timer_deadline
+1711                        .map(|current_deadline| current_deadline != deadline)
+1712                        .unwrap_or(true)
+1713                    {
+1714                        delay.as_mut().reset(deadline);
+1715                    }
+1716                } else {
+1717                    self.timer = Some(self.runtime.new_timer(deadline));
+1718                }
+1719                // Store the actual expiration time of the timer
+1720                self.timer_deadline = Some(deadline);
+1721            }
+1722            None => {
+1723                self.timer_deadline = None;
+1724                return false;
+1725            }
+1726        }
+1727
+1728        if self.timer_deadline.is_none() {
+1729            return false;
+1730        }
+1731
+1732        let delay = self
+1733            .timer
+1734            .as_mut()
+1735            .expect("timer must exist in this state")
+1736            .as_mut();
+1737        if delay.poll(cx).is_pending() {
+1738            // Since there wasn't a timeout event, there is nothing new
+1739            // for the connection to do
+1740            return false;
+1741        }
+1742
+1743        // A timer expired, so the caller needs to check for
+1744        // new transmits, which might cause new timers to be set.
+1745        self.inner.handle_timeout(self.runtime.now());
+1746        self.timer_deadline = None;
+1747        true
+1748    }
+1749
+1750    /// Wake up a blocked `Driver` task to process I/O
+1751    pub(crate) fn wake(&mut self) {
+1752        if let Some(x) = self.driver.take() {
+1753            x.wake();
+1754        }
+1755    }
+1756
+1757    /// Used to wake up all blocked futures when the connection becomes closed for any reason
+1758    fn terminate(&mut self, reason: ConnectionError, shared: &Shared) {
+1759        self.error = Some(reason.clone());
+1760        if let Some(x) = self.on_handshake_data.take() {
+1761            let _ = x.send(());
+1762        }
+1763        wake_all(&mut self.blocked_writers);
+1764        wake_all(&mut self.blocked_readers);
+1765        shared.stream_budget_available[Dir::Uni as usize].notify_waiters();
+1766        shared.stream_budget_available[Dir::Bi as usize].notify_waiters();
+1767        shared.stream_incoming[Dir::Uni as usize].notify_waiters();
+1768        shared.stream_incoming[Dir::Bi as usize].notify_waiters();
+1769        shared.datagram_received.notify_waiters();
+1770        shared.datagrams_unblocked.notify_waiters();
+1771        if let Some(x) = self.on_connected.take() {
+1772            let _ = x.send(false);
+1773        }
+1774        shared.handshake_confirmed.notify_waiters();
+1775        wake_all_notify(&mut self.stopped);
+1776        shared.closed.notify_waiters();
+1777
+1778        // Send to the registered on_closed futures.
+1779        if !self.on_closed.is_empty() {
+1780            let closed = Closed::new(self, reason);
+1781            for tx in self.on_closed.drain(..) {
+1782                tx.send(closed.clone()).ok();
+1783            }
+1784        }
+1785    }
+1786
+1787    fn close(&mut self, error_code: VarInt, reason: Bytes, shared: &Shared) {
+1788        self.inner.close(self.runtime.now(), error_code, reason);
+1789        self.terminate(ConnectionError::LocallyClosed, shared);
+1790        self.wake();
+1791    }
+1792
+1793    /// Close for a reason other than the application's explicit request
+1794    pub(crate) fn implicit_close(&mut self, shared: &Shared) {
+1795        self.close(0u32.into(), Bytes::new(), shared);
+1796    }
+1797
+1798    pub(crate) fn check_0rtt(&self) -> Result<(), ()> {
+1799        if self.inner.is_handshaking()
+1800            || self.inner.accepted_0rtt()
+1801            || self.inner.side().is_server()
+1802        {
+1803            Ok(())
+1804        } else {
+1805            Err(())
+1806        }
+1807    }
+1808
+1809    /// Returns [`PathStats`] for a path, if available.
+1810    ///
+1811    /// This gets the stats from [`proto::Connection`]. If that returns `None`
+1812    /// it gets them from `Self::final_path_stats` instead.
+1813    pub(crate) fn path_stats(&mut self, path_id: PathId) -> Option<PathStats> {
+1814        self.inner
+1815            .path_stats(path_id)
+1816            .or_else(|| self.final_path_stats.get(&path_id).copied())
+1817    }
+1818
+1819    /// Acquire a new [`PathRef`] for a path id, bumping its reference counter by 1.
+1820    ///
+1821    /// The returned [`PathRef`] is intended to be stored on a [`Path`] or [`WeakPathHandle`].
+1822    /// Its reference count is automatically increased when cloned. When its owner is dropped,
+1823    /// [`PathRef::on_drop`] must be called to decrement the refcount.
+1824    ///
+1825    /// [`WeakPathHandle`]: crate::path::WeakPathHandle
+1826    pub(crate) fn acquire_path_ref(&mut self, path_id: PathId) -> PathRef {
+1827        self.path_refs.entry(path_id).or_default().acquire(path_id)
+1828    }
+1829}
+1830
+1831impl Drop for State {
+1832    fn drop(&mut self) {
+1833        if !self.inner.is_drained() {
+1834            // Ensure the endpoint can tidy up
+1835            let _ = self
+1836                .endpoint_events
+1837                .send((self.handle, proto::EndpointEvent::drained()));
+1838        }
+1839
+1840        if !self.on_closed.is_empty()
+1841            && let Some(reason) = self.error.clone()
+1842        {
+1843            // Ensure that all on_closed oneshot senders are triggered before dropping.
+1844            let closed = Closed::new(self, reason);
+1845            for tx in self.on_closed.drain(..) {
+1846                tx.send(closed.clone()).ok();
+1847            }
+1848        }
+1849    }
+1850}
+1851
+1852impl fmt::Debug for State {
+1853    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+1854        f.debug_struct("State").field("inner", &self.inner).finish()
+1855    }
+1856}
+1857
+1858fn wake_stream(stream_id: StreamId, wakers: &mut FxHashMap<StreamId, Waker>) {
+1859    if let Some(waker) = wakers.remove(&stream_id) {
+1860        waker.wake();
+1861    }
+1862}
+1863
+1864fn wake_all(wakers: &mut FxHashMap<StreamId, Waker>) {
+1865    wakers.drain().for_each(|(_, waker)| waker.wake())
+1866}
+1867
+1868fn wake_stream_notify(stream_id: StreamId, wakers: &mut FxHashMap<StreamId, Arc<Notify>>) {
+1869    if let Some(notify) = wakers.remove(&stream_id) {
+1870        notify.notify_waiters()
+1871    }
+1872}
+1873
+1874fn wake_all_notify(wakers: &mut FxHashMap<StreamId, Arc<Notify>>) {
+1875    wakers
+1876        .drain()
+1877        .for_each(|(_, notify)| notify.notify_waiters())
+1878}
+1879
+1880/// Errors that can arise when sending a datagram
+1881#[derive(Debug, Error, Clone, Eq, PartialEq)]
+1882pub enum SendDatagramError {
+1883    /// The peer does not support receiving datagram frames
+1884    #[error("datagrams not supported by peer")]
+1885    UnsupportedByPeer,
+1886    /// Datagram support is disabled locally
+1887    #[error("datagram support disabled")]
+1888    Disabled,
+1889    /// The datagram is larger than the connection can currently accommodate
+1890    ///
+1891    /// Indicates that the path MTU minus overhead or the limit advertised by the peer has been
+1892    /// exceeded.
+1893    #[error("datagram too large")]
+1894    TooLarge,
+1895    /// The connection was lost
+1896    #[error("connection lost")]
+1897    ConnectionLost(#[from] ConnectionError),
+1898}
+1899
+1900/// The maximum amount of datagrams which will be produced in a single `drive_transmit` call
+1901///
+1902/// This limits the amount of CPU resources consumed by datagram generation,
+1903/// and allows other tasks (like receiving ACKs) to run in between.
+1904const MAX_TRANSMIT_DATAGRAMS: usize = 20;
+1905
+1906/// The maximum amount of datagrams that are sent in a single transmit
+1907///
+1908/// This can be lower than the maximum platform capabilities, to avoid excessive
+1909/// memory allocations when calling `poll_transmit()`. Benchmarks have shown
+1910/// that numbers around 10 are a good compromise.
+1911const MAX_TRANSMIT_SEGMENTS: NonZeroUsize = NonZeroUsize::new(10).expect("known");
\ No newline at end of file diff --git a/pr/672/docs/src/noq/endpoint.rs.html b/pr/672/docs/src/noq/endpoint.rs.html new file mode 100644 index 000000000..c28cb98f1 --- /dev/null +++ b/pr/672/docs/src/noq/endpoint.rs.html @@ -0,0 +1,1044 @@ +endpoint.rs - source

noq/
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::{
+12        Arc, Mutex,
+13        atomic::{AtomicUsize, Ordering},
+14    },
+15    task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
+16};
+17
+18#[cfg(all(
+19    not(wasm_browser),
+20    any(feature = "runtime-tokio", feature = "runtime-smol"),
+21    any(feature = "aws-lc-rs", feature = "ring"),
+22))]
+23use crate::runtime::default_runtime;
+24use crate::{
+25    Instant,
+26    runtime::{AsyncUdpSocket, Runtime, UdpSender},
+27    udp_transmit,
+28};
+29use bytes::{Bytes, BytesMut};
+30use pin_project_lite::pin_project;
+31use proto::{
+32    self as proto, ClientConfig, ConnectError, ConnectionError, ConnectionHandle, DatagramEvent,
+33    EndpointEvent, FourTuple, NetworkChangeHint, ServerConfig,
+34};
+35use rustc_hash::FxHashMap;
+36#[cfg(all(
+37    not(wasm_browser),
+38    any(feature = "runtime-tokio", feature = "runtime-smol"),
+39    any(feature = "aws-lc-rs", feature = "ring"),
+40))]
+41use socket2::{Domain, Protocol, Socket, Type};
+42use tokio::sync::{Notify, futures::Notified, mpsc};
+43use tracing::{Instrument, Span, trace};
+44use udp::{BATCH_SIZE, RecvMeta};
+45
+46use crate::{
+47    ConnectionEvent, EndpointConfig, IO_LOOP_BOUND, RECV_TIME_BOUND, VarInt,
+48    connection::Connecting, incoming::Incoming, work_limiter::WorkLimiter,
+49};
+50
+51/// A QUIC endpoint.
+52///
+53/// An endpoint corresponds to a single UDP socket, may host many connections, and may act as both
+54/// client and server for different connections.
+55///
+56/// May be cloned to obtain another handle to the same endpoint.
+57#[derive(Debug, Clone)]
+58pub struct Endpoint {
+59    pub(crate) inner: EndpointRef,
+60    runtime: Arc<dyn Runtime>,
+61}
+62
+63impl Endpoint {
+64    /// Helper to construct an endpoint for use with outgoing connections only
+65    ///
+66    /// Note that `addr` is the *local* address to bind to, which should usually be a wildcard
+67    /// address like `0.0.0.0:0` or `[::]:0`, which allow communication with any reachable IPv4 or
+68    /// IPv6 address respectively from an OS-assigned port.
+69    ///
+70    /// If an IPv6 address is provided, attempts to make the socket dual-stack so as to allow
+71    /// communication with both IPv4 and IPv6 addresses. As such, calling `Endpoint::client` with
+72    /// the address `[::]:0` is a reasonable default to maximize the ability to connect to other
+73    /// address. For example:
+74    ///
+75    /// ```
+76    /// noq::Endpoint::client((std::net::Ipv6Addr::UNSPECIFIED, 0).into());
+77    /// ```
+78    ///
+79    /// Some environments may not allow creation of dual-stack sockets, in which case an IPv6
+80    /// client will only be able to connect to IPv6 servers. An IPv4 client is never dual-stack.
+81    #[cfg(all(
+82        not(wasm_browser),
+83        any(feature = "runtime-tokio", feature = "runtime-smol"),
+84        any(feature = "aws-lc-rs", feature = "ring"), // `EndpointConfig::default()` is only available with these
+85    ))]
+86    pub fn client(addr: SocketAddr) -> io::Result<Self> {
+87        let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))?;
+88        if addr.is_ipv6()
+89            && let Err(e) = socket.set_only_v6(false)
+90        {
+91            tracing::debug!(%e, "unable to make socket dual-stack");
+92        }
+93        socket.bind(&addr.into())?;
+94        let runtime =
+95            default_runtime().ok_or_else(|| io::Error::other("no async runtime found"))?;
+96        Self::new_with_abstract_socket(
+97            EndpointConfig::default(),
+98            None,
+99            runtime.wrap_udp_socket(socket.into())?,
+100            runtime,
+101        )
+102    }
+103
+104    /// Returns relevant stats from this Endpoint
+105    pub fn stats(&self) -> EndpointStats {
+106        self.inner.state.lock().unwrap().stats
+107    }
+108
+109    /// Helper to construct an endpoint for use with both incoming and outgoing connections
+110    ///
+111    /// Note that `addr` is the *local* address to bind to, which should usually be a wildcard
+112    /// address like `0.0.0.0:0` or `[::]:0`, which allow communication with any reachable IPv4 or
+113    /// IPv6 address respectively from an OS-assigned port.
+114    ///
+115    /// If an IPv6 address is provided, attempts to make the socket dual-stack so as to allow
+116    /// communication with both IPv4 and IPv6 clients. As such, calling `Endpoint::server` with
+117    /// the address `[::]:0` is a reasonable default to maximize the ability to accept connections
+118    /// from any address.
+119    ///
+120    /// Some environments may not allow creation of dual-stack sockets, in which case an IPv6
+121    /// server will only be able to accept connections from IPv6 clients. An IPv4 server is never
+122    /// dual-stack.
+123    #[cfg(all(
+124        not(wasm_browser),
+125        any(feature = "runtime-tokio", feature = "runtime-smol"),
+126        any(feature = "aws-lc-rs", feature = "ring"), // `EndpointConfig::default()` is only available with these
+127    ))]
+128    pub fn server(config: ServerConfig, addr: SocketAddr) -> io::Result<Self> {
+129        let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))?;
+130        if addr.is_ipv6()
+131            && let Err(e) = socket.set_only_v6(false)
+132        {
+133            tracing::debug!(%e, "unable to make socket dual-stack");
+134        }
+135        socket.bind(&addr.into())?;
+136        let runtime =
+137            default_runtime().ok_or_else(|| io::Error::other("no async runtime found"))?;
+138        Self::new_with_abstract_socket(
+139            EndpointConfig::default(),
+140            Some(config),
+141            runtime.wrap_udp_socket(socket.into())?,
+142            runtime,
+143        )
+144    }
+145
+146    /// Construct an endpoint with arbitrary configuration and socket
+147    #[cfg(not(wasm_browser))]
+148    pub fn new(
+149        config: EndpointConfig,
+150        server_config: Option<ServerConfig>,
+151        socket: std::net::UdpSocket,
+152        runtime: Arc<dyn Runtime>,
+153    ) -> io::Result<Self> {
+154        let socket = runtime.wrap_udp_socket(socket)?;
+155        Self::new_with_abstract_socket(config, server_config, socket, runtime)
+156    }
+157
+158    /// Construct an endpoint with arbitrary configuration and pre-constructed abstract socket
+159    ///
+160    /// Useful when `socket` has additional state (e.g. sidechannels) attached for which shared
+161    /// ownership is needed.
+162    pub fn new_with_abstract_socket(
+163        config: EndpointConfig,
+164        server_config: Option<ServerConfig>,
+165        socket: Box<dyn AsyncUdpSocket>,
+166        runtime: Arc<dyn Runtime>,
+167    ) -> io::Result<Self> {
+168        let addr = socket.local_addr()?;
+169        let allow_mtud = !socket.may_fragment();
+170        let rc = EndpointRef::new(
+171            socket,
+172            proto::Endpoint::new(Arc::new(config), server_config.map(Arc::new), allow_mtud),
+173            addr.is_ipv6(),
+174            runtime.clone(),
+175        );
+176        let driver = EndpointDriver(rc.clone());
+177        runtime.spawn(Box::pin(
+178            async {
+179                if let Err(e) = driver.await {
+180                    tracing::error!("I/O error: {}", e);
+181                }
+182            }
+183            .instrument(Span::current()),
+184        ));
+185        Ok(Self { inner: rc, runtime })
+186    }
+187
+188    /// Get the next incoming connection attempt from a client
+189    ///
+190    /// Yields [`Incoming`]s, or `None` if the endpoint is [`close`](Self::close)d. [`Incoming`]
+191    /// can be `await`ed to obtain the final [`Connection`](crate::Connection), or used to e.g.
+192    /// filter connection attempts or force address validation, or converted into an intermediate
+193    /// `Connecting` future which can be used to e.g. send 0.5-RTT data.
+194    pub fn accept(&self) -> Accept<'_> {
+195        Accept {
+196            endpoint: self,
+197            notify: self.inner.shared.incoming.notified(),
+198        }
+199    }
+200
+201    /// Set the client configuration used by `connect`
+202    pub fn set_default_client_config(&self, config: ClientConfig) {
+203        self.inner.0.state.lock().unwrap().default_client_config = Some(config);
+204    }
+205
+206    /// Connect to a remote endpoint
+207    ///
+208    /// `server_name` must be covered by the certificate presented by the server. This prevents a
+209    /// connection from being intercepted by an attacker with a valid certificate for some other
+210    /// server.
+211    ///
+212    /// May fail immediately due to configuration errors, or in the future if the connection could
+213    /// not be established.
+214    pub fn connect(&self, addr: SocketAddr, server_name: &str) -> Result<Connecting, ConnectError> {
+215        let Some(config) = self
+216            .inner
+217            .0
+218            .state
+219            .lock()
+220            .unwrap()
+221            .default_client_config
+222            .clone()
+223        else {
+224            return Err(ConnectError::NoDefaultClientConfig);
+225        };
+226
+227        self.connect_with(config, addr, server_name)
+228    }
+229
+230    /// Connect to a remote endpoint using a custom configuration.
+231    ///
+232    /// See [`connect()`] for details.
+233    ///
+234    /// [`connect()`]: Endpoint::connect
+235    pub fn connect_with(
+236        &self,
+237        config: ClientConfig,
+238        addr: SocketAddr,
+239        server_name: &str,
+240    ) -> Result<Connecting, ConnectError> {
+241        let mut endpoint = self.inner.state.lock().unwrap();
+242        if endpoint.driver_lost || endpoint.recv_state.connections.close.is_some() {
+243            return Err(ConnectError::EndpointStopping);
+244        }
+245        if addr.is_ipv6() && !endpoint.ipv6 {
+246            return Err(ConnectError::InvalidRemoteAddress(addr));
+247        }
+248        let addr = if endpoint.ipv6 {
+249            SocketAddr::V6(ensure_ipv6(addr))
+250        } else {
+251            addr
+252        };
+253
+254        let (ch, conn) = endpoint
+255            .inner
+256            .connect(self.runtime.now(), config, addr, server_name)?;
+257
+258        let sender = endpoint.socket.create_sender();
+259        endpoint.stats.outgoing_handshakes += 1;
+260        Ok(endpoint
+261            .recv_state
+262            .connections
+263            .insert(ch, conn, sender, self.runtime.clone()))
+264    }
+265
+266    /// Switch to a new UDP socket
+267    ///
+268    /// See [`Endpoint::rebind_abstract()`] for details.
+269    #[cfg(not(wasm_browser))]
+270    pub fn rebind(&self, socket: std::net::UdpSocket) -> io::Result<()> {
+271        self.rebind_abstract(self.runtime.wrap_udp_socket(socket)?)
+272    }
+273
+274    /// Switch to a new UDP socket
+275    ///
+276    /// Allows the endpoint's address to be updated live, affecting all active connections. Incoming
+277    /// connections and connections to servers unreachable from the new address will be lost.
+278    ///
+279    /// On error, the old UDP socket is retained.
+280    pub fn rebind_abstract(&self, socket: Box<dyn AsyncUdpSocket>) -> io::Result<()> {
+281        let addr = socket.local_addr()?;
+282        let mut inner = self.inner.state.lock().unwrap();
+283        inner.prev_socket = Some(mem::replace(&mut inner.socket, socket));
+284        inner.ipv6 = addr.is_ipv6();
+285
+286        // Update connection socket references
+287        for sender in inner.recv_state.connections.senders.values() {
+288            // Ignoring errors from dropped connections
+289            let _ = sender.send(ConnectionEvent::Rebind(inner.socket.create_sender()));
+290        }
+291        if let Some(driver) = inner.driver.take() {
+292            // Ensure the driver can register for wake-ups from the new socket
+293            driver.wake();
+294        }
+295
+296        Ok(())
+297    }
+298
+299    /// Notify connections that the local network address has changed.
+300    ///
+301    /// This informs all active connections that the local address may have changed (e.g., due to a
+302    /// network interface change), triggering liveness checks and recovery procedures on each
+303    /// connection without requiring a socket rebind.
+304    ///
+305    /// Unlike [`Self::rebind`], this does not change the underlying socket. Use this when the
+306    /// network topology changes but the socket remains valid (e.g., if bound to the unspecified
+307    /// address and switching from WiFi to cellular, or when the local IP address changes).
+308    ///
+309    /// The optional `hint` allows callers to indicate which paths may still be recoverable after
+310    /// the network change. If `None`, all paths are assumed to be non-recoverable. For client-side
+311    /// multipath connections, unrecoverable paths will be closed and replaced with new paths to
+312    /// the same remote addresses.
+313    pub fn handle_network_change(&self, hint: Option<Arc<dyn NetworkChangeHint + Sync + Send>>) {
+314        let mut inner = self.inner.state.lock().unwrap();
+315        for sender in inner.recv_state.connections.senders.values() {
+316            // Ignoring errors from dropped connections
+317            let _ = sender.send(ConnectionEvent::LocalAddressChanged(hint.clone()));
+318        }
+319        if let Some(driver) = inner.driver.take() {
+320            driver.wake();
+321        }
+322    }
+323
+324    /// Replace the server configuration, affecting new incoming connections only
+325    ///
+326    /// Useful for e.g. refreshing TLS certificates without disrupting existing connections.
+327    pub fn set_server_config(&self, server_config: Option<ServerConfig>) {
+328        self.inner
+329            .state
+330            .lock()
+331            .unwrap()
+332            .inner
+333            .set_server_config(server_config.map(Arc::new))
+334    }
+335
+336    /// Get the local `SocketAddr` the underlying socket is bound to
+337    pub fn local_addr(&self) -> io::Result<SocketAddr> {
+338        self.inner.state.lock().unwrap().socket.local_addr()
+339    }
+340
+341    /// Get the number of connections that are currently open
+342    pub fn open_connections(&self) -> usize {
+343        self.inner.state.lock().unwrap().inner.open_connections()
+344    }
+345
+346    /// Close all of this endpoint's connections immediately and cease accepting new connections.
+347    ///
+348    /// See [`Connection::close()`] for details.
+349    ///
+350    /// [`Connection::close()`]: crate::Connection::close
+351    pub fn close(&self, error_code: VarInt, reason: &[u8]) {
+352        let reason = Bytes::copy_from_slice(reason);
+353        let mut endpoint = self.inner.state.lock().unwrap();
+354        endpoint.recv_state.connections.close = Some((error_code, reason.clone()));
+355        for sender in endpoint.recv_state.connections.senders.values() {
+356            // Ignoring errors from dropped connections
+357            let _ = sender.send(ConnectionEvent::Close {
+358                error_code,
+359                reason: reason.clone(),
+360            });
+361        }
+362        self.inner.shared.incoming.notify_waiters();
+363        // Wake the endpoint driver so that it can shutdown if the endpoint is already drained.
+364        if let Some(waker) = endpoint.driver.take() {
+365            waker.wake();
+366        }
+367    }
+368
+369    /// Waits for all connections on the endpoint to be cleanly shut down and drained.
+370    ///
+371    /// This is equivalent to [`wait_all_draining()`] with additionally waiting for the connections to be
+372    /// drained. Please see its documentation for more information.
+373    ///
+374    /// Use `wait_idle()` in favor of `wait_all_draining()` if you care about waiting for the
+375    /// [`Connection`] structs to be dropped.
+376    ///
+377    /// [`wait_all_draining()`]: Self::wait_all_draining
+378    /// [`Connection`]: crate::Connection
+379    pub async fn wait_idle(&self) {
+380        loop {
+381            {
+382                let endpoint = &mut *self.inner.state.lock().unwrap();
+383                if endpoint.recv_state.connections.is_empty() {
+384                    break;
+385                }
+386                // Construct future while lock is held to avoid race
+387                self.inner.shared.idle.notified()
+388            }
+389            .await;
+390        }
+391    }
+392
+393    /// Waits for all connections on the endpoint to be ready for shutting down.
+394    ///
+395    /// Waiting for this condition before exiting ensures that a good-faith effort is made to notify
+396    /// peers of recent connection closes, whereas exiting immediately could force them to wait out
+397    /// the idle timeout period.
+398    ///
+399    /// Does not proactively close existing connections or cause incoming connections to be
+400    /// rejected. Consider calling [`close()`] if that is desired.
+401    ///
+402    /// Unlike [`wait_idle()`], this doesn't wait for the full draining period, so it can't be
+403    /// used to wait for all now-idle [`Connection`]s to be dropped.
+404    ///
+405    /// See also this section in the QUIC RFC: <https://datatracker.ietf.org/doc/html/rfc9000#section-10.2-6>
+406    ///
+407    /// [`close()`]: Self::close
+408    /// [`wait_idle()`]: Self::wait_idle
+409    /// [`Connection`]: crate::Connection
+410    pub async fn wait_all_draining(&self) {
+411        loop {
+412            {
+413                let endpoint = &mut *self.inner.state.lock().unwrap();
+414                if endpoint.recv_state.connections.active_connections == 0 {
+415                    break;
+416                }
+417                // Construct future while lock is held to avoid race
+418                self.inner.shared.all_draining.notified()
+419            }
+420            .await;
+421        }
+422    }
+423}
+424
+425/// Statistics on [Endpoint] activity
+426#[non_exhaustive]
+427#[derive(Debug, Default, Copy, Clone)]
+428pub struct EndpointStats {
+429    /// Cumulative number of Quic handshakes accepted by this [Endpoint]
+430    pub accepted_handshakes: u64,
+431    /// Cumulative number of Quic handshakes sent from this [Endpoint]
+432    pub outgoing_handshakes: u64,
+433    /// Cumulative number of Quic handshakes refused on this [Endpoint]
+434    pub refused_handshakes: u64,
+435    /// Cumulative number of Quic handshakes ignored on this [Endpoint]
+436    pub ignored_handshakes: u64,
+437}
+438
+439/// A future that drives IO on an endpoint
+440///
+441/// This task functions as the switch point between the UDP socket object and the
+442/// `Endpoint` responsible for routing datagrams to their owning `Connection`.
+443/// In order to do so, it also facilitates the exchange of different types of events
+444/// flowing between the `Endpoint` and the tasks managing `Connection`s. As such,
+445/// running this task is necessary to keep the endpoint's connections running.
+446///
+447/// `EndpointDriver` futures terminate when all clones of the `Endpoint` have been dropped, or when
+448/// an I/O error occurs.
+449#[must_use = "endpoint drivers must be spawned for I/O to occur"]
+450#[derive(Debug)]
+451pub(crate) struct EndpointDriver(pub(crate) EndpointRef);
+452
+453impl Future for EndpointDriver {
+454    type Output = Result<(), io::Error>;
+455
+456    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+457        let mut endpoint = self.0.state.lock().unwrap();
+458        if endpoint.driver.is_none() {
+459            endpoint.driver = Some(cx.waker().clone());
+460        }
+461
+462        let now = endpoint.runtime.now();
+463        let mut keep_going = false;
+464        keep_going |= endpoint.drive_recv(cx, now)?;
+465        keep_going |= endpoint.handle_events(cx, &self.0.shared);
+466
+467        if !endpoint.recv_state.incoming.is_empty() {
+468            self.0.shared.incoming.notify_waiters();
+469        }
+470
+471        // Stop the driver if either:
+472        // - all `Endpoint` structs are dropped and all connections are drained,
+473        // - or `Endpoint::close` has been called and all connections are drained.
+474        if endpoint.recv_state.connections.is_empty()
+475            && (self.0.shared.ref_count.load(Ordering::Relaxed) == 0
+476                || endpoint.recv_state.connections.close.is_some())
+477        {
+478            trace!("endpoint driver stopping");
+479            Poll::Ready(Ok(()))
+480        } else {
+481            drop(endpoint);
+482            // If there is more work to do schedule the endpoint task again.
+483            // `wake_by_ref()` is called outside the lock to minimize
+484            // lock contention on a multithreaded runtime.
+485            if keep_going {
+486                cx.waker().wake_by_ref();
+487            }
+488            Poll::Pending
+489        }
+490    }
+491}
+492
+493impl Drop for EndpointDriver {
+494    fn drop(&mut self) {
+495        let mut endpoint = self.0.state.lock().unwrap();
+496        endpoint.driver_lost = true;
+497        self.0.shared.incoming.notify_waiters();
+498        // Drop all outgoing channels, signaling the termination of the endpoint to the associated
+499        // connections.
+500        endpoint.recv_state.connections.senders.clear();
+501        endpoint.recv_state.connections.active_connections = 0;
+502    }
+503}
+504
+505#[derive(Debug)]
+506pub(crate) struct EndpointInner {
+507    pub(crate) state: Mutex<State>,
+508    pub(crate) shared: Shared,
+509}
+510
+511impl EndpointInner {
+512    pub(crate) fn accept(
+513        &self,
+514        incoming: proto::Incoming,
+515        server_config: Option<Arc<ServerConfig>>,
+516    ) -> Result<Connecting, ConnectionError> {
+517        let mut state = self.state.lock().unwrap();
+518        let mut response_buffer = Vec::new();
+519        let now = state.runtime.now();
+520        match state
+521            .inner
+522            .accept(incoming, now, &mut response_buffer, server_config)
+523        {
+524            Ok((handle, conn)) => {
+525                state.stats.accepted_handshakes += 1;
+526                let sender = state.socket.create_sender();
+527                let runtime = state.runtime.clone();
+528                Ok(state
+529                    .recv_state
+530                    .connections
+531                    .insert(handle, conn, sender, runtime))
+532            }
+533            Err(error) => {
+534                if let Some(transmit) = error.response {
+535                    respond(transmit, &response_buffer, &mut state.sender);
+536                }
+537                Err(error.cause)
+538            }
+539        }
+540    }
+541
+542    pub(crate) fn refuse(&self, incoming: proto::Incoming) {
+543        let mut state = self.state.lock().unwrap();
+544        state.stats.refused_handshakes += 1;
+545        let mut response_buffer = Vec::new();
+546        let transmit = state.inner.refuse(incoming, &mut response_buffer);
+547        respond(transmit, &response_buffer, &mut state.sender);
+548    }
+549
+550    pub(crate) fn retry(&self, incoming: proto::Incoming) -> Result<(), proto::RetryError> {
+551        let mut state = self.state.lock().unwrap();
+552        let mut response_buffer = Vec::new();
+553        let transmit = state.inner.retry(incoming, &mut response_buffer)?;
+554        respond(transmit, &response_buffer, &mut state.sender);
+555        Ok(())
+556    }
+557
+558    pub(crate) fn ignore(&self, incoming: proto::Incoming) {
+559        let mut state = self.state.lock().unwrap();
+560        state.stats.ignored_handshakes += 1;
+561        state.inner.ignore(incoming);
+562    }
+563}
+564
+565#[derive(Debug)]
+566pub(crate) struct State {
+567    socket: Box<dyn AsyncUdpSocket>,
+568    sender: Pin<Box<dyn UdpSender>>,
+569    /// During an active migration, abandoned_socket receives traffic
+570    /// until the first packet arrives on the new socket.
+571    prev_socket: Option<Box<dyn AsyncUdpSocket>>,
+572    inner: proto::Endpoint,
+573    recv_state: RecvState,
+574    driver: Option<Waker>,
+575    ipv6: bool,
+576    events: mpsc::UnboundedReceiver<(ConnectionHandle, EndpointEvent)>,
+577    driver_lost: bool,
+578    runtime: Arc<dyn Runtime>,
+579    stats: EndpointStats,
+580    default_client_config: Option<ClientConfig>,
+581}
+582
+583#[derive(Debug)]
+584pub(crate) struct Shared {
+585    /// Notifies subscribers of new incoming connections.
+586    ///
+587    /// This enables the `Endpoint::accept` API.
+588    incoming: Notify,
+589    /// Notifies subscribers when *all* connections have entered the draining state.
+590    ///
+591    /// This powers the `Endpoint::wait_idle` API.
+592    all_draining: Notify,
+593    /// Notifies subscribesr when *all* connections have been dropped.
+594    ///
+595    /// This powers the `Endpoint::wait_drained` API.
+596    idle: Notify,
+597    /// Number of live handles that can be used to initiate or handle I/O; excludes the driver
+598    ref_count: AtomicUsize,
+599}
+600
+601impl State {
+602    fn drive_recv(&mut self, cx: &mut Context<'_>, now: Instant) -> Result<bool, io::Error> {
+603        let get_time = || self.runtime.now();
+604        self.recv_state.recv_limiter.start_cycle(get_time);
+605        if let Some(socket) = &mut self.prev_socket {
+606            // We don't care about the `PollProgress` from old sockets.
+607            let poll_res = self.recv_state.poll_socket(
+608                cx,
+609                &mut self.inner,
+610                &mut **socket,
+611                &mut self.sender,
+612                &*self.runtime,
+613                now,
+614            );
+615            if poll_res.is_err() {
+616                self.prev_socket = None;
+617            }
+618        };
+619        let poll_res = self.recv_state.poll_socket(
+620            cx,
+621            &mut self.inner,
+622            &mut *self.socket,
+623            &mut self.sender,
+624            &*self.runtime,
+625            now,
+626        );
+627        self.recv_state.recv_limiter.finish_cycle(get_time);
+628        let poll_res = poll_res?;
+629        if poll_res.received_connection_packet {
+630            // Traffic has arrived on self.socket, therefore there is no need for the abandoned
+631            // one anymore. TODO: Account for multiple outgoing connections.
+632            self.prev_socket = None;
+633        }
+634        Ok(poll_res.keep_going)
+635    }
+636
+637    fn handle_events(&mut self, cx: &mut Context<'_>, shared: &Shared) -> bool {
+638        for _ in 0..IO_LOOP_BOUND {
+639            let (ch, event) = match self.events.poll_recv(cx) {
+640                Poll::Ready(Some(x)) => x,
+641                Poll::Ready(None) => unreachable!("EndpointInner owns one sender"),
+642                Poll::Pending => {
+643                    return false;
+644                }
+645            };
+646
+647            if event.is_draining() {
+648                self.recv_state.connections.active_connections -= 1;
+649                if self.recv_state.connections.active_connections == 0 {
+650                    shared.all_draining.notify_waiters();
+651                }
+652            } else if event.is_drained() {
+653                self.recv_state.connections.senders.remove(&ch);
+654                if self.recv_state.connections.is_empty() {
+655                    shared.idle.notify_waiters();
+656                }
+657            }
+658            let Some(event) = self.inner.handle_event(ch, event) else {
+659                continue;
+660            };
+661            // Ignoring errors from dropped connections that haven't yet been cleaned up
+662            let _ = self
+663                .recv_state
+664                .connections
+665                .senders
+666                .get_mut(&ch)
+667                .unwrap()
+668                .send(ConnectionEvent::Proto(event));
+669        }
+670
+671        true
+672    }
+673}
+674
+675impl Drop for State {
+676    fn drop(&mut self) {
+677        for incoming in self.recv_state.incoming.drain(..) {
+678            self.inner.ignore(incoming);
+679        }
+680    }
+681}
+682
+683fn respond(
+684    transmit: proto::Transmit,
+685    response_buffer: &[u8],
+686    sender: &mut Pin<Box<dyn UdpSender>>,
+687) {
+688    // Send if there's kernel buffer space; otherwise, drop it
+689    //
+690    // As an endpoint-generated packet, we know this is an
+691    // immediate, stateless response to an unconnected peer,
+692    // one of:
+693    //
+694    // - A version negotiation response due to an unknown version
+695    // - A `CLOSE` due to a malformed or unwanted connection attempt
+696    // - A stateless reset due to an unrecognized connection
+697    // - A `Retry` packet due to a connection attempt when
+698    //   `use_retry` is set
+699    //
+700    // In each case, a well-behaved peer can be trusted to retry a
+701    // few times, which is guaranteed to produce the same response
+702    // from us. Repeated failures might at worst cause a peer's new
+703    // connection attempt to time out, which is acceptable if we're
+704    // under such heavy load that there's never room for this code
+705    // to transmit. This is morally equivalent to the packet getting
+706    // lost due to congestion further along the link, which
+707    // similarly relies on peer retries for recovery.
+708
+709    // Copied from rust 1.85's std::task::Waker::noop() implementation for backwards compatibility
+710    const NOOP: RawWaker = {
+711        const VTABLE: RawWakerVTable = RawWakerVTable::new(
+712            // Cloning just returns a new no-op raw waker
+713            |_| NOOP,
+714            // `wake` does nothing
+715            |_| {},
+716            // `wake_by_ref` does nothing
+717            |_| {},
+718            // Dropping does nothing as we don't allocate anything
+719            |_| {},
+720        );
+721        RawWaker::new(std::ptr::null(), &VTABLE)
+722    };
+723    // SAFETY: Copied from rust stdlib, the NOOP waker is thread-safe and doesn't violate the RawWakerVTable contract,
+724    // it doesn't access the data pointer at all.
+725    let waker = unsafe { Waker::from_raw(NOOP) };
+726    let mut cx = Context::from_waker(&waker);
+727    _ = sender.as_mut().poll_send(
+728        &udp_transmit(&transmit, &response_buffer[..transmit.size]),
+729        &mut cx,
+730    );
+731}
+732
+733#[inline]
+734fn proto_ecn(ecn: udp::EcnCodepoint) -> proto::EcnCodepoint {
+735    match ecn {
+736        udp::EcnCodepoint::Ect0 => proto::EcnCodepoint::Ect0,
+737        udp::EcnCodepoint::Ect1 => proto::EcnCodepoint::Ect1,
+738        udp::EcnCodepoint::Ce => proto::EcnCodepoint::Ce,
+739    }
+740}
+741
+742#[derive(Debug)]
+743struct ConnectionSet {
+744    /// Senders for communicating with the endpoint's connections
+745    senders: FxHashMap<ConnectionHandle, mpsc::UnboundedSender<ConnectionEvent>>,
+746    /// Stored to give out clones to new ConnectionInners
+747    sender: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+748    /// Set if the endpoint has been manually closed
+749    close: Option<(VarInt, Bytes)>,
+750    /// Counter for all active (non-draining/drained) connections.
+751    ///
+752    /// This is directly related to the QUIC connection states "Initial", "Handshake",
+753    /// "Established", "Closed", "Draining" and "Drained" (see also `proto/src/connection/state.rs`).
+754    ///
+755    /// Any connection state that is not "Draining" or "Drained" is considered active.
+756    ///
+757    /// This counter is updated when new connections are added ([`ConnectionSet::insert`]) and when
+758    /// a connection informs us about entering the draining state ([`State::handle_events`]).
+759    active_connections: u64,
+760}
+761
+762impl ConnectionSet {
+763    fn insert(
+764        &mut self,
+765        handle: ConnectionHandle,
+766        conn: proto::Connection,
+767        sender: Pin<Box<dyn UdpSender>>,
+768        runtime: Arc<dyn Runtime>,
+769    ) -> Connecting {
+770        let (send, recv) = mpsc::unbounded_channel();
+771        if let Some((error_code, ref reason)) = self.close {
+772            send.send(ConnectionEvent::Close {
+773                error_code,
+774                reason: reason.clone(),
+775            })
+776            .unwrap();
+777        }
+778        self.senders.insert(handle, send);
+779        if self.close.is_none() {
+780            self.active_connections += 1;
+781        }
+782        Connecting::new(handle, conn, self.sender.clone(), recv, sender, runtime)
+783    }
+784
+785    fn is_empty(&self) -> bool {
+786        self.senders.is_empty()
+787    }
+788}
+789
+790pub(crate) fn ensure_ipv6(x: SocketAddr) -> SocketAddrV6 {
+791    match x {
+792        SocketAddr::V6(x) => x,
+793        SocketAddr::V4(x) => SocketAddrV6::new(x.ip().to_ipv6_mapped(), x.port(), 0, 0),
+794    }
+795}
+796
+797pin_project! {
+798    /// Future produced by [`Endpoint::accept`]
+799    pub struct Accept<'a> {
+800        endpoint: &'a Endpoint,
+801        #[pin]
+802        notify: Notified<'a>,
+803    }
+804}
+805
+806impl Future for Accept<'_> {
+807    type Output = Option<Incoming>;
+808    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+809        let mut this = self.project();
+810        let mut endpoint = this.endpoint.inner.state.lock().unwrap();
+811        if endpoint.driver_lost {
+812            return Poll::Ready(None);
+813        }
+814        if let Some(incoming) = endpoint.recv_state.incoming.pop_front() {
+815            // Release the mutex lock on endpoint so cloning it doesn't deadlock
+816            drop(endpoint);
+817            let incoming = Incoming::new(incoming, this.endpoint.inner.clone());
+818            return Poll::Ready(Some(incoming));
+819        }
+820        if endpoint.recv_state.connections.close.is_some() {
+821            return Poll::Ready(None);
+822        }
+823        loop {
+824            match this.notify.as_mut().poll(ctx) {
+825                // `state` lock ensures we didn't race with readiness
+826                Poll::Pending => return Poll::Pending,
+827                // Spurious wakeup, get a new future
+828                Poll::Ready(()) => this
+829                    .notify
+830                    .set(this.endpoint.inner.shared.incoming.notified()),
+831            }
+832        }
+833    }
+834}
+835
+836#[derive(Debug)]
+837pub(crate) struct EndpointRef(Arc<EndpointInner>);
+838
+839impl EndpointRef {
+840    pub(crate) fn new(
+841        socket: Box<dyn AsyncUdpSocket>,
+842        inner: proto::Endpoint,
+843        ipv6: bool,
+844        runtime: Arc<dyn Runtime>,
+845    ) -> Self {
+846        let (sender, events) = mpsc::unbounded_channel();
+847        let recv_state = RecvState::new(sender, socket.max_receive_segments(), &inner);
+848        let sender = socket.create_sender();
+849        Self(Arc::new(EndpointInner {
+850            shared: Shared {
+851                incoming: Notify::new(),
+852                all_draining: Notify::new(),
+853                idle: Notify::new(),
+854                ref_count: AtomicUsize::new(0),
+855            },
+856            state: Mutex::new(State {
+857                socket,
+858                sender,
+859                prev_socket: None,
+860                inner,
+861                ipv6,
+862                events,
+863                driver: None,
+864                driver_lost: false,
+865                recv_state,
+866                runtime,
+867                stats: EndpointStats::default(),
+868                default_client_config: None,
+869            }),
+870        }))
+871    }
+872}
+873
+874impl Clone for EndpointRef {
+875    fn clone(&self) -> Self {
+876        self.0.shared.ref_count.fetch_add(1, Ordering::Relaxed);
+877        Self(self.0.clone())
+878    }
+879}
+880
+881impl Drop for EndpointRef {
+882    fn drop(&mut self) {
+883        if self.0.shared.ref_count.fetch_sub(1, Ordering::Relaxed) > 1 {
+884            return;
+885        }
+886
+887        let endpoint = &mut *self.0.state.lock().unwrap();
+888        // If the driver is about to be on its own, ensure it can shut down if the last
+889        // connection is gone.
+890        if let Some(task) = endpoint.driver.take() {
+891            task.wake();
+892        }
+893    }
+894}
+895
+896impl std::ops::Deref for EndpointRef {
+897    type Target = EndpointInner;
+898    fn deref(&self) -> &Self::Target {
+899        &self.0
+900    }
+901}
+902
+903/// State directly involved in handling incoming packets
+904struct RecvState {
+905    incoming: VecDeque<proto::Incoming>,
+906    connections: ConnectionSet,
+907    recv_buf: Box<[u8]>,
+908    recv_limiter: WorkLimiter,
+909}
+910
+911impl RecvState {
+912    fn new(
+913        sender: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+914        max_receive_segments: NonZeroUsize,
+915        endpoint: &proto::Endpoint,
+916    ) -> Self {
+917        let recv_buf = vec![
+918            0;
+919            endpoint.config().get_max_udp_payload_size().min(64 * 1024) as usize
+920                * max_receive_segments.get()
+921                * BATCH_SIZE
+922        ];
+923        Self {
+924            connections: ConnectionSet {
+925                senders: FxHashMap::default(),
+926                sender,
+927                close: None,
+928                active_connections: 0,
+929            },
+930            incoming: VecDeque::new(),
+931            recv_buf: recv_buf.into(),
+932            recv_limiter: WorkLimiter::new(RECV_TIME_BOUND),
+933        }
+934    }
+935
+936    fn poll_socket(
+937        &mut self,
+938        cx: &mut Context<'_>,
+939        endpoint: &mut proto::Endpoint,
+940        socket: &mut dyn AsyncUdpSocket,
+941        sender: &mut Pin<Box<dyn UdpSender>>,
+942        runtime: &dyn Runtime,
+943        now: Instant,
+944    ) -> Result<PollProgress, io::Error> {
+945        let mut received_connection_packet = false;
+946        let mut metas = [RecvMeta::default(); BATCH_SIZE];
+947        let mut iovs: [IoSliceMut<'_>; BATCH_SIZE] = {
+948            let mut bufs = self
+949                .recv_buf
+950                .chunks_mut(self.recv_buf.len() / BATCH_SIZE)
+951                .map(IoSliceMut::new);
+952
+953            // expect() safe as self.recv_buf is chunked into BATCH_SIZE items
+954            // and iovs will be of size BATCH_SIZE, thus from_fn is called
+955            // exactly BATCH_SIZE times.
+956            std::array::from_fn(|_| bufs.next().expect("BATCH_SIZE elements"))
+957        };
+958        loop {
+959            match socket.poll_recv(cx, &mut iovs, &mut metas) {
+960                Poll::Ready(Ok(msgs)) => {
+961                    self.recv_limiter.record_work(msgs);
+962                    for (meta, buf) in metas.iter().zip(iovs.iter()).take(msgs) {
+963                        let mut data: BytesMut = buf[0..meta.len].into();
+964                        while !data.is_empty() {
+965                            let buf = data.split_to(meta.stride.min(data.len()));
+966                            let mut response_buffer = Vec::new();
+967                            let addresses = FourTuple::new(meta.addr, meta.dst_ip);
+968                            match endpoint.handle(
+969                                now,
+970                                addresses,
+971                                meta.ecn.map(proto_ecn),
+972                                buf,
+973                                &mut response_buffer,
+974                            ) {
+975                                Some(DatagramEvent::NewConnection(incoming)) => {
+976                                    if self.connections.close.is_none() {
+977                                        self.incoming.push_back(incoming);
+978                                    } else {
+979                                        let transmit =
+980                                            endpoint.refuse(incoming, &mut response_buffer);
+981                                        respond(transmit, &response_buffer, sender);
+982                                    }
+983                                }
+984                                Some(DatagramEvent::ConnectionEvent(handle, event)) => {
+985                                    // Ignoring errors from dropped connections that haven't yet been cleaned up
+986                                    received_connection_packet = true;
+987                                    let _ = self
+988                                        .connections
+989                                        .senders
+990                                        .get_mut(&handle)
+991                                        .unwrap()
+992                                        .send(ConnectionEvent::Proto(event));
+993                                }
+994                                Some(DatagramEvent::Response(transmit)) => {
+995                                    respond(transmit, &response_buffer, sender);
+996                                }
+997                                None => {}
+998                            }
+999                        }
+1000                    }
+1001                }
+1002                Poll::Pending => {
+1003                    return Ok(PollProgress {
+1004                        received_connection_packet,
+1005                        keep_going: false,
+1006                    });
+1007                }
+1008                // Ignore ECONNRESET as it's undefined in QUIC and may be injected by an
+1009                // attacker
+1010                Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::ConnectionReset => {
+1011                    continue;
+1012                }
+1013                Poll::Ready(Err(e)) => {
+1014                    return Err(e);
+1015                }
+1016            }
+1017            if !self.recv_limiter.allow_work(|| runtime.now()) {
+1018                return Ok(PollProgress {
+1019                    received_connection_packet,
+1020                    keep_going: true,
+1021                });
+1022            }
+1023        }
+1024    }
+1025}
+1026
+1027impl fmt::Debug for RecvState {
+1028    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+1029        f.debug_struct("RecvState")
+1030            .field("incoming", &self.incoming)
+1031            .field("connections", &self.connections)
+1032            // recv_buf too large
+1033            .field("recv_limiter", &self.recv_limiter)
+1034            .finish_non_exhaustive()
+1035    }
+1036}
+1037
+1038#[derive(Default)]
+1039struct PollProgress {
+1040    /// Whether a datagram was routed to an existing connection
+1041    received_connection_packet: bool,
+1042    /// Whether datagram handling was interrupted early by the work limiter for fairness
+1043    keep_going: bool,
+1044}
\ No newline at end of file diff --git a/pr/672/docs/src/noq/event_stream.rs.html b/pr/672/docs/src/noq/event_stream.rs.html new file mode 100644 index 000000000..0e9f5811e --- /dev/null +++ b/pr/672/docs/src/noq/event_stream.rs.html @@ -0,0 +1,114 @@ +event_stream.rs - source

noq/
event_stream.rs

1//! Newtype wrappers around broadcast receivers for connection events.
+2
+3use std::fmt;
+4use std::net::SocketAddr;
+5use std::pin::Pin;
+6use std::task::{Context, Poll};
+7
+8use proto::PathEvent;
+9use proto::n0_nat_traversal;
+10use thiserror::Error;
+11use tokio::sync::{broadcast, watch};
+12use tokio_stream::Stream;
+13use tokio_stream::wrappers::{BroadcastStream, WatchStream, errors::BroadcastStreamRecvError};
+14
+15/// The receiver lagged too far behind.
+16///
+17/// Attempting to receive again will return the oldest message still retained
+18/// by the channel.
+19#[derive(Debug, Clone, PartialEq, Eq, Error)]
+20#[error("channel lagged by {0}")]
+21pub struct Lagged(pub u64);
+22
+23/// A stream of [`PathEvent`]s for all paths in a connection.
+24#[derive(Debug)]
+25pub struct PathEvents {
+26    inner: BroadcastStream<PathEvent>,
+27}
+28
+29impl PathEvents {
+30    pub(crate) fn new(rx: broadcast::Receiver<PathEvent>) -> Self {
+31        Self {
+32            inner: BroadcastStream::new(rx),
+33        }
+34    }
+35}
+36
+37impl Stream for PathEvents {
+38    type Item = Result<PathEvent, Lagged>;
+39
+40    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+41        Pin::new(&mut self.inner)
+42            .poll_next(cx)
+43            .map(|opt| opt.map(|res| res.map_err(|BroadcastStreamRecvError::Lagged(n)| Lagged(n))))
+44    }
+45}
+46
+47/// A stream of NAT traversal updates for a connection.
+48#[derive(Debug)]
+49pub struct NatTraversalUpdates {
+50    inner: BroadcastStream<n0_nat_traversal::Event>,
+51}
+52
+53impl NatTraversalUpdates {
+54    pub(crate) fn new(rx: broadcast::Receiver<n0_nat_traversal::Event>) -> Self {
+55        Self {
+56            inner: BroadcastStream::new(rx),
+57        }
+58    }
+59}
+60
+61impl Stream for NatTraversalUpdates {
+62    type Item = Result<n0_nat_traversal::Event, Lagged>;
+63
+64    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+65        Pin::new(&mut self.inner)
+66            .poll_next(cx)
+67            .map(|opt| opt.map(|res| res.map_err(|BroadcastStreamRecvError::Lagged(n)| Lagged(n))))
+68    }
+69}
+70
+71/// Watches the external address reported by the peer for this connection.
+72///
+73/// Implements [`Stream`] yielding `SocketAddr` on each change.
+74/// Use [`get`](Self::get) to read the current value without waiting.
+75pub struct ObservedExternalAddr {
+76    rx: watch::Receiver<Option<SocketAddr>>,
+77    stream: WatchStream<Option<SocketAddr>>,
+78}
+79
+80impl ObservedExternalAddr {
+81    pub(crate) fn new(rx: watch::Receiver<Option<SocketAddr>>) -> Self {
+82        let stream = WatchStream::new(rx.clone());
+83        Self { rx, stream }
+84    }
+85
+86    /// Returns the most recently observed external address.
+87    ///
+88    /// `None` is returned if the peer has not yet reported an address. Retains
+89    /// the last value even after the stream is closed.
+90    pub fn get(&self) -> Option<SocketAddr> {
+91        *self.rx.borrow()
+92    }
+93}
+94
+95impl Stream for ObservedExternalAddr {
+96    type Item = SocketAddr;
+97
+98    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+99        loop {
+100            match Pin::new(&mut self.stream).poll_next(cx) {
+101                Poll::Ready(Some(Some(addr))) => return Poll::Ready(Some(addr)),
+102                Poll::Ready(Some(None)) => continue,
+103                Poll::Ready(None) => return Poll::Ready(None),
+104                Poll::Pending => return Poll::Pending,
+105            }
+106        }
+107    }
+108}
+109
+110impl fmt::Debug for ObservedExternalAddr {
+111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+112        f.debug_struct("ObservedExternalAddr").finish()
+113    }
+114}
\ No newline at end of file diff --git a/pr/672/docs/src/noq/incoming.rs.html b/pr/672/docs/src/noq/incoming.rs.html new file mode 100644 index 000000000..d36dcf1e9 --- /dev/null +++ b/pr/672/docs/src/noq/incoming.rs.html @@ -0,0 +1,160 @@ +incoming.rs - source

noq/
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, DecryptedInitial, 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    /// Decrypt the Initial packet payload
+103    ///
+104    /// This clones and decrypts the packet payload (~1200 bytes).
+105    /// Can be used to extract information from the TLS ClientHello without completing the handshake.
+106    pub fn decrypt(&self) -> Option<DecryptedInitial> {
+107        self.0.as_ref()?.inner.decrypt()
+108    }
+109}
+110
+111impl Drop for Incoming {
+112    fn drop(&mut self) {
+113        // Implicit reject, similar to Connection's implicit close
+114        if let Some(state) = self.0.take() {
+115            state.endpoint.refuse(state.inner);
+116        }
+117    }
+118}
+119
+120#[derive(Debug)]
+121struct State {
+122    inner: proto::Incoming,
+123    endpoint: EndpointRef,
+124}
+125
+126/// Error for attempting to retry an [`Incoming`] which already bears a token from a previous retry
+127#[derive(Debug, Error)]
+128#[error("retry() with validated Incoming")]
+129pub struct RetryError(Box<Incoming>);
+130
+131impl RetryError {
+132    /// Get the [`Incoming`]
+133    pub fn into_incoming(self) -> Incoming {
+134        *self.0
+135    }
+136}
+137
+138/// Basic adapter to let [`Incoming`] be `await`-ed like a [`Connecting`]
+139#[derive(Debug)]
+140pub struct IncomingFuture(Result<Connecting, ConnectionError>);
+141
+142impl Future for IncomingFuture {
+143    type Output = Result<Connection, ConnectionError>;
+144
+145    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+146        match &mut self.0 {
+147            Ok(connecting) => Pin::new(connecting).poll(cx),
+148            Err(e) => Poll::Ready(Err(e.clone())),
+149        }
+150    }
+151}
+152
+153impl IntoFuture for Incoming {
+154    type Output = Result<Connection, ConnectionError>;
+155    type IntoFuture = IncomingFuture;
+156
+157    fn into_future(self) -> Self::IntoFuture {
+158        IncomingFuture(self.accept())
+159    }
+160}
\ No newline at end of file diff --git a/pr/672/docs/src/noq/lib.rs.html b/pr/672/docs/src/noq/lib.rs.html new file mode 100644 index 000000000..8dccaeb02 --- /dev/null +++ b/pr/672/docs/src/noq/lib.rs.html @@ -0,0 +1,144 @@ +lib.rs - source

noq/
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 noq-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//! noq also exposes unreliable datagrams, which are a low-level primitive preferred when
+33//! automatic fragmentation and retransmission of certain data is not desired.
+34//!
+35//! QUIC uses encryption and identity verification built directly on TLS 1.3. Just as with a TLS
+36//! server, it is useful for a QUIC server to be identified by a certificate signed by a trusted
+37//! authority. If this is infeasible--for example, if servers are short-lived or not associated
+38//! with a domain name--then as with TLS, self-signed certificates can be used to provide
+39//! encryption alone.
+40#![warn(missing_docs)]
+41
+42use std::pin::Pin;
+43use std::sync::Arc;
+44
+45mod connection;
+46mod endpoint;
+47mod event_stream;
+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, ClosePathError, ClosedPath,
+65    ClosedStream, ConfigError, ConnectError, ConnectionClose, ConnectionError, ConnectionId,
+66    ConnectionIdGenerator, ConnectionStats, DecryptedInitial, Dir, EcnCodepoint, EndpointConfig,
+67    FourTuple, FrameStats, FrameType, IdleTimeout, InvalidCid, MtuDiscoveryConfig,
+68    NetworkChangeHint, NoneTokenLog, NoneTokenStore, PathError, PathEvent, PathId, PathStats,
+69    PathStatus, ServerConfig, SetPathStatusError, Side, StdSystemTime, StreamId, TimeSource,
+70    TokenLog, TokenMemoryCache, TokenReuseError, TokenStore, Transmit, TransportConfig,
+71    TransportErrorCode, UdpStats, ValidationTokenConfig, VarInt, VarIntBoundsExceeded, congestion,
+72    crypto,
+73};
+74#[cfg(feature = "qlog")]
+75pub use proto::{QlogConfig, QlogFactory, QlogFileFactory};
+76#[cfg(feature = "rustls")]
+77pub use rustls;
+78pub use udp;
+79
+80pub use crate::connection::{
+81    AcceptBi, AcceptUni, Closed, Connecting, Connection, OnClosed, OpenBi, OpenUni, ReadDatagram,
+82    SendDatagram, SendDatagramError, WeakConnectionHandle, ZeroRttAccepted,
+83};
+84pub use crate::endpoint::{Accept, Endpoint, EndpointStats};
+85pub use crate::event_stream::{Lagged, NatTraversalUpdates, ObservedExternalAddr, PathEvents};
+86pub use crate::incoming::{Incoming, IncomingFuture, RetryError};
+87pub use crate::path::{AddressDiscovery, OpenPath, Path, WeakPathHandle};
+88pub use crate::recv_stream::{
+89    ReadError, ReadExactError, ReadToEndError, RecvStream, ResetError, UnorderedRecvStream,
+90};
+91#[cfg(feature = "runtime-smol")]
+92pub use crate::runtime::SmolRuntime;
+93#[cfg(feature = "runtime-tokio")]
+94pub use crate::runtime::TokioRuntime;
+95#[cfg(any(feature = "runtime-tokio", feature = "runtime-smol"))]
+96pub use crate::runtime::default_runtime;
+97pub use crate::runtime::{AsyncTimer, AsyncUdpSocket, Runtime, UdpSender};
+98pub use crate::send_stream::{SendStream, Stopped, StoppedError, WriteError};
+99
+100#[cfg(test)]
+101mod tests;
+102
+103#[derive(Debug)]
+104enum ConnectionEvent {
+105    Close {
+106        error_code: VarInt,
+107        reason: bytes::Bytes,
+108    },
+109    Proto(proto::ConnectionEvent),
+110    Rebind(Pin<Box<dyn UdpSender>>),
+111    LocalAddressChanged(Option<Arc<dyn NetworkChangeHint + Sync + Send>>),
+112}
+113
+114fn udp_transmit<'a>(t: &proto::Transmit, buffer: &'a [u8]) -> udp::Transmit<'a> {
+115    udp::Transmit {
+116        destination: t.destination,
+117        ecn: t.ecn.map(udp_ecn),
+118        contents: buffer,
+119        segment_size: t.segment_size,
+120        src_ip: t.src_ip,
+121    }
+122}
+123
+124fn udp_ecn(ecn: proto::EcnCodepoint) -> udp::EcnCodepoint {
+125    match ecn {
+126        proto::EcnCodepoint::Ect0 => udp::EcnCodepoint::Ect0,
+127        proto::EcnCodepoint::Ect1 => udp::EcnCodepoint::Ect1,
+128        proto::EcnCodepoint::Ce => udp::EcnCodepoint::Ce,
+129    }
+130}
+131
+132/// Maximum number of datagrams processed in send/recv calls to make before moving on to other processing
+133///
+134/// This helps ensure we don't starve anything when the CPU is slower than the link.
+135/// Value is selected by picking a low number which didn't degrade throughput in benchmarks.
+136const IO_LOOP_BOUND: usize = 160;
+137
+138/// The maximum amount of time that should be spent in `recvmsg()` calls per endpoint iteration
+139///
+140/// 50us are chosen so that an endpoint iteration with a 50us sendmsg limit blocks
+141/// the runtime for a maximum of about 100us.
+142/// Going much lower does not yield any noticeable difference, since a single `recvmmsg`
+143/// batch of size 32 was observed to take 30us on some systems.
+144const RECV_TIME_BOUND: Duration = Duration::from_micros(50);
\ No newline at end of file diff --git a/pr/672/docs/src/noq/mutex.rs.html b/pr/672/docs/src/noq/mutex.rs.html new file mode 100644 index 000000000..f184b770b --- /dev/null +++ b/pr/672/docs/src/noq/mutex.rs.html @@ -0,0 +1,163 @@ +mutex.rs - source

noq/
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, MutexGuard};
+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, MutexGuard};
\ No newline at end of file diff --git a/pr/672/docs/src/noq/path.rs.html b/pr/672/docs/src/noq/path.rs.html new file mode 100644 index 000000000..03d09aa49 --- /dev/null +++ b/pr/672/docs/src/noq/path.rs.html @@ -0,0 +1,478 @@ +path.rs - source

noq/
path.rs

1use std::future::Future;
+2use std::net::{IpAddr, SocketAddr};
+3use std::pin::Pin;
+4use std::sync::Arc;
+5use std::sync::atomic::{AtomicUsize, Ordering};
+6use std::task::{Context, Poll, ready};
+7use std::time::Duration;
+8
+9use proto::{
+10    ClosePathError, ClosedPath, FourTuple, PathError, PathEvent, PathId, PathStats, PathStatus,
+11    SetPathStatusError, TransportErrorCode,
+12};
+13use tokio::sync::watch;
+14use tokio_stream::{Stream, wrappers::WatchStream};
+15
+16use crate::connection::ConnectionRef;
+17use crate::{Runtime, WeakConnectionHandle};
+18
+19/// Future produced by [`crate::Connection::open_path`]
+20pub struct OpenPath(OpenPathInner);
+21
+22enum OpenPathInner {
+23    /// Opening a path in underway
+24    ///
+25    /// This might fail later on.
+26    Ongoing {
+27        opened: WatchStream<Result<(), PathError>>,
+28        path_id: PathId,
+29        conn: ConnectionRef,
+30    },
+31    /// Opening a path failed immediately
+32    Rejected {
+33        /// The error that occurred
+34        err: PathError,
+35    },
+36    /// The path is already open
+37    Ready {
+38        path_id: PathId,
+39        conn: ConnectionRef,
+40    },
+41}
+42
+43impl OpenPath {
+44    pub(crate) fn new(
+45        path_id: PathId,
+46        opened: watch::Receiver<Result<(), PathError>>,
+47        conn: ConnectionRef,
+48    ) -> Self {
+49        Self(OpenPathInner::Ongoing {
+50            opened: WatchStream::from_changes(opened),
+51            path_id,
+52            conn,
+53        })
+54    }
+55
+56    pub(crate) fn ready(path_id: PathId, conn: ConnectionRef) -> Self {
+57        Self(OpenPathInner::Ready { path_id, conn })
+58    }
+59
+60    pub(crate) fn rejected(err: PathError) -> Self {
+61        Self(OpenPathInner::Rejected { err })
+62    }
+63
+64    /// Returns the path ID of the new path being opened.
+65    ///
+66    /// If an error occurred before a path ID was allocated, `None` is returned.  In this
+67    /// case the future is ready and polling it will immediately yield the error.
+68    ///
+69    /// The returned value remains the same for the entire lifetime of this future.
+70    pub fn path_id(&self) -> Option<PathId> {
+71        match self.0 {
+72            OpenPathInner::Ongoing { path_id, .. } => Some(path_id),
+73            OpenPathInner::Rejected { .. } => None,
+74            OpenPathInner::Ready { path_id, .. } => Some(path_id),
+75        }
+76    }
+77}
+78
+79impl Future for OpenPath {
+80    type Output = Result<Path, PathError>;
+81    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+82        match self.get_mut().0 {
+83            OpenPathInner::Ongoing {
+84                ref mut opened,
+85                path_id,
+86                ref mut conn,
+87            } => match ready!(Pin::new(opened).poll_next(ctx)) {
+88                Some(value) => {
+89                    Poll::Ready(value.map(|_| Path::new_unchecked(conn.clone(), path_id)))
+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::new_unchecked(conn.clone(), path_id))),
+102            OpenPathInner::Rejected { err } => Poll::Ready(Err(err)),
+103        }
+104    }
+105}
+106
+107/// An open network transmission within a multipath-enabled connection.
+108///
+109/// As long as a [`Path`] or [`WeakPathHandle`] is alive, it is ensured that the
+110/// [`PathStats`] for this path are not dropped even after the path is abandoned.
+111///
+112/// [`WeakPathHandle`]: crate::path::WeakPathHandle
+113#[derive(Debug, Clone)]
+114pub struct Path {
+115    conn: ConnectionRef,
+116    path_ref: PathRef,
+117}
+118
+119impl Path {
+120    /// Returns a [`Path`] for a path id, after checking that the path is not closed.
+121    pub(crate) fn new(conn: &ConnectionRef, id: PathId) -> Option<Self> {
+122        let path_ref = {
+123            let mut state = conn.lock_without_waking("Path::new");
+124            // TODO(flub): Using this to know if the path still exists is... hacky.
+125            state.inner.path_status(id).ok()?;
+126            state.acquire_path_ref(id)
+127        };
+128        Some(Self {
+129            conn: conn.clone(),
+130            path_ref,
+131        })
+132    }
+133
+134    /// Returns a [`Path`] for a path id without checking if the path exists or is closed.
+135    fn new_unchecked(conn: ConnectionRef, id: PathId) -> Self {
+136        let path_ref = conn
+137            .lock_without_waking("Path::new_unchecked")
+138            .acquire_path_ref(id);
+139        Self { conn, path_ref }
+140    }
+141
+142    /// Returns a [`WeakPathHandle`] for this path.
+143    ///
+144    /// Holding a [`WeakPathHandle`] does not keep a connection alive, but ensures that the
+145    /// path's stats are not dropped until the underlying connection is dropped, even if the
+146    /// path is abandoned.
+147    pub fn weak_handle(&self) -> WeakPathHandle {
+148        WeakPathHandle {
+149            path_ref: self.path_ref.clone(),
+150            conn: self.conn.weak_handle(),
+151        }
+152    }
+153
+154    /// The [`PathId`] of this path.
+155    pub fn id(&self) -> PathId {
+156        self.path_ref.id
+157    }
+158
+159    /// The current local [`PathStatus`] of this path.
+160    pub fn status(&self) -> Result<PathStatus, ClosedPath> {
+161        self.conn
+162            .lock_without_waking("path status")
+163            .inner
+164            .path_status(self.id())
+165    }
+166
+167    /// Sets the [`PathStatus`] of this path.
+168    ///
+169    /// Returns the previous status of the path.
+170    pub fn set_status(&self, status: PathStatus) -> Result<PathStatus, SetPathStatusError> {
+171        self.conn
+172            .lock_and_wake("set path status")
+173            .inner
+174            .set_path_status(self.id(), status)
+175    }
+176
+177    /// Returns the [`PathStats`] for this path.
+178    pub fn stats(&self) -> PathStats {
+179        // The `expect` is safe:
+180        // - `Path` can only be created for non-closed paths.
+181        // - `Path` and its clones or `WeakPathHandle`s all increment the connection state's `path_ref`
+182        //   reference counter
+183        // - As long as a path is not abandoned, its stats are available from `proto::Connection`
+184        // - If a path is abandoned, the `crate::Connection` stores the final stats as long as
+185        //   the path's refcount is not 0
+186        // - Therefore, we always get stats here.
+187        self.conn
+188            .lock_without_waking("Path::stats")
+189            .path_stats(self.id())
+190            .expect("either path stats or discarded path stats are always set as long as Path is not dropped")
+191    }
+192
+193    /// Closes this path.
+194    ///
+195    /// The path is immediately considered closed by the local endpoint. Once the state is removed,
+196    /// after a short period of time for any in-flight packets, a [`PathEvent::Abandoned`] is
+197    /// returned.
+198    pub fn close(&self) -> Result<(), ClosePathError> {
+199        let mut state = self.conn.lock_and_wake("close_path");
+200        state.inner.close_path(
+201            crate::Instant::now(),
+202            self.id(),
+203            TransportErrorCode::APPLICATION_ABANDON_PATH.into(),
+204        )
+205    }
+206
+207    /// Sets the max idle timeout for a specific path
+208    ///
+209    /// See [`TransportConfig::default_path_max_idle_timeout`] for details.
+210    ///
+211    /// Returns the previous value of the setting.
+212    ///
+213    /// [`TransportConfig::default_path_max_idle_timeout`]: crate::TransportConfig::default_path_max_idle_timeout
+214    pub fn set_max_idle_timeout(
+215        &self,
+216        timeout: Option<Duration>,
+217    ) -> Result<Option<Duration>, ClosedPath> {
+218        let mut state = self.conn.lock_and_wake("path_set_max_idle_timeout");
+219        let now = state.runtime.now();
+220        state
+221            .inner
+222            .set_path_max_idle_timeout(now, self.id(), timeout)
+223    }
+224
+225    /// Sets the keep_alive_interval for a specific path
+226    ///
+227    /// See [`TransportConfig::default_path_keep_alive_interval`] for details.
+228    ///
+229    /// Returns the previous value of the setting.
+230    ///
+231    /// [`TransportConfig::default_path_keep_alive_interval`]: crate::TransportConfig::default_path_keep_alive_interval
+232    pub fn set_keep_alive_interval(
+233        &self,
+234        interval: Option<Duration>,
+235    ) -> Result<Option<Duration>, ClosedPath> {
+236        let mut state = self.conn.lock_and_wake("path_set_keep_alive_interval");
+237        state
+238            .inner
+239            .set_path_keep_alive_interval(self.id(), interval)
+240    }
+241
+242    /// Track changes on our external address as reported by the peer.
+243    ///
+244    /// If the address-discovery extension is not negotiated, the stream will never return.
+245    pub fn observed_external_addr(&self) -> Result<AddressDiscovery, ClosedPath> {
+246        let state = self.conn.lock_without_waking("per_path_observed_address");
+247        let path_events = state.path_events.subscribe();
+248        let initial_value = state.inner.path_observed_address(self.id())?;
+249        Ok(AddressDiscovery::new(
+250            self.id(),
+251            path_events,
+252            initial_value,
+253            state.runtime.clone(),
+254        ))
+255    }
+256
+257    /// The peer's UDP address for this path.
+258    pub fn remote_address(&self) -> Result<SocketAddr, ClosedPath> {
+259        let state = self.conn.lock_without_waking("per_path_remote_address");
+260        Ok(state.inner.network_path(self.id())?.remote())
+261    }
+262
+263    /// The local IP used for this path, if known.
+264    ///
+265    /// Returns `Ok(None)` for clients or when the platform does not expose this information; see
+266    /// [`noq_udp::RecvMeta::dst_ip`](udp::RecvMeta::dst_ip) for supported platforms.
+267    pub fn local_ip(&self) -> Result<Option<IpAddr>, ClosedPath> {
+268        let state = self.conn.lock_without_waking("per_path_local_ip");
+269        Ok(state.inner.network_path(self.id())?.local_ip())
+270    }
+271
+272    /// The network path used for this path.
+273    ///
+274    /// Returns a [`FourTuple`], combining [`Self::remote_address`] and [`Self::local_ip`].
+275    pub fn network_path(&self) -> Result<FourTuple, ClosedPath> {
+276        let state = self.conn.lock_without_waking("per_path_local_ip");
+277        state.inner.network_path(self.id())
+278    }
+279
+280    /// Ping the remote endpoint over this path.
+281    pub fn ping(&self) -> Result<(), ClosedPath> {
+282        let mut state = self.conn.lock_and_wake("ping");
+283        state.inner.ping_path(self.id())
+284    }
+285}
+286
+287impl Drop for Path {
+288    fn drop(&mut self) {
+289        self.path_ref.on_drop(&self.conn);
+290    }
+291}
+292
+293impl PartialEq for Path {
+294    fn eq(&self, other: &Self) -> bool {
+295        self.id() == other.id() && self.conn.stable_id() == other.conn.stable_id()
+296    }
+297}
+298
+299/// Weak handle for a [`Path`] that does not keep the connection alive.
+300///
+301/// As long as a [`WeakPathHandle`] for a path exists, that path's final stats will not be dropped even if
+302/// the path was abandoned.
+303///
+304/// The [`WeakPathHandle`] can be upgraded to a [`Path`] as long as its [`Connection`] has not been dropped.
+305///
+306/// [`Connection`]: crate::Connection
+307#[derive(Debug, Clone)]
+308pub struct WeakPathHandle {
+309    conn: WeakConnectionHandle,
+310    path_ref: PathRef,
+311}
+312
+313impl PartialEq for WeakPathHandle {
+314    fn eq(&self, other: &Self) -> bool {
+315        self.id() == other.id() && self.conn.is_same_connection(&other.conn)
+316    }
+317}
+318
+319impl Eq for WeakPathHandle {}
+320
+321impl WeakPathHandle {
+322    /// Returns the [`PathId`] of this path.
+323    pub fn id(&self) -> PathId {
+324        self.path_ref.id
+325    }
+326
+327    /// Upgrades to a [`Path`].
+328    ///
+329    /// Returns `None` if the connection was dropped.
+330    pub fn upgrade(&self) -> Option<Path> {
+331        let conn = self.conn.upgrade_to_ref()?;
+332        Some(Path {
+333            conn,
+334            path_ref: self.path_ref.clone(),
+335        })
+336    }
+337}
+338
+339impl Drop for WeakPathHandle {
+340    fn drop(&mut self) {
+341        if let Some(conn) = self.conn.upgrade_to_ref() {
+342            self.path_ref.on_drop(&conn);
+343        }
+344    }
+345}
+346
+347/// Owner side of a path's reference counter, stored in [`State::path_refs`].
+348///
+349/// Holds the shared [`AtomicUsize`] but does not itself contribute to the count.
+350/// Hands out [`PathRef`] handles via [`Self::acquire`].
+351///
+352/// [`State::path_refs`]: crate::connection::State::path_refs
+353#[derive(Debug, Default)]
+354pub(crate) struct PathRefOwner {
+355    ref_count: Arc<AtomicUsize>,
+356}
+357
+358impl PathRefOwner {
+359    /// Acquire a new [`PathRef`] handle, bumping the reference counter by 1.
+360    pub(crate) fn acquire(&self, path_id: PathId) -> PathRef {
+361        self.ref_count.fetch_add(1, Ordering::Relaxed);
+362        PathRef {
+363            id: path_id,
+364            ref_count: self.ref_count.clone(),
+365        }
+366    }
+367}
+368
+369/// Handle side of a path's reference counter, held by [`Path`] and [`WeakPathHandle`].
+370///
+371/// Cloning bumps the counter automatically. When dropping a `PathRef`, holders must call
+372/// [`Self::on_drop`] to decrement the counter and clear the corresponding entries
+373/// from the connection state if the counter reaches zero.
+374///
+375/// [`WeakPathHandle`]: crate::path::WeakPathHandle
+376#[derive(Debug)]
+377pub(crate) struct PathRef {
+378    id: PathId,
+379    ref_count: Arc<AtomicUsize>,
+380}
+381
+382impl Clone for PathRef {
+383    fn clone(&self) -> Self {
+384        self.ref_count.fetch_add(1, Ordering::Relaxed);
+385        Self {
+386            id: self.id,
+387            ref_count: self.ref_count.clone(),
+388        }
+389    }
+390}
+391
+392impl PathRef {
+393    /// Decreases the refcount and clears state once the counter reaches zero.
+394    ///
+395    /// This must be called before dropping a [`PathRef`], i.e. in the [`Drop`] impl
+396    /// of its holder.
+397    fn on_drop(&self, conn: &ConnectionRef) {
+398        if self.ref_count.fetch_sub(1, Ordering::Relaxed) > 1 {
+399            return;
+400        }
+401        let mut state = conn.lock_without_waking("PathRef::drop");
+402        // Re-check under the lock: a concurrent `Path::new` may have bumped
+403        // the counter back up between our `fetch_sub` and the lock.
+404        if self.ref_count.load(Ordering::Relaxed) > 0 {
+405            return;
+406        }
+407        state.path_refs.remove(&self.id);
+408        state.final_path_stats.remove(&self.id);
+409    }
+410}
+411
+412/// Stream produced by [`Path::observed_external_addr`]
+413///
+414/// This will always return the external address most recently reported by the remote over this
+415/// path. If the extension is not negotiated, this stream will never return.
+416// TODO(@divma): provide a way to check if the extension is negotiated.
+417pub struct AddressDiscovery {
+418    watcher: WatchStream<SocketAddr>,
+419}
+420
+421impl AddressDiscovery {
+422    pub(super) fn new(
+423        path_id: PathId,
+424        mut path_events: tokio::sync::broadcast::Receiver<PathEvent>,
+425        initial_value: Option<SocketAddr>,
+426        runtime: Arc<dyn Runtime>,
+427    ) -> Self {
+428        let (tx, rx) = watch::channel(initial_value.unwrap_or_else(||
+429                // if the dummy value is used, it will be ignored
+430                SocketAddr::new([0, 0, 0, 0].into(), 0)));
+431        let filter = async move {
+432            loop {
+433                match path_events.recv().await {
+434                    Ok(PathEvent::ObservedAddr {
+435                        id, addr: observed, ..
+436                    }) if id == path_id => {
+437                        tx.send_if_modified(|addr| {
+438                            let old = std::mem::replace(addr, observed);
+439                            old != *addr
+440                        });
+441                    }
+442                    Ok(PathEvent::Discarded { id, .. }) if id == path_id => {
+443                        // If the path is closed, terminate the stream
+444                        break;
+445                    }
+446                    Ok(_) => {
+447                        // ignore any other event
+448                    }
+449                    Err(_) => {
+450                        // A lagged error should never happen since this (detached) task is
+451                        // constantly reading from the channel. Therefore, if an error does happen,
+452                        // the stream can terminate
+453                        break;
+454                    }
+455                }
+456            }
+457        };
+458
+459        let watcher = if initial_value.is_some() {
+460            WatchStream::new(rx)
+461        } else {
+462            WatchStream::from_changes(rx)
+463        };
+464
+465        runtime.spawn(Box::pin(filter));
+466        // TODO(@divma): check if there's a way to ensure the future ends. AbortHandle is not an
+467        // option
+468        Self { watcher }
+469    }
+470}
+471
+472impl Stream for AddressDiscovery {
+473    type Item = SocketAddr;
+474
+475    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+476        Pin::new(&mut self.watcher).poll_next(cx)
+477    }
+478}
\ No newline at end of file diff --git a/pr/672/docs/src/noq/recv_stream.rs.html b/pr/672/docs/src/noq/recv_stream.rs.html new file mode 100644 index 000000000..ade0ce745 --- /dev/null +++ b/pr/672/docs/src/noq/recv_stream.rs.html @@ -0,0 +1,788 @@ +recv_stream.rs - source

noq/
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(crate) 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    /// Reads the next segment of data as zero-copy [`Bytes`].
+160    ///
+161    /// Yields `None` if the stream was finished. Otherwise, yields the next segment of data. The
+162    /// chunk's offset will be immediately after the last data yielded by [`RecvStream::read`] or
+163    /// [`RecvStream::read_chunk`]; use [`bytes_read()`](Self::bytes_read) to query that offset
+164    /// explicitly.
+165    ///
+166    /// For unordered reads, convert the stream into an unordered stream using [`Self::into_unordered`].
+167    ///
+168    /// Slightly more efficient than [`RecvStream::read`] due to not copying. Chunk boundaries do
+169    /// not correspond to peer writes, and hence cannot be used as framing.
+170    ///
+171    /// This operation is cancel-safe.
+172    pub async fn read_chunk(&mut self, max_length: usize) -> Result<Option<Bytes>, ReadError> {
+173        Ok(ReadChunk {
+174            stream: self,
+175            max_length,
+176            ordered: true,
+177        }
+178        .await?
+179        .map(|chunk| chunk.bytes))
+180    }
+181
+182    /// Attempts to read a chunk from the stream.
+183    ///
+184    /// On success, returns `Poll::Ready(Ok(Some(chunk)))`. If `Poll::Ready(Ok(None))`
+185    /// is returned, it implies that EOF has been reached.
+186    ///
+187    /// If no data is available for reading, the method returns `Poll::Pending`
+188    /// and arranges for the current task (via cx.waker()) to receive a notification
+189    /// when the stream becomes readable or is closed.
+190    fn poll_read_chunk(
+191        &mut self,
+192        cx: &mut Context<'_>,
+193        max_length: usize,
+194        ordered: bool,
+195    ) -> Poll<Result<Option<Chunk>, ReadError>> {
+196        self.poll_read_generic(cx, ordered, |chunks| match chunks.next(max_length) {
+197            Ok(Some(chunk)) => ReadStatus::Readable(chunk),
+198            res => (None, res.err()).into(),
+199        })
+200    }
+201
+202    /// Reads the next segments of data.
+203    ///
+204    /// Fills `bufs` with the segments of data beginning immediately after the last data yielded
+205    /// by [`read`](Self::read), [`read_chunk`](Self::read_chunk), or
+206    /// [`read_many_chunks`](Self::read_many_chunks), or `None` if the stream was finished.
+207    ///
+208    /// Slightly more efficient than [`read`](Self::read) due to not copying. Chunk boundaries do
+209    /// not correspond to peer writes, and hence cannot be used as framing.
+210    ///
+211    /// This operation is cancel-safe.
+212    pub async fn read_many_chunks(
+213        &mut self,
+214        bufs: &mut [Bytes],
+215    ) -> Result<Option<usize>, ReadError> {
+216        ReadChunks { stream: self, bufs }.await
+217    }
+218
+219    /// Foundation of [`Self::read_many_chunks`]
+220    fn poll_read_chunks(
+221        &mut self,
+222        cx: &mut Context<'_>,
+223        bufs: &mut [Bytes],
+224    ) -> Poll<Result<Option<usize>, ReadError>> {
+225        if bufs.is_empty() {
+226            return Poll::Ready(Ok(Some(0)));
+227        }
+228
+229        self.poll_read_generic(cx, true, |chunks| {
+230            let mut read = 0;
+231            loop {
+232                if read >= bufs.len() {
+233                    // We know `read > 0` because `bufs` cannot be empty here
+234                    return ReadStatus::Readable(read);
+235                }
+236
+237                match chunks.next(usize::MAX) {
+238                    Ok(Some(chunk)) => {
+239                        bufs[read] = chunk.bytes;
+240                        read += 1;
+241                    }
+242                    res => return (if read == 0 { None } else { Some(read) }, res.err()).into(),
+243                }
+244            }
+245        })
+246    }
+247
+248    /// Convenience method to read all remaining data into a buffer
+249    ///
+250    /// Fails with [`ReadToEndError::TooLong`] on reading more than `size_limit` bytes, discarding
+251    /// all data read. Uses unordered reads to be more efficient than using `AsyncRead` would
+252    /// allow. `size_limit` should be set to limit worst-case memory use.
+253    ///
+254    /// If unordered reads have already been made, the resulting buffer may have gaps containing
+255    /// arbitrary data.
+256    ///
+257    /// This operation is *not* cancel-safe.
+258    ///
+259    /// [`ReadToEndError::TooLong`]: crate::ReadToEndError::TooLong
+260    pub async fn read_to_end(&mut self, size_limit: usize) -> Result<Vec<u8>, ReadToEndError> {
+261        ReadToEnd {
+262            stream: self,
+263            size_limit,
+264            read: Vec::new(),
+265            start: u64::MAX,
+266            end: 0,
+267        }
+268        .await
+269    }
+270
+271    /// Stop accepting data
+272    ///
+273    /// Discards unread data and notifies the peer to stop transmitting. Once stopped, further
+274    /// attempts to operate on a stream will yield `ClosedStream` errors.
+275    pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+276        let mut conn = self.conn.lock_and_wake("RecvStream::stop");
+277        if self.is_0rtt && conn.check_0rtt().is_err() {
+278            conn.skip_waking();
+279            return Ok(());
+280        }
+281        conn.inner.recv_stream(self.stream).stop(error_code)?;
+282        self.all_data_read = true;
+283        // Clean up shared state that might be left over from a cancelled read
+284        // operation, so `drop` doesn't have to
+285        conn.blocked_readers.remove(&self.stream);
+286        Ok(())
+287    }
+288
+289    /// Check if this stream has been opened during 0-RTT.
+290    ///
+291    /// In which case any non-idempotent request should be considered dangerous at the application
+292    /// level. Because read data is subject to replay attacks.
+293    pub fn is_0rtt(&self) -> bool {
+294        self.is_0rtt
+295    }
+296
+297    /// Get the identity of this stream
+298    pub fn id(&self) -> StreamId {
+299        self.stream
+300    }
+301
+302    /// Returns the number of bytes read from this stream.
+303    ///
+304    /// This is the offset of the next byte to be read, i.e. the length of the contiguous
+305    /// prefix of the stream consumed by the application.
+306    pub fn bytes_read(&self) -> Result<u64, ClosedStream> {
+307        let mut conn = self.conn.lock_without_waking("RecvStream::bytes_read");
+308        conn.inner.recv_stream(self.stream).bytes_read()
+309    }
+310
+311    /// Completes when the stream has been reset by the peer or otherwise closed
+312    ///
+313    /// Yields `Some` with the reset error code when the stream is reset by the peer. Yields `None`
+314    /// when the stream was previously [`stop()`](Self::stop)ed, or when the stream was
+315    /// [`finish()`](crate::SendStream::finish)ed by the peer and all data has been received, after
+316    /// which it is no longer meaningful for the stream to be reset.
+317    ///
+318    /// This operation is cancel-safe.
+319    pub async fn received_reset(&mut self) -> Result<Option<VarInt>, ResetError> {
+320        poll_fn(|cx| {
+321            let mut conn = self.conn.lock_without_waking("RecvStream::reset");
+322            if self.is_0rtt && conn.check_0rtt().is_err() {
+323                return Poll::Ready(Err(ResetError::ZeroRttRejected));
+324            }
+325
+326            if let Some(code) = self.reset {
+327                return Poll::Ready(Ok(Some(code)));
+328            }
+329
+330            match conn.inner.recv_stream(self.stream).received_reset() {
+331                Err(_) => Poll::Ready(Ok(None)),
+332                Ok(Some(error_code)) => {
+333                    // Stream state has just now been freed, so the connection may need to issue new
+334                    // stream ID flow control credit
+335                    conn.wake();
+336                    Poll::Ready(Ok(Some(error_code)))
+337                }
+338                Ok(None) => {
+339                    if let Some(e) = &conn.error {
+340                        return Poll::Ready(Err(e.clone().into()));
+341                    }
+342                    // Resets always notify readers, since a reset is an immediate read error. We
+343                    // could introduce a dedicated channel to reduce the risk of spurious wakeups,
+344                    // but that increased complexity is probably not justified, as an application
+345                    // that is expecting a reset is not likely to receive large amounts of data.
+346                    conn.blocked_readers.insert(self.stream, cx.waker().clone());
+347                    Poll::Pending
+348                }
+349            }
+350        })
+351        .await
+352    }
+353
+354    /// Handle common logic related to reading out of a receive stream
+355    ///
+356    /// This takes an `FnMut` closure that takes care of the actual reading process, matching
+357    /// the detailed read semantics for the calling function with a particular return type.
+358    /// The closure can read from the passed `&mut Chunks` and has to return the status after
+359    /// reading: the amount of data read, and the status after the final read call.
+360    fn poll_read_generic<T, U>(
+361        &mut self,
+362        cx: &mut Context<'_>,
+363        ordered: bool,
+364        mut read_fn: T,
+365    ) -> Poll<Result<Option<U>, ReadError>>
+366    where
+367        T: FnMut(&mut Chunks<'_>) -> ReadStatus<U>,
+368    {
+369        use proto::ReadError::*;
+370        if self.all_data_read {
+371            return Poll::Ready(Ok(None));
+372        }
+373
+374        let mut conn = self.conn.lock_without_waking("RecvStream::poll_read");
+375        if self.is_0rtt {
+376            conn.check_0rtt().map_err(|()| ReadError::ZeroRttRejected)?;
+377        }
+378
+379        // If we stored an error during a previous call, return it now. This can happen if a
+380        // `read_fn` both wants to return data and also returns an error in its final stream status.
+381        let status = match self.reset {
+382            Some(code) => ReadStatus::Failed(None, Reset(code)),
+383            None => {
+384                let mut recv = conn.inner.recv_stream(self.stream);
+385                let mut chunks = recv.read(ordered).map_err(|e| match e {
+386                    ReadableError::ClosedStream => ReadError::ClosedStream,
+387                    ReadableError::IllegalOrderedRead => {
+388                        // We should never get here because the only way to do unordered reads is
+389                        // via UnorderedRecvStream, which allows only unordered reads. It is not
+390                        // possible to get a RecvStream from an UnorderedRecvStream.
+391                        unreachable!("ordered read after unordered read")
+392                    }
+393                })?;
+394                let status = read_fn(&mut chunks);
+395                if chunks.finalize().should_transmit() {
+396                    conn.wake();
+397                }
+398                status
+399            }
+400        };
+401
+402        match status {
+403            ReadStatus::Readable(read) => Poll::Ready(Ok(Some(read))),
+404            ReadStatus::Finished(read) => {
+405                self.all_data_read = true;
+406                Poll::Ready(Ok(read))
+407            }
+408            ReadStatus::Failed(read, Blocked) => match read {
+409                Some(val) => Poll::Ready(Ok(Some(val))),
+410                None => {
+411                    if let Some(ref x) = conn.error {
+412                        return Poll::Ready(Err(ReadError::ConnectionLost(x.clone())));
+413                    }
+414                    conn.blocked_readers.insert(self.stream, cx.waker().clone());
+415                    Poll::Pending
+416                }
+417            },
+418            ReadStatus::Failed(read, Reset(error_code)) => match read {
+419                None => {
+420                    self.all_data_read = true;
+421                    self.reset = Some(error_code);
+422                    Poll::Ready(Err(ReadError::Reset(error_code)))
+423                }
+424                done => {
+425                    self.reset = Some(error_code);
+426                    Poll::Ready(Ok(done))
+427                }
+428            },
+429        }
+430    }
+431
+432    /// Converts this stream into an unordered stream.
+433    pub fn into_unordered(self) -> UnorderedRecvStream {
+434        UnorderedRecvStream { inner: self }
+435    }
+436}
+437
+438/// A stream that can be used to receive data out-of-order.
+439///
+440/// Obtained by converting a [`RecvStream`] via [`RecvStream::into_unordered`].
+441///
+442/// This variant of `RecvStream` allows reading chunks of data *exclusively*
+443/// out of order. Once you have done an unordered read, ordered reads are no
+444/// longer possible since data may have been consumed out of order.
+445///
+446/// The stream state related fns like [`Self::id`], [`Self::is_0rtt`], [`Self::stop`], and
+447/// [`Self::received_reset`] behave exactly as on [`RecvStream`].
+448#[derive(Debug)]
+449pub struct UnorderedRecvStream {
+450    inner: RecvStream,
+451}
+452
+453impl UnorderedRecvStream {
+454    /// Reads the next segment of data.
+455    ///
+456    /// Yields `None` if the stream was finished. Otherwise, yields a segment of data and its
+457    /// offset in the stream. Segments may be received in any order, and the `Chunk`'s `offset`
+458    /// field can be used to determine ordering in the caller. Unordered reads are less prone
+459    /// to head-of-line blocking within a stream, but require the application to manage
+460    /// reassembling the original data.
+461    ///
+462    /// This operation is cancel-safe.
+463    pub async fn read_chunk(&mut self, max_length: usize) -> Result<Option<Chunk>, ReadError> {
+464        ReadChunk {
+465            stream: &mut self.inner,
+466            max_length,
+467            ordered: false,
+468        }
+469        .await
+470    }
+471
+472    /// Get the identity of this stream
+473    pub fn id(&self) -> StreamId {
+474        self.inner.id()
+475    }
+476
+477    /// Check if this stream has been opened during 0-RTT.
+478    ///
+479    /// In which case any non-idempotent request should be considered dangerous at the application
+480    /// level. Because read data is subject to replay attacks.
+481    pub fn is_0rtt(&self) -> bool {
+482        self.inner.is_0rtt()
+483    }
+484
+485    /// Stop accepting data
+486    ///
+487    /// Discards unread data and notifies the peer to stop transmitting. Once stopped, further
+488    /// attempts to operate on a stream will yield `ClosedStream` errors.
+489    pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+490        self.inner.stop(error_code)
+491    }
+492
+493    /// Completes when the stream has been reset by the peer or otherwise closed
+494    ///
+495    /// Yields `Some` with the reset error code when the stream is reset by the peer. Yields `None`
+496    /// when the stream was previously [`stop()`](Self::stop)ed, or when the stream was
+497    /// [`finish()`](crate::SendStream::finish)ed by the peer and all data has been received, after
+498    /// which it is no longer meaningful for the stream to be reset.
+499    ///
+500    /// This operation is cancel-safe.
+501    pub async fn received_reset(&mut self) -> Result<Option<VarInt>, ResetError> {
+502        self.inner.received_reset().await
+503    }
+504}
+505
+506enum ReadStatus<T> {
+507    Readable(T),
+508    Finished(Option<T>),
+509    Failed(Option<T>, proto::ReadError),
+510}
+511
+512impl<T> From<(Option<T>, Option<proto::ReadError>)> for ReadStatus<T> {
+513    fn from(status: (Option<T>, Option<proto::ReadError>)) -> Self {
+514        match status {
+515            (read, None) => Self::Finished(read),
+516            (read, Some(e)) => Self::Failed(read, e),
+517        }
+518    }
+519}
+520
+521/// Future produced by [`RecvStream::read_to_end()`].
+522///
+523/// [`RecvStream::read_to_end()`]: crate::RecvStream::read_to_end
+524struct ReadToEnd<'a> {
+525    stream: &'a mut RecvStream,
+526    read: Vec<(Bytes, u64)>,
+527    start: u64,
+528    end: u64,
+529    size_limit: usize,
+530}
+531
+532impl Future for ReadToEnd<'_> {
+533    type Output = Result<Vec<u8>, ReadToEndError>;
+534    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+535        loop {
+536            match ready!(self.stream.poll_read_chunk(cx, usize::MAX, false))? {
+537                Some(chunk) => {
+538                    self.start = self.start.min(chunk.offset);
+539                    let end = chunk.bytes.len() as u64 + chunk.offset;
+540                    if (end - self.start) > self.size_limit as u64 {
+541                        return Poll::Ready(Err(ReadToEndError::TooLong));
+542                    }
+543                    self.end = self.end.max(end);
+544                    self.read.push((chunk.bytes, chunk.offset));
+545                }
+546                None => {
+547                    if self.end == 0 {
+548                        // Never received anything
+549                        return Poll::Ready(Ok(Vec::new()));
+550                    }
+551                    let start = self.start;
+552                    let mut buffer = vec![0; (self.end - start) as usize];
+553                    for (data, offset) in self.read.drain(..) {
+554                        let offset = (offset - start) as usize;
+555                        buffer[offset..offset + data.len()].copy_from_slice(&data);
+556                    }
+557                    return Poll::Ready(Ok(buffer));
+558                }
+559            }
+560        }
+561    }
+562}
+563
+564/// Errors from [`RecvStream::read_to_end`]
+565#[derive(Debug, Error, Clone, PartialEq, Eq)]
+566pub enum ReadToEndError {
+567    /// An error occurred during reading
+568    #[error("read error: {0}")]
+569    Read(#[from] ReadError),
+570    /// The stream is larger than the user-supplied limit
+571    #[error("stream too long")]
+572    TooLong,
+573}
+574
+575#[cfg(feature = "futures-io")]
+576impl futures_io::AsyncRead for RecvStream {
+577    fn poll_read(
+578        self: Pin<&mut Self>,
+579        cx: &mut Context<'_>,
+580        buf: &mut [u8],
+581    ) -> Poll<io::Result<usize>> {
+582        let mut buf = ReadBuf::new(buf);
+583        ready!(Self::poll_read_buf(self.get_mut(), cx, &mut buf))?;
+584        Poll::Ready(Ok(buf.filled().len()))
+585    }
+586}
+587
+588impl tokio::io::AsyncRead for RecvStream {
+589    fn poll_read(
+590        self: Pin<&mut Self>,
+591        cx: &mut Context<'_>,
+592        buf: &mut ReadBuf<'_>,
+593    ) -> Poll<io::Result<()>> {
+594        ready!(Self::poll_read_buf(self.get_mut(), cx, buf))?;
+595        Poll::Ready(Ok(()))
+596    }
+597}
+598
+599impl Drop for RecvStream {
+600    fn drop(&mut self) {
+601        if self.all_data_read {
+602            debug_assert!(
+603                !self
+604                    .conn
+605                    .lock_without_waking("RecvStream:drop")
+606                    .blocked_readers
+607                    .contains_key(&self.stream),
+608                "Stream {} should not have a blocked reader when all data read is true",
+609                &self.stream
+610            );
+611            return;
+612        }
+613        let mut conn = self.conn.lock_and_wake("RecvStream::drop");
+614
+615        // clean up any previously registered wakers
+616        conn.blocked_readers.remove(&self.stream);
+617
+618        if conn.error.is_some() || (self.is_0rtt && conn.check_0rtt().is_err()) {
+619            conn.skip_waking();
+620            return;
+621        }
+622
+623        // Ignore ClosedStream errors
+624        let _ = conn.inner.recv_stream(self.stream).stop(0u32.into());
+625    }
+626}
+627
+628/// Errors that arise from reading from a stream.
+629#[derive(Debug, Error, Clone, PartialEq, Eq)]
+630pub enum ReadError {
+631    /// The peer abandoned transmitting data on this stream
+632    ///
+633    /// Carries an application-defined error code.
+634    #[error("stream reset by peer: error {0}")]
+635    Reset(VarInt),
+636    /// The connection was lost
+637    #[error("connection lost")]
+638    ConnectionLost(#[from] ConnectionError),
+639    /// The stream has already been stopped, finished, or reset
+640    #[error("closed stream")]
+641    ClosedStream,
+642    /// This was a 0-RTT stream and the server rejected it
+643    ///
+644    /// Can only occur on clients for 0-RTT streams, which can be opened using
+645    /// [`Connecting::into_0rtt()`].
+646    ///
+647    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+648    #[error("0-RTT rejected")]
+649    ZeroRttRejected,
+650}
+651
+652impl From<ResetError> for ReadError {
+653    fn from(e: ResetError) -> Self {
+654        match e {
+655            ResetError::ConnectionLost(e) => Self::ConnectionLost(e),
+656            ResetError::ZeroRttRejected => Self::ZeroRttRejected,
+657        }
+658    }
+659}
+660
+661impl From<ReadError> for io::Error {
+662    fn from(x: ReadError) -> Self {
+663        use ReadError::*;
+664        let kind = match x {
+665            Reset { .. } | ZeroRttRejected => io::ErrorKind::ConnectionReset,
+666            ConnectionLost(_) | ClosedStream => io::ErrorKind::NotConnected,
+667        };
+668        Self::new(kind, x)
+669    }
+670}
+671
+672/// Errors that arise while waiting for a stream to be reset
+673#[derive(Debug, Error, Clone, PartialEq, Eq)]
+674pub enum ResetError {
+675    /// The connection was lost
+676    #[error("connection lost")]
+677    ConnectionLost(#[from] ConnectionError),
+678    /// This was a 0-RTT stream and the server rejected it
+679    ///
+680    /// Can only occur on clients for 0-RTT streams, which can be opened using
+681    /// [`Connecting::into_0rtt()`].
+682    ///
+683    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+684    #[error("0-RTT rejected")]
+685    ZeroRttRejected,
+686}
+687
+688impl From<ResetError> for io::Error {
+689    fn from(x: ResetError) -> Self {
+690        use ResetError::*;
+691        let kind = match x {
+692            ZeroRttRejected => io::ErrorKind::ConnectionReset,
+693            ConnectionLost(_) => io::ErrorKind::NotConnected,
+694        };
+695        Self::new(kind, x)
+696    }
+697}
+698
+699/// Future produced by [`RecvStream::read()`].
+700///
+701/// [`RecvStream::read()`]: crate::RecvStream::read
+702struct Read<'a> {
+703    stream: &'a mut RecvStream,
+704    buf: ReadBuf<'a>,
+705}
+706
+707impl Future for Read<'_> {
+708    type Output = Result<Option<usize>, ReadError>;
+709
+710    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+711        let this = self.get_mut();
+712        ready!(this.stream.poll_read_buf(cx, &mut this.buf))?;
+713        match this.buf.filled().len() {
+714            0 if this.buf.capacity() != 0 => Poll::Ready(Ok(None)),
+715            n => Poll::Ready(Ok(Some(n))),
+716        }
+717    }
+718}
+719
+720/// Future produced by [`RecvStream::read_exact()`].
+721///
+722/// [`RecvStream::read_exact()`]: crate::RecvStream::read_exact
+723struct ReadExact<'a> {
+724    stream: &'a mut RecvStream,
+725    buf: ReadBuf<'a>,
+726}
+727
+728impl Future for ReadExact<'_> {
+729    type Output = Result<(), ReadExactError>;
+730    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+731        let this = self.get_mut();
+732        let mut remaining = this.buf.remaining();
+733        while remaining > 0 {
+734            ready!(this.stream.poll_read_buf(cx, &mut this.buf))?;
+735            let new = this.buf.remaining();
+736            if new == remaining {
+737                return Poll::Ready(Err(ReadExactError::FinishedEarly(this.buf.filled().len())));
+738            }
+739            remaining = new;
+740        }
+741        Poll::Ready(Ok(()))
+742    }
+743}
+744
+745/// Errors that arise from reading from a stream.
+746#[derive(Debug, Error, Clone, PartialEq, Eq)]
+747pub enum ReadExactError {
+748    /// The stream finished before all bytes were read
+749    #[error("stream finished early ({0} bytes read)")]
+750    FinishedEarly(usize),
+751    /// A read error occurred
+752    #[error(transparent)]
+753    ReadError(#[from] ReadError),
+754}
+755
+756/// Future produced by [`RecvStream::read_chunk()`] or [`UnorderedRecvStream::read_chunk()`].
+757///
+758/// [`RecvStream::read_chunk()`]: crate::RecvStream::read_chunk
+759/// [`UnorderedRecvStream::read_chunk()`]: crate::UnorderedRecvStream::read_chunk
+760struct ReadChunk<'a> {
+761    stream: &'a mut RecvStream,
+762    max_length: usize,
+763    ordered: bool,
+764}
+765
+766impl Future for ReadChunk<'_> {
+767    type Output = Result<Option<Chunk>, ReadError>;
+768    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+769        let (max_length, ordered) = (self.max_length, self.ordered);
+770        self.stream.poll_read_chunk(cx, max_length, ordered)
+771    }
+772}
+773
+774/// Future produced by [`RecvStream::read_many_chunks()`].
+775///
+776/// [`RecvStream::read_many_chunks()`]: crate::RecvStream::read_many_chunks
+777struct ReadChunks<'a> {
+778    stream: &'a mut RecvStream,
+779    bufs: &'a mut [Bytes],
+780}
+781
+782impl Future for ReadChunks<'_> {
+783    type Output = Result<Option<usize>, ReadError>;
+784    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+785        let this = self.get_mut();
+786        this.stream.poll_read_chunks(cx, this.bufs)
+787    }
+788}
\ No newline at end of file diff --git a/pr/672/docs/src/noq/runtime/mod.rs.html b/pr/672/docs/src/noq/runtime/mod.rs.html new file mode 100644 index 000000000..7556ac37d --- /dev/null +++ b/pr/672/docs/src/noq/runtime/mod.rs.html @@ -0,0 +1,251 @@ +mod.rs - source

noq/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    /// noq 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/672/docs/src/noq/runtime/smol.rs.html b/pr/672/docs/src/noq/runtime/smol.rs.html new file mode 100644 index 000000000..b4c82ed2b --- /dev/null +++ b/pr/672/docs/src/noq/runtime/smol.rs.html @@ -0,0 +1,102 @@ +smol.rs - source

noq/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 noq 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/672/docs/src/noq/runtime/tokio.rs.html b/pr/672/docs/src/noq/runtime/tokio.rs.html new file mode 100644 index 000000000..c7dd8b409 --- /dev/null +++ b/pr/672/docs/src/noq/runtime/tokio.rs.html @@ -0,0 +1,106 @@ +tokio.rs - source

noq/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 noq 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/672/docs/src/noq/send_stream.rs.html b/pr/672/docs/src/noq/send_stream.rs.html new file mode 100644 index 000000000..801f7d568 --- /dev/null +++ b/pr/672/docs/src/noq/send_stream.rs.html @@ -0,0 +1,488 @@ +send_stream.rs - source

noq/
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 pin_project_lite::pin_project;
+10use proto::{ClosedStream, ConnectionError, FinishError, StreamId};
+11use thiserror::Error;
+12use tokio::sync::futures::OwnedNotified;
+13
+14use crate::{
+15    VarInt,
+16    connection::{ConnectionRef, State},
+17};
+18
+19/// A stream that can only be used to send data
+20///
+21/// If dropped, streams that haven't been explicitly [`reset()`] will be implicitly [`finish()`]ed,
+22/// continuing to (re)transmit previously written data until it has been fully acknowledged or the
+23/// connection is closed.
+24///
+25/// # Cancellation
+26///
+27/// A `write` method is said to be *cancel-safe* when dropping its future before the future becomes
+28/// ready will always result in no data being written to the stream. This is true of methods which
+29/// succeed immediately when any progress is made, and is not true of methods which might need to
+30/// perform multiple writes internally before succeeding. Each `write` method documents whether it is
+31/// cancel-safe.
+32///
+33/// [`reset()`]: SendStream::reset
+34/// [`finish()`]: SendStream::finish
+35#[derive(Debug)]
+36pub struct SendStream {
+37    conn: ConnectionRef,
+38    stream: StreamId,
+39    is_0rtt: bool,
+40}
+41
+42impl SendStream {
+43    pub(crate) fn new(conn: ConnectionRef, stream: StreamId, is_0rtt: bool) -> Self {
+44        Self {
+45            conn,
+46            stream,
+47            is_0rtt,
+48        }
+49    }
+50
+51    /// Write a buffer into this stream, returning how many bytes were written
+52    ///
+53    /// Unless this method errors, it waits until some amount of `buf` can be written into this
+54    /// stream, and then writes as much as it can without waiting again. Due to congestion and flow
+55    /// control, this may be shorter than `buf.len()`. On success this yields the length of the
+56    /// prefix that was written.
+57    ///
+58    /// # Cancel safety
+59    ///
+60    /// This method is cancellation safe. If this does not resolve, no bytes were written.
+61    pub async fn write(&mut self, buf: &[u8]) -> Result<usize, WriteError> {
+62        poll_fn(|cx| self.execute_poll(cx, |s| s.write(buf))).await
+63    }
+64
+65    /// Write a buffer into this stream in its entirety
+66    ///
+67    /// This method repeatedly calls [`write`](Self::write) until all bytes are written, or an
+68    /// error occurs.
+69    ///
+70    /// # Cancel safety
+71    ///
+72    /// This method is *not* cancellation safe. Even if this does not resolve, some prefix of `buf`
+73    /// may have been written when previously polled.
+74    pub async fn write_all(&mut self, mut buf: &[u8]) -> Result<(), WriteError> {
+75        while !buf.is_empty() {
+76            let written = self.write(buf).await?;
+77            buf = &buf[written..];
+78        }
+79        Ok(())
+80    }
+81
+82    /// Writes [`Bytes`] from a slice of buffers into this stream, returning how many bytes were.
+83    /// written
+84    ///
+85    /// Bytes to try to write are provided to this method as an array of cheaply cloneable chunks.
+86    /// Unless this method errors, it waits until some amount of those bytes can be written into
+87    /// this stream, and then writes as much as it can without waiting again. Due to congestion and
+88    /// flow control, this may be less than the total number of bytes.
+89    ///
+90    /// On success, this method both mutates `bufs` and returns the number of bytes written:
+91    ///
+92    /// - `bufs` is advanced past chunks that were fully written.
+93    /// - If a [`Bytes`] chunk was partially written, the chunk at the new front of `bufs` is
+94    ///   [split to](Bytes::split_to) contain only the suffix of bytes that were not 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_many_chunks(
+100        &mut self,
+101        bufs: &mut &mut [Bytes],
+102    ) -> Result<usize, WriteError> {
+103        poll_fn(|cx| self.execute_poll(cx, |s| s.write_chunks(bufs))).await
+104    }
+105
+106    /// Writes a single [`Bytes`] into this stream in its entirety.
+107    ///
+108    /// Bytes to write are provided to this method as a single cheaply cloneable chunk. This
+109    /// method repeatedly calls [`write_many_chunks`](Self::write_many_chunks) until all bytes
+110    /// are written, or an error occurs.
+111    ///
+112    /// # Cancel safety
+113    ///
+114    /// This method is *not* cancellation safe. Even if this does not resolve, some bytes may have
+115    /// been written when previously polled.
+116    pub async fn write_chunk(&mut self, buf: Bytes) -> Result<(), WriteError> {
+117        self.write_all_chunks(&mut [buf]).await
+118    }
+119
+120    /// Writes a slice of [`Bytes`] into this stream in its entirety.
+121    ///
+122    /// Bytes to write are provided to this method as an array of cheaply cloneable chunks. This
+123    /// method repeatedly calls [`write_many_chunks`](Self::write_many_chunks) until all bytes are
+124    /// written, or an error occurs.
+125    ///
+126    /// # Cancel safety
+127    ///
+128    /// This method is *not* cancellation safe. Even if this does not resolve, some bytes may have
+129    /// been written when previously polled.
+130    pub async fn write_all_chunks(&mut self, bufs: &mut [Bytes]) -> Result<(), WriteError> {
+131        let mut bufs = &mut bufs[..];
+132        while !bufs.is_empty() {
+133            self.write_many_chunks(&mut bufs).await?;
+134        }
+135        Ok(())
+136    }
+137
+138    fn execute_poll<F, R>(
+139        &mut self,
+140        cx: &mut Context<'_>,
+141        write_fn: F,
+142    ) -> Poll<Result<R, WriteError>>
+143    where
+144        F: FnOnce(&mut proto::SendStream<'_>) -> Result<R, proto::WriteError>,
+145    {
+146        use proto::WriteError::*;
+147        let mut conn = self.conn.lock_and_wake("SendStream::poll_write");
+148        if self.is_0rtt && conn.check_0rtt().is_err() {
+149            conn.skip_waking();
+150            return Poll::Ready(Err(WriteError::ZeroRttRejected));
+151        }
+152        if let Some(conn_err) = conn.error.clone() {
+153            conn.skip_waking();
+154            return Poll::Ready(Err(WriteError::ConnectionLost(conn_err)));
+155        }
+156
+157        let result = match write_fn(&mut conn.inner.send_stream(self.stream)) {
+158            Ok(result) => result,
+159            Err(Blocked) => {
+160                conn.blocked_writers.insert(self.stream, cx.waker().clone());
+161                conn.skip_waking();
+162                return Poll::Pending;
+163            }
+164            Err(Stopped(error_code)) => {
+165                conn.skip_waking();
+166                return Poll::Ready(Err(WriteError::Stopped(error_code)));
+167            }
+168            Err(ClosedStream) => {
+169                conn.skip_waking();
+170                return Poll::Ready(Err(WriteError::ClosedStream));
+171            }
+172        };
+173
+174        Poll::Ready(Ok(result))
+175    }
+176
+177    /// Notify the peer that no more data will ever be written to this stream
+178    ///
+179    /// It is an error to write to a [`SendStream`] after `finish()`ing it. [`reset()`](Self::reset)
+180    /// may still be called after `finish` to abandon transmission of any stream data that might
+181    /// still be buffered.
+182    ///
+183    /// To wait for the peer to receive all buffered stream data, see [`stopped()`](Self::stopped).
+184    ///
+185    /// May fail if [`finish()`](Self::finish) or [`reset()`](Self::reset) was previously
+186    /// called. This error is harmless and serves only to indicate that the caller may have
+187    /// incorrect assumptions about the stream's state.
+188    pub fn finish(&mut self) -> Result<(), ClosedStream> {
+189        let mut conn = self.conn.lock_and_wake("finish");
+190        if let Err(e) = conn.inner.send_stream(self.stream).finish() {
+191            conn.skip_waking();
+192            match e {
+193                FinishError::ClosedStream => Err(ClosedStream::default()),
+194                // Harmless. If the application needs to know about stopped streams at this point, it
+195                // should call `stopped`.
+196                FinishError::Stopped(_) => Ok(()),
+197            }
+198        } else {
+199            Ok(())
+200        }
+201    }
+202
+203    /// Close the send stream immediately.
+204    ///
+205    /// No new data can be written after calling this method. Locally buffered data is dropped, and
+206    /// previously transmitted data will no longer be retransmitted if lost. If an attempt has
+207    /// already been made to finish the stream, the peer may still receive all written data.
+208    ///
+209    /// May fail if [`finish()`](Self::finish) or [`reset()`](Self::reset) was previously
+210    /// called. This error is harmless and serves only to indicate that the caller may have
+211    /// incorrect assumptions about the stream's state.
+212    pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+213        let mut conn = self.conn.lock_and_wake("SendStream::reset");
+214        if self.is_0rtt && conn.check_0rtt().is_err() {
+215            conn.skip_waking();
+216            return Ok(());
+217        }
+218        conn.inner.send_stream(self.stream).reset(error_code)?;
+219        Ok(())
+220    }
+221
+222    /// Set the priority of the send stream
+223    ///
+224    /// Every send stream has an initial priority of 0. Locally buffered data from streams with
+225    /// higher priority will be transmitted before data from streams with lower priority. Changing
+226    /// the priority of a stream with pending data may only take effect after that data has been
+227    /// transmitted. Using many different priority levels per connection may have a negative
+228    /// impact on performance.
+229    pub fn set_priority(&self, priority: i32) -> Result<(), ClosedStream> {
+230        let mut conn = self.conn.lock_without_waking("SendStream::set_priority");
+231        conn.inner.send_stream(self.stream).set_priority(priority)?;
+232        Ok(())
+233    }
+234
+235    /// Get the priority of the send stream
+236    pub fn priority(&self) -> Result<i32, ClosedStream> {
+237        let mut conn = self.conn.lock_without_waking("SendStream::priority");
+238        conn.inner.send_stream(self.stream).priority()
+239    }
+240
+241    /// Completes when the peer stops the stream or reads the stream to completion
+242    ///
+243    /// Yields `Some` with the stop error code if the peer stops the stream. Yields `None` if the
+244    /// local side [`finish()`](Self::finish)es the stream and then the peer acknowledges receipt
+245    /// of all stream data (although not necessarily the processing of it), after which the peer
+246    /// closing the stream is no longer meaningful.
+247    ///
+248    /// For a variety of reasons, the peer may not send acknowledgements immediately upon receiving
+249    /// data. As such, relying on `stopped` to know when the peer has read a stream to completion
+250    /// may introduce more latency than using an application-level response of some sort.
+251    pub fn stopped(&self) -> Stopped {
+252        let notified = {
+253            // Create an `OwnedNotified` to move into the future. By creating it before the first poll,
+254            // we make sure that we don't miss any notifications.
+255            let mut conn = self.conn.lock_without_waking("SendStream::stopped");
+256            conn.stopped
+257                .entry(self.stream)
+258                .or_default()
+259                .clone()
+260                .notified_owned()
+261        };
+262        Stopped {
+263            conn: self.conn.clone(),
+264            stream: self.stream,
+265            is_0rtt: self.is_0rtt,
+266            notified,
+267        }
+268    }
+269
+270    /// Get the identity of this stream
+271    pub fn id(&self) -> StreamId {
+272        self.stream
+273    }
+274
+275    /// Attempt to write bytes from buf into the stream.
+276    ///
+277    /// On success, returns Poll::Ready(Ok(num_bytes_written)).
+278    ///
+279    /// If the stream is not ready for writing, the method returns Poll::Pending and arranges
+280    /// for the current task (via cx.waker().wake_by_ref()) to receive a notification when the
+281    /// stream becomes writable or is closed.
+282    pub fn poll_write(
+283        self: Pin<&mut Self>,
+284        cx: &mut Context<'_>,
+285        buf: &[u8],
+286    ) -> Poll<Result<usize, WriteError>> {
+287        pin!(self.get_mut().write(buf)).as_mut().poll(cx)
+288    }
+289}
+290
+291/// Check if a send stream is stopped.
+292///
+293/// Returns `Some` if the stream is stopped or the connection is closed.
+294/// Returns `None` if the stream is not stopped.
+295fn send_stream_stopped(
+296    conn: &mut State,
+297    stream: StreamId,
+298    is_0rtt: bool,
+299) -> Option<Result<Option<VarInt>, StoppedError>> {
+300    if is_0rtt && conn.check_0rtt().is_err() {
+301        return Some(Err(StoppedError::ZeroRttRejected));
+302    }
+303    match conn.inner.send_stream(stream).stopped() {
+304        Err(ClosedStream { .. }) => Some(Ok(None)),
+305        Ok(Some(error_code)) => Some(Ok(Some(error_code))),
+306        Ok(None) => conn.error.clone().map(|error| Err(error.into())),
+307    }
+308}
+309
+310#[cfg(feature = "futures-io")]
+311impl futures_io::AsyncWrite for SendStream {
+312    fn poll_write(
+313        self: Pin<&mut Self>,
+314        cx: &mut Context<'_>,
+315        buf: &[u8],
+316    ) -> Poll<io::Result<usize>> {
+317        self.poll_write(cx, buf).map_err(Into::into)
+318    }
+319
+320    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+321        Poll::Ready(Ok(()))
+322    }
+323
+324    fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+325        Poll::Ready(self.get_mut().finish().map_err(Into::into))
+326    }
+327}
+328
+329impl tokio::io::AsyncWrite for SendStream {
+330    fn poll_write(
+331        self: Pin<&mut Self>,
+332        cx: &mut Context<'_>,
+333        buf: &[u8],
+334    ) -> Poll<io::Result<usize>> {
+335        self.poll_write(cx, buf).map_err(Into::into)
+336    }
+337
+338    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+339        Poll::Ready(Ok(()))
+340    }
+341
+342    fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+343        Poll::Ready(self.get_mut().finish().map_err(Into::into))
+344    }
+345}
+346
+347impl Drop for SendStream {
+348    fn drop(&mut self) {
+349        let mut conn = self.conn.lock_and_wake("SendStream::drop");
+350
+351        // clean up any previously registered wakers
+352        conn.blocked_writers.remove(&self.stream);
+353
+354        if conn.error.is_some() || (self.is_0rtt && conn.check_0rtt().is_err()) {
+355            conn.skip_waking();
+356            return;
+357        }
+358        match conn.inner.send_stream(self.stream).finish() {
+359            Ok(()) => {}
+360            Err(FinishError::Stopped(reason)) => {
+361                if conn.inner.send_stream(self.stream).reset(reason).is_err() {
+362                    conn.skip_waking()
+363                }
+364            }
+365            // Already finished or reset, which is fine.
+366            Err(FinishError::ClosedStream) => {
+367                conn.skip_waking();
+368            }
+369        }
+370    }
+371}
+372
+373/// Errors that arise from writing to a stream
+374#[derive(Debug, Error, Clone, PartialEq, Eq)]
+375pub enum WriteError {
+376    /// The peer is no longer accepting data on this stream
+377    ///
+378    /// Carries an application-defined error code.
+379    #[error("sending stopped by peer: error {0}")]
+380    Stopped(VarInt),
+381    /// The connection was lost
+382    #[error("connection lost")]
+383    ConnectionLost(#[from] ConnectionError),
+384    /// The stream has already been finished or reset
+385    #[error("closed stream")]
+386    ClosedStream,
+387    /// This was a 0-RTT stream and the server rejected it
+388    ///
+389    /// Can only occur on clients for 0-RTT streams, which can be opened using
+390    /// [`Connecting::into_0rtt()`].
+391    ///
+392    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+393    #[error("0-RTT rejected")]
+394    ZeroRttRejected,
+395}
+396
+397impl From<ClosedStream> for WriteError {
+398    #[inline]
+399    fn from(_: ClosedStream) -> Self {
+400        Self::ClosedStream
+401    }
+402}
+403
+404impl From<StoppedError> for WriteError {
+405    fn from(x: StoppedError) -> Self {
+406        match x {
+407            StoppedError::ConnectionLost(e) => Self::ConnectionLost(e),
+408            StoppedError::ZeroRttRejected => Self::ZeroRttRejected,
+409        }
+410    }
+411}
+412
+413impl From<WriteError> for io::Error {
+414    fn from(x: WriteError) -> Self {
+415        use WriteError::*;
+416        let kind = match x {
+417            Stopped(_) | ZeroRttRejected => io::ErrorKind::ConnectionReset,
+418            ConnectionLost(_) | ClosedStream => io::ErrorKind::NotConnected,
+419        };
+420        Self::new(kind, x)
+421    }
+422}
+423
+424/// Errors that arise while monitoring for a send stream stop from the peer
+425#[derive(Debug, Error, Clone, PartialEq, Eq)]
+426pub enum StoppedError {
+427    /// The connection was lost
+428    #[error("connection lost")]
+429    ConnectionLost(#[from] ConnectionError),
+430    /// This was a 0-RTT stream and the server rejected it
+431    ///
+432    /// Can only occur on clients for 0-RTT streams, which can be opened using
+433    /// [`Connecting::into_0rtt()`].
+434    ///
+435    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+436    #[error("0-RTT rejected")]
+437    ZeroRttRejected,
+438}
+439
+440impl From<StoppedError> for io::Error {
+441    fn from(x: StoppedError) -> Self {
+442        use StoppedError::*;
+443        let kind = match x {
+444            ZeroRttRejected => io::ErrorKind::ConnectionReset,
+445            ConnectionLost(_) => io::ErrorKind::NotConnected,
+446        };
+447        Self::new(kind, x)
+448    }
+449}
+450
+451pin_project! {
+452    /// Future returned from [`SendStream::stopped`].
+453    #[derive(Debug)]
+454    pub struct Stopped {
+455        conn: ConnectionRef,
+456        stream: StreamId,
+457        is_0rtt: bool,
+458        #[pin]
+459        notified: OwnedNotified,
+460    }
+461}
+462
+463impl Future for Stopped {
+464    type Output = Result<Option<VarInt>, StoppedError>;
+465
+466    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+467        let mut this = self.project();
+468        loop {
+469            let mut conn = this.conn.lock_without_waking("SendStream::stopped");
+470            // Check if the stream is stopped before polling the notify. This makes sure that
+471            // no wakeups are missed.
+472            if let Some(output) = send_stream_stopped(&mut conn, *this.stream, *this.is_0rtt) {
+473                return Poll::Ready(output);
+474            }
+475            std::task::ready!(this.notified.as_mut().poll(cx));
+476        }
+477    }
+478}
+479
+480#[cfg(test)]
+481mod tests {
+482    fn check_is_send_sync<A: Send + Sync>() {}
+483
+484    #[allow(dead_code)]
+485    fn test_bounds() {
+486        check_is_send_sync::<super::Stopped>();
+487    }
+488}
\ No newline at end of file diff --git a/pr/672/docs/src/noq/work_limiter.rs.html b/pr/672/docs/src/noq/work_limiter.rs.html new file mode 100644 index 000000000..a94d3824d --- /dev/null +++ b/pr/672/docs/src/noq/work_limiter.rs.html @@ -0,0 +1,224 @@ +work_limiter.rs - source

noq/
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/672/docs/src/noq_perf/perf.rs.html b/pr/672/docs/src/noq_perf/perf.rs.html new file mode 100644 index 000000000..4f377e286 --- /dev/null +++ b/pr/672/docs/src/noq_perf/perf.rs.html @@ -0,0 +1,46 @@ +perf.rs - source

noq_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/672/docs/src/noq_proto/address_discovery.rs.html b/pr/672/docs/src/noq_proto/address_discovery.rs.html new file mode 100644 index 000000000..ecc530e19 --- /dev/null +++ b/pr/672/docs/src/noq_proto/address_discovery.rs.html @@ -0,0 +1,126 @@ +address_discovery.rs - source

noq_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/672/docs/src/noq_proto/bloom_token_log.rs.html b/pr/672/docs/src/noq_proto/bloom_token_log.rs.html new file mode 100644 index 000000000..537e210e2 --- /dev/null +++ b/pr/672/docs/src/noq_proto/bloom_token_log.rs.html @@ -0,0 +1,368 @@ +bloom_token_log.rs - source

noq_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/672/docs/src/noq_proto/cid_generator.rs.html b/pr/672/docs/src/noq_proto/cid_generator.rs.html new file mode 100644 index 000000000..fb610863a --- /dev/null +++ b/pr/672/docs/src/noq_proto/cid_generator.rs.html @@ -0,0 +1,181 @@ +cid_generator.rs - source

noq_proto/
cid_generator.rs

1use std::hash::Hasher;
+2
+3use rand::Rng;
+4use rand::RngExt;
+5
+6use crate::Duration;
+7use crate::MAX_CID_SIZE;
+8use crate::shared::ConnectionId;
+9
+10/// Generates connection IDs for incoming connections
+11pub trait ConnectionIdGenerator: Send + Sync {
+12    /// Generates a new CID
+13    ///
+14    /// Connection IDs MUST NOT contain any information that can be used by
+15    /// an external observer (that is, one that does not cooperate with the
+16    /// issuer) to correlate them with other connection IDs for the same
+17    /// connection. They MUST have high entropy, e.g. due to encrypted data
+18    /// or cryptographic-grade random data.
+19    fn generate_cid(&mut self) -> ConnectionId;
+20
+21    /// Quickly determine whether `cid` could have been generated by this generator
+22    ///
+23    /// False positives are permitted, but increase the cost of handling invalid packets.
+24    fn validate(&self, _cid: ConnectionId) -> Result<(), InvalidCid> {
+25        Ok(())
+26    }
+27
+28    /// Returns the length of a CID for connections created by this generator
+29    fn cid_len(&self) -> usize;
+30    /// Returns the lifetime of generated Connection IDs
+31    ///
+32    /// Connection IDs will be retired after the returned `Duration`, if any. Assumed to be constant.
+33    fn cid_lifetime(&self) -> Option<Duration>;
+34}
+35
+36/// The connection ID was not recognized by the [`ConnectionIdGenerator`]
+37#[derive(Debug, Copy, Clone)]
+38pub struct InvalidCid;
+39
+40/// Generates purely random connection IDs of a specified length
+41///
+42/// Random CIDs can be smaller than those produced by [`HashedConnectionIdGenerator`], but cannot be
+43/// usefully [`validate`](ConnectionIdGenerator::validate)d.
+44#[derive(Debug, Clone, Copy)]
+45pub struct RandomConnectionIdGenerator {
+46    cid_len: usize,
+47    lifetime: Option<Duration>,
+48}
+49
+50impl Default for RandomConnectionIdGenerator {
+51    fn default() -> Self {
+52        Self {
+53            cid_len: 8,
+54            lifetime: None,
+55        }
+56    }
+57}
+58
+59impl RandomConnectionIdGenerator {
+60    /// Initialize Random CID generator with a fixed CID length
+61    ///
+62    /// The given length must be less than or equal to MAX_CID_SIZE.
+63    pub fn new(cid_len: usize) -> Self {
+64        debug_assert!(cid_len <= MAX_CID_SIZE);
+65        Self {
+66            cid_len,
+67            ..Self::default()
+68        }
+69    }
+70
+71    /// Set the lifetime of CIDs created by this generator
+72    pub fn set_lifetime(&mut self, d: Duration) -> &mut Self {
+73        self.lifetime = Some(d);
+74        self
+75    }
+76}
+77
+78impl ConnectionIdGenerator for RandomConnectionIdGenerator {
+79    fn generate_cid(&mut self) -> ConnectionId {
+80        let mut bytes_arr = [0; MAX_CID_SIZE];
+81        rand::rng().fill_bytes(&mut bytes_arr[..self.cid_len]);
+82
+83        ConnectionId::new(&bytes_arr[..self.cid_len])
+84    }
+85
+86    /// Provide the length of dst_cid in short header packet
+87    fn cid_len(&self) -> usize {
+88        self.cid_len
+89    }
+90
+91    fn cid_lifetime(&self) -> Option<Duration> {
+92        self.lifetime
+93    }
+94}
+95
+96/// Generates 8-byte connection IDs that can be efficiently
+97/// [`validate`](ConnectionIdGenerator::validate)d
+98///
+99/// This generator uses a non-cryptographic hash and can therefore still be spoofed, but nonetheless
+100/// helps prevents noq from responding to non-QUIC packets at very low cost.
+101pub struct HashedConnectionIdGenerator {
+102    key: u64,
+103    lifetime: Option<Duration>,
+104}
+105
+106impl HashedConnectionIdGenerator {
+107    /// Create a generator with a random key
+108    pub fn new() -> Self {
+109        Self::from_key(rand::rng().random())
+110    }
+111
+112    /// Create a generator with a specific key
+113    ///
+114    /// Allows [`validate`](ConnectionIdGenerator::validate) to recognize a consistent set of
+115    /// connection IDs across restarts
+116    pub fn from_key(key: u64) -> Self {
+117        Self {
+118            key,
+119            lifetime: None,
+120        }
+121    }
+122
+123    /// Set the lifetime of CIDs created by this generator
+124    pub fn set_lifetime(&mut self, d: Duration) -> &mut Self {
+125        self.lifetime = Some(d);
+126        self
+127    }
+128}
+129
+130impl Default for HashedConnectionIdGenerator {
+131    fn default() -> Self {
+132        Self::new()
+133    }
+134}
+135
+136impl ConnectionIdGenerator for HashedConnectionIdGenerator {
+137    fn generate_cid(&mut self) -> ConnectionId {
+138        let mut bytes_arr = [0; NONCE_LEN + SIGNATURE_LEN];
+139        rand::rng().fill_bytes(&mut bytes_arr[..NONCE_LEN]);
+140        let mut hasher = rustc_hash::FxHasher::default();
+141        hasher.write_u64(self.key);
+142        hasher.write(&bytes_arr[..NONCE_LEN]);
+143        bytes_arr[NONCE_LEN..].copy_from_slice(&hasher.finish().to_le_bytes()[..SIGNATURE_LEN]);
+144        ConnectionId::new(&bytes_arr)
+145    }
+146
+147    fn validate(&self, cid: ConnectionId) -> Result<(), InvalidCid> {
+148        let (nonce, signature) = cid.split_at(NONCE_LEN);
+149        let mut hasher = rustc_hash::FxHasher::default();
+150        hasher.write_u64(self.key);
+151        hasher.write(nonce);
+152        let expected = hasher.finish().to_le_bytes();
+153        match expected[..SIGNATURE_LEN] == signature[..] {
+154            true => Ok(()),
+155            false => Err(InvalidCid),
+156        }
+157    }
+158
+159    fn cid_len(&self) -> usize {
+160        NONCE_LEN + SIGNATURE_LEN
+161    }
+162
+163    fn cid_lifetime(&self) -> Option<Duration> {
+164        self.lifetime
+165    }
+166}
+167
+168const NONCE_LEN: usize = 3; // Good for more than 16 million connections
+169const SIGNATURE_LEN: usize = 8 - NONCE_LEN; // 8-byte total CID length
+170
+171#[cfg(test)]
+172mod tests {
+173    use super::*;
+174
+175    #[test]
+176    fn validate_keyed_cid() {
+177        let mut generator = HashedConnectionIdGenerator::new();
+178        let cid = generator.generate_cid();
+179        generator.validate(cid).unwrap();
+180    }
+181}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/cid_queue.rs.html b/pr/672/docs/src/noq_proto/cid_queue.rs.html new file mode 100644 index 000000000..1b520749d --- /dev/null +++ b/pr/672/docs/src/noq_proto/cid_queue.rs.html @@ -0,0 +1,519 @@ +cid_queue.rs - source

noq_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    ///
+39    /// The reserved CIDs section of the buffer, if non-empty will always be ahead of the active
+40    /// CID.
+41    cursor_reserved: usize,
+42}
+43
+44impl CidQueue {
+45    pub(crate) fn new(cid: ConnectionId) -> Self {
+46        let mut buffer = [None; Self::LEN];
+47        buffer[0] = Some(CidData(cid, None));
+48        Self {
+49            buffer,
+50            cursor: 0,
+51            offset: 0,
+52            cursor_reserved: 0,
+53        }
+54    }
+55
+56    /// Handle a `NEW_CONNECTION_ID` frame
+57    ///
+58    /// Returns a non-empty range of retired sequence numbers and the reset token of the new active
+59    /// CID iff any CIDs were retired.
+60    pub(crate) fn insert(
+61        &mut self,
+62        cid: NewConnectionId,
+63    ) -> Result<Option<(Range<u64>, ResetToken)>, InsertError> {
+64        // Position of new CID wrt. the current active CID
+65        let Some(index) = cid.sequence.checked_sub(self.offset) else {
+66            return Err(InsertError::Retired);
+67        };
+68
+69        let retired_count = cid.retire_prior_to.saturating_sub(self.offset);
+70        if index >= Self::LEN as u64 + retired_count {
+71            return Err(InsertError::ExceedsLimit);
+72        }
+73
+74        // Discard retired CIDs, if any
+75        for i in 0..(retired_count.min(Self::LEN as u64) as usize) {
+76            self.buffer[(self.cursor + i) % Self::LEN] = None;
+77        }
+78
+79        // Record the new CID
+80        let index = ((self.cursor as u64 + index) % Self::LEN as u64) as usize;
+81        self.buffer[index] = Some(CidData(cid.id, Some(cid.reset_token)));
+82
+83        if retired_count == 0 {
+84            return Ok(None);
+85        }
+86
+87        // The active CID was retired. Find the first known CID with sequence number of at least
+88        // retire_prior_to, and inform the caller that all prior CIDs have been retired, and of
+89        // the new CID's reset token.
+90        self.cursor = ((self.cursor as u64 + retired_count) % Self::LEN as u64) as usize;
+91        let (i, CidData(_, token)) = self
+92            .iter_from_active()
+93            .next()
+94            .expect("it is impossible to retire a CID without supplying a new one");
+95        self.cursor = (self.cursor + i) % Self::LEN;
+96        self.cursor_reserved = self.cursor;
+97        let orig_offset = self.offset;
+98        self.offset = cid.retire_prior_to + i as u64;
+99        // We don't immediately retire CIDs in the range (orig_offset +
+100        // Self::LEN)..self.offset. These are CIDs that we haven't yet received from a
+101        // NEW_CONNECTION_ID frame, since having previously received them would violate the
+102        // connection ID limit we specified based on Self::LEN. If we do receive a such a frame
+103        // in the future, e.g. due to reordering, we'll retire it then. This ensures we can't be
+104        // made to buffer an arbitrarily large number of RETIRE_CONNECTION_ID frames.
+105        Ok(Some((
+106            orig_offset..self.offset.min(orig_offset + Self::LEN as u64),
+107            token.expect("non-initial CID missing reset token"),
+108        )))
+109    }
+110
+111    /// Switch to next active CID if possible, return
+112    /// 1) the corresponding ResetToken and 2) a non-empty range preceding it to retire
+113    pub(crate) fn next(&mut self) -> Option<(ResetToken, Range<u64>)> {
+114        let (i, cid_data) = self.iter_from_reserved().nth(1)?;
+115        let reserved = self.reserved_len();
+116        for j in 0..=reserved {
+117            self.buffer[(self.cursor + j) % Self::LEN] = None;
+118        }
+119        let orig_offset = self.offset;
+120        self.offset += (i + reserved) as u64;
+121        self.cursor = (self.cursor_reserved + i) % Self::LEN;
+122        self.cursor_reserved = self.cursor;
+123
+124        Some((cid_data.1.unwrap(), orig_offset..self.offset))
+125    }
+126
+127    /// Returns a CID from the available ones and marks it as reserved.
+128    ///
+129    /// If there's no more CIDs in the ready set, this will return None.
+130    /// CIDs marked as reserved will be skipped when the active one advances.
+131    #[allow(dead_code)]
+132    pub(crate) fn next_reserved(&mut self) -> Option<ConnectionId> {
+133        let (i, cid_data) = self.iter_from_reserved().nth(1)?;
+134
+135        self.cursor_reserved = (self.cursor_reserved + i) % Self::LEN;
+136        Some(cid_data.0)
+137    }
+138
+139    /// Returns the number of unused CIDs (neither active nor reserved).
+140    #[allow(unused)]
+141    pub(crate) fn remaining(&self) -> usize {
+142        self.iter_from_reserved()
+143            .count()
+144            .checked_sub(1)
+145            .expect("iterator is non empty")
+146    }
+147
+148    /// Iterate CIDs in CidQueue that are not `None`, including the active CID
+149    fn iter_from_active(&self) -> impl Iterator<Item = (usize, CidData)> + '_ {
+150        (0..Self::LEN).filter_map(move |step| {
+151            let index = (self.cursor + step) % Self::LEN;
+152            self.buffer[index].map(|cid_data| (step, cid_data))
+153        })
+154    }
+155
+156    /// Iterate CIDs in CidQueue that are not `None`, from [`Self::cursor_reserved`].
+157    ///
+158    /// The iterator will always have at least one item, as it will include the active CID when no
+159    /// CID has been reserved, or the last reserved CID otherwise.
+160    ///
+161    /// Along with the CID, it returns the offset counted from [`Self::cursor_reserved`] where the
+162    /// CID is stored.
+163    fn iter_from_reserved(&self) -> impl Iterator<Item = (usize, CidData)> + '_ {
+164        (0..(Self::LEN - self.reserved_len())).filter_map(move |step| {
+165            let index = (self.cursor_reserved + step) % Self::LEN;
+166            self.buffer[index].map(|cid_data| (step, cid_data))
+167        })
+168    }
+169
+170    /// The length of the internal buffer's section of CIDs that are marked as reserved.
+171    fn reserved_len(&self) -> usize {
+172        if self.cursor_reserved >= self.cursor {
+173            self.cursor_reserved - self.cursor
+174        } else {
+175            self.cursor_reserved + Self::LEN - self.cursor
+176        }
+177    }
+178
+179    /// Replace the initial CID
+180    pub(crate) fn update_initial_cid(&mut self, cid: ConnectionId) {
+181        debug_assert_eq!(self.offset, 0);
+182        self.buffer[self.cursor] = Some(CidData(cid, None));
+183    }
+184
+185    /// Return active remote CID itself
+186    pub(crate) fn active(&self) -> ConnectionId {
+187        self.buffer[self.cursor].unwrap().0
+188    }
+189
+190    /// Return the sequence number of active remote CID
+191    pub(crate) fn active_seq(&self) -> u64 {
+192        self.offset
+193    }
+194
+195    pub(crate) const LEN: usize = 5;
+196}
+197
+198#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+199pub(crate) enum InsertError {
+200    /// CID was already retired
+201    Retired,
+202    /// Sequence number violates the leading edge of the window
+203    ExceedsLimit,
+204}
+205
+206#[cfg(test)]
+207mod tests {
+208    use super::*;
+209
+210    fn cid(sequence: u64, retire_prior_to: u64) -> NewConnectionId {
+211        NewConnectionId {
+212            path_id: None,
+213            sequence,
+214            id: ConnectionId::new(&sequence.to_be_bytes()),
+215            reset_token: ResetToken::from([0xCD; crate::RESET_TOKEN_SIZE]),
+216            retire_prior_to,
+217        }
+218    }
+219
+220    fn initial_cid() -> ConnectionId {
+221        ConnectionId::new(&[0xFF; 8])
+222    }
+223
+224    #[test]
+225    fn next_dense() {
+226        let mut q = CidQueue::new(initial_cid());
+227        assert!(q.next().is_none());
+228        assert!(q.next().is_none());
+229
+230        for i in 1..CidQueue::LEN as u64 {
+231            q.insert(cid(i, 0)).unwrap();
+232        }
+233        for i in 1..CidQueue::LEN as u64 {
+234            let (_, retire) = q.next().unwrap();
+235            assert_eq!(q.active_seq(), i);
+236            assert_eq!(retire.end - retire.start, 1);
+237        }
+238        assert!(q.next().is_none());
+239    }
+240
+241    #[test]
+242    fn next_sparse() {
+243        let mut q = CidQueue::new(initial_cid());
+244        let seqs = (1..CidQueue::LEN as u64).filter(|x| x % 2 == 0);
+245        for i in seqs.clone() {
+246            q.insert(cid(i, 0)).unwrap();
+247        }
+248        for i in seqs {
+249            let (_, retire) = q.next().unwrap();
+250            dbg!(&retire);
+251            assert_eq!(q.active_seq(), i);
+252            assert_eq!(retire, (q.active_seq().saturating_sub(2))..q.active_seq());
+253        }
+254        assert!(q.next().is_none());
+255    }
+256
+257    #[test]
+258    fn wrap() {
+259        let mut q = CidQueue::new(initial_cid());
+260
+261        for i in 1..CidQueue::LEN as u64 {
+262            q.insert(cid(i, 0)).unwrap();
+263        }
+264        for _ in 1..(CidQueue::LEN as u64 - 1) {
+265            q.next().unwrap();
+266        }
+267        for i in CidQueue::LEN as u64..(CidQueue::LEN as u64 + 3) {
+268            q.insert(cid(i, 0)).unwrap();
+269        }
+270        for i in (CidQueue::LEN as u64 - 1)..(CidQueue::LEN as u64 + 3) {
+271            q.next().unwrap();
+272            assert_eq!(q.active_seq(), i);
+273        }
+274        assert!(q.next().is_none());
+275    }
+276
+277    #[test]
+278    fn retire_dense() {
+279        let mut q = CidQueue::new(initial_cid());
+280
+281        for i in 1..CidQueue::LEN as u64 {
+282            q.insert(cid(i, 0)).unwrap();
+283        }
+284        assert_eq!(q.active_seq(), 0);
+285
+286        assert_eq!(q.insert(cid(4, 2)).unwrap().unwrap().0, 0..2);
+287        assert_eq!(q.active_seq(), 2);
+288        assert_eq!(q.insert(cid(4, 2)), Ok(None));
+289
+290        for i in 2..(CidQueue::LEN as u64 - 1) {
+291            let _ = q.next().unwrap();
+292            assert_eq!(q.active_seq(), i + 1);
+293            assert_eq!(q.insert(cid(i + 1, i + 1)), Ok(None));
+294        }
+295
+296        assert!(q.next().is_none());
+297    }
+298
+299    #[test]
+300    fn retire_sparse() {
+301        // Retiring CID 0 when CID 1 is not known should retire CID 1 as we move to CID 2
+302        let mut q = CidQueue::new(initial_cid());
+303        q.insert(cid(2, 0)).unwrap();
+304        assert_eq!(q.insert(cid(3, 1)).unwrap().unwrap().0, 0..2,);
+305        assert_eq!(q.active_seq(), 2);
+306    }
+307
+308    #[test]
+309    fn retire_many() {
+310        let mut q = CidQueue::new(initial_cid());
+311        q.insert(cid(2, 0)).unwrap();
+312        assert_eq!(
+313            q.insert(cid(1_000_000, 1_000_000)).unwrap().unwrap().0,
+314            0..CidQueue::LEN as u64,
+315        );
+316        assert_eq!(q.active_seq(), 1_000_000);
+317    }
+318
+319    #[test]
+320    fn insert_limit() {
+321        let mut q = CidQueue::new(initial_cid());
+322        assert_eq!(q.insert(cid(CidQueue::LEN as u64 - 1, 0)), Ok(None));
+323        assert_eq!(
+324            q.insert(cid(CidQueue::LEN as u64, 0)),
+325            Err(InsertError::ExceedsLimit)
+326        );
+327    }
+328
+329    #[test]
+330    fn insert_duplicate() {
+331        let mut q = CidQueue::new(initial_cid());
+332        q.insert(cid(0, 0)).unwrap();
+333        q.insert(cid(0, 0)).unwrap();
+334    }
+335
+336    #[test]
+337    fn insert_retired() {
+338        let mut q = CidQueue::new(initial_cid());
+339        assert_eq!(
+340            q.insert(cid(0, 0)),
+341            Ok(None),
+342            "reinserting active CID succeeds"
+343        );
+344        assert!(q.next().is_none(), "active CID isn't requeued");
+345        q.insert(cid(1, 0)).unwrap();
+346        q.next().unwrap();
+347        assert_eq!(
+348            q.insert(cid(0, 0)),
+349            Err(InsertError::Retired),
+350            "previous active CID is already retired"
+351        );
+352    }
+353
+354    #[test]
+355    fn retire_then_insert_next() {
+356        let mut q = CidQueue::new(initial_cid());
+357        for i in 1..CidQueue::LEN as u64 {
+358            q.insert(cid(i, 0)).unwrap();
+359        }
+360        q.next().unwrap();
+361        q.insert(cid(CidQueue::LEN as u64, 0)).unwrap();
+362        assert_eq!(
+363            q.insert(cid(CidQueue::LEN as u64 + 1, 0)),
+364            Err(InsertError::ExceedsLimit)
+365        );
+366    }
+367
+368    #[test]
+369    fn always_valid() {
+370        let mut q = CidQueue::new(initial_cid());
+371        assert!(q.next().is_none());
+372        assert_eq!(q.active(), initial_cid());
+373        assert_eq!(q.active_seq(), 0);
+374    }
+375
+376    #[test]
+377    fn reserved_smoke() {
+378        let mut q = CidQueue::new(initial_cid());
+379        assert_eq!(q.next_reserved(), None);
+380
+381        let one = cid(1, 0);
+382        q.insert(one).unwrap();
+383        assert_eq!(q.next_reserved(), Some(one.id));
+384
+385        let two = cid(2, 2);
+386        let (retired_range, reset_token) = q.insert(two).unwrap().unwrap();
+387        assert_eq!(reset_token, two.reset_token);
+388        assert_eq!(retired_range, 0..2);
+389
+390        assert_eq!(q.next_reserved(), None);
+391
+392        let four = cid(4, 2);
+393        q.insert(four).unwrap();
+394        println!("{q:?}");
+395        assert_eq!(q.next_reserved(), Some(four.id));
+396        assert_eq!(q.active(), two.id);
+397
+398        assert_eq!(q.next(), None);
+399    }
+400
+401    #[test]
+402    fn reserve_multiple() {
+403        let mut q = CidQueue::new(initial_cid());
+404        let one = cid(1, 0);
+405        let two = cid(2, 0);
+406        q.insert(one).unwrap();
+407        q.insert(two).unwrap();
+408        assert_eq!(q.next_reserved(), Some(one.id));
+409        assert_eq!(q.next_reserved(), Some(two.id));
+410        assert_eq!(q.next_reserved(), None);
+411    }
+412
+413    #[test]
+414    fn reserve_multiple_sparse() {
+415        let mut q = CidQueue::new(initial_cid());
+416        let two = cid(2, 0);
+417        let four = cid(4, 0);
+418        q.insert(two).unwrap();
+419        q.insert(four).unwrap();
+420        assert_eq!(q.next_reserved(), Some(two.id));
+421        assert_eq!(q.next_reserved(), Some(four.id));
+422        assert_eq!(q.next_reserved(), None);
+423    }
+424
+425    #[test]
+426    fn reserve_many_next_clears() {
+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 - 2 {
+433            assert!(q.next_reserved().is_some());
+434        }
+435
+436        assert!(q.next().is_some());
+437        assert_eq!(q.next(), None);
+438    }
+439
+440    #[test]
+441    fn reserve_many_next_reserved_none() {
+442        let mut q = CidQueue::new(initial_cid());
+443        for i in 1..CidQueue::LEN {
+444            q.insert(cid(i as u64, 0)).unwrap();
+445        }
+446
+447        for _ in 0..CidQueue::LEN - 1 {
+448            assert!(q.next_reserved().is_some());
+449        }
+450
+451        assert_eq!(q.next_reserved(), None);
+452    }
+453
+454    #[test]
+455    fn one_active_all_else_reserved_next_none() {
+456        let mut q = CidQueue::new(initial_cid());
+457        for i in 1..CidQueue::LEN {
+458            q.insert(cid(i as u64, 0)).unwrap();
+459        }
+460
+461        for _ in 0..CidQueue::LEN - 1 {
+462            assert!(q.next_reserved().is_some());
+463        }
+464
+465        assert_eq!(q.next(), None);
+466    }
+467
+468    #[test]
+469    fn insert_reserve_advance() {
+470        let mut q = CidQueue::new(initial_cid());
+471
+472        let first = cid(1, 0);
+473        let second = cid(2, 0);
+474        let third = cid(3, 0);
+475
+476        q.insert(first).unwrap();
+477        q.insert(second).unwrap();
+478
+479        assert_eq!(q.next_reserved(), Some(first.id));
+480        q.insert(third).unwrap();
+481        q.next();
+482        assert_eq!(q.active(), second.id);
+483    }
+484
+485    #[test]
+486    fn sparse_insert_reserve_insert_advance() {
+487        let mut q = CidQueue::new(initial_cid());
+488
+489        let one = cid(1, 0);
+490        let two = cid(2, 0);
+491        let three = cid(3, 0);
+492
+493        q.insert(two).unwrap();
+494        q.insert(three).unwrap();
+495        assert_eq!(q.next_reserved(), Some(two.id));
+496        q.insert(one).unwrap();
+497        q.next();
+498        assert_eq!(q.active(), three.id);
+499        assert_eq!(q.next_reserved(), None);
+500    }
+501
+502    #[test]
+503    fn reserve_many_next_clears_across_wraparound() {
+504        let mut q = CidQueue::new(initial_cid());
+505        for i in 1..CidQueue::LEN {
+506            q.insert(cid(i as u64, 0)).unwrap();
+507        }
+508
+509        for _ in 0..CidQueue::LEN - 2 {
+510            assert!(q.next_reserved().is_some());
+511        }
+512
+513        assert!(q.next().is_some());
+514        q.insert(cid(CidQueue::LEN as u64, 0)).unwrap();
+515        assert!(q.next_reserved().is_some());
+516        q.insert(cid(CidQueue::LEN as u64 + 1, 0)).unwrap();
+517        assert!(q.next().is_some());
+518    }
+519}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/coding.rs.html b/pr/672/docs/src/noq_proto/coding.rs.html new file mode 100644 index 000000000..28149b1d5 --- /dev/null +++ b/pr/672/docs/src/noq_proto/coding.rs.html @@ -0,0 +1,152 @@ +coding.rs - source

noq_proto/
coding.rs

1//! Coding related traits.
+2
+3use std::net::{Ipv4Addr, Ipv6Addr};
+4
+5use bytes::{Buf, BufMut};
+6use thiserror::Error;
+7
+8use crate::VarInt;
+9
+10/// Error indicating that the provided buffer was too small
+11#[derive(Error, Debug, Copy, Clone, Eq, PartialEq)]
+12#[error("unexpected end of buffer")]
+13pub struct UnexpectedEnd;
+14
+15/// Coding result type
+16pub type Result<T> = ::std::result::Result<T, UnexpectedEnd>;
+17
+18/// Infallible encoding of QUIC primitives.
+19pub trait Encodable {
+20    /// Append the encoding of `self` to the provided buffer.
+21    fn encode<B: BufMut>(&self, buf: &mut B);
+22}
+23
+24/// Decoding of QUIC primitives.
+25pub trait Decodable: Sized {
+26    /// Decode a `Self` from the provided buffer, if the buffer is large enough
+27    fn decode<B: Buf>(buf: &mut B) -> Result<Self>;
+28}
+29
+30impl Decodable for u8 {
+31    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+32        if buf.remaining() < 1 {
+33            return Err(UnexpectedEnd);
+34        }
+35        Ok(buf.get_u8())
+36    }
+37}
+38
+39impl Encodable for u8 {
+40    fn encode<B: BufMut>(&self, buf: &mut B) {
+41        buf.put_u8(*self);
+42    }
+43}
+44
+45impl Decodable for u16 {
+46    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+47        if buf.remaining() < 2 {
+48            return Err(UnexpectedEnd);
+49        }
+50        Ok(buf.get_u16())
+51    }
+52}
+53
+54impl Encodable for u16 {
+55    fn encode<B: BufMut>(&self, buf: &mut B) {
+56        buf.put_u16(*self);
+57    }
+58}
+59
+60impl Decodable for u32 {
+61    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+62        if buf.remaining() < 4 {
+63            return Err(UnexpectedEnd);
+64        }
+65        Ok(buf.get_u32())
+66    }
+67}
+68
+69impl Encodable for u32 {
+70    fn encode<B: BufMut>(&self, buf: &mut B) {
+71        buf.put_u32(*self);
+72    }
+73}
+74
+75impl Decodable for u64 {
+76    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+77        if buf.remaining() < 8 {
+78            return Err(UnexpectedEnd);
+79        }
+80        Ok(buf.get_u64())
+81    }
+82}
+83
+84impl Encodable for u64 {
+85    fn encode<B: BufMut>(&self, buf: &mut B) {
+86        buf.put_u64(*self);
+87    }
+88}
+89
+90impl Decodable for Ipv4Addr {
+91    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+92        if buf.remaining() < 4 {
+93            return Err(UnexpectedEnd);
+94        }
+95        let mut octets = [0; 4];
+96        buf.copy_to_slice(&mut octets);
+97        Ok(octets.into())
+98    }
+99}
+100
+101impl Encodable for Ipv4Addr {
+102    fn encode<B: BufMut>(&self, buf: &mut B) {
+103        buf.put_slice(&self.octets());
+104    }
+105}
+106
+107impl Decodable for Ipv6Addr {
+108    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+109        if buf.remaining() < 16 {
+110            return Err(UnexpectedEnd);
+111        }
+112        let mut octets = [0; 16];
+113        buf.copy_to_slice(&mut octets);
+114        Ok(octets.into())
+115    }
+116}
+117
+118impl Encodable for Ipv6Addr {
+119    fn encode<B: BufMut>(&self, buf: &mut B) {
+120        buf.put_slice(&self.octets());
+121    }
+122}
+123
+124pub(crate) trait BufExt {
+125    fn get<T: Decodable>(&mut self) -> Result<T>;
+126    fn get_var(&mut self) -> Result<u64>;
+127}
+128
+129impl<T: Buf> BufExt for T {
+130    fn get<U: Decodable>(&mut self) -> Result<U> {
+131        U::decode(self)
+132    }
+133
+134    fn get_var(&mut self) -> Result<u64> {
+135        Ok(VarInt::decode(self)?.into_inner())
+136    }
+137}
+138
+139pub(crate) trait BufMutExt {
+140    fn write<T: Encodable>(&mut self, x: T);
+141    fn write_var(&mut self, x: u64);
+142}
+143
+144impl<T: BufMut> BufMutExt for T {
+145    fn write<U: Encodable>(&mut self, x: U) {
+146        x.encode(self);
+147    }
+148
+149    fn write_var(&mut self, x: u64) {
+150        VarInt::from_u64(x).unwrap().encode(self);
+151    }
+152}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/config/mod.rs.html b/pr/672/docs/src/noq_proto/config/mod.rs.html new file mode 100644 index 000000000..48ebae7a0 --- /dev/null +++ b/pr/672/docs/src/noq_proto/config/mod.rs.html @@ -0,0 +1,685 @@ +mod.rs - source

noq_proto/config/
mod.rs

1use std::{
+2    fmt,
+3    net::{SocketAddrV4, SocketAddrV6},
+4    num::TryFromIntError,
+5    sync::Arc,
+6};
+7
+8#[cfg(all(feature = "rustls", any(feature = "aws-lc-rs", feature = "ring")))]
+9use rustls::client::WebPkiServerVerifier;
+10#[cfg(all(feature = "rustls", any(feature = "aws-lc-rs", feature = "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(all(feature = "rustls", any(feature = "aws-lc-rs", feature = "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/// Global configuration for the endpoint, affecting all connections
+37///
+38/// Default values should be suitable for most internet applications.
+39#[derive(Clone)]
+40pub struct EndpointConfig {
+41    pub(crate) reset_key: Arc<dyn HmacKey>,
+42    pub(crate) max_udp_payload_size: VarInt,
+43    /// CID generator factory
+44    ///
+45    /// Create a cid generator for local cid in Endpoint struct
+46    pub(crate) connection_id_generator_factory:
+47        Arc<dyn Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync>,
+48    pub(crate) supported_versions: Vec<u32>,
+49    pub(crate) grease_quic_bit: bool,
+50    /// Minimum interval between outgoing stateless reset packets
+51    pub(crate) min_reset_interval: Duration,
+52    /// Optional seed to be used internally for random number generation
+53    pub(crate) rng_seed: Option<[u8; 32]>,
+54}
+55
+56impl EndpointConfig {
+57    /// Create a default config with a particular `reset_key`
+58    pub fn new(reset_key: Arc<dyn HmacKey>) -> Self {
+59        let cid_factory =
+60            || -> Box<dyn ConnectionIdGenerator> { Box::<HashedConnectionIdGenerator>::default() };
+61        Self {
+62            reset_key,
+63            max_udp_payload_size: (1500u32 - 28).into(), // Ethernet MTU minus IP + UDP headers
+64            connection_id_generator_factory: Arc::new(cid_factory),
+65            supported_versions: DEFAULT_SUPPORTED_VERSIONS.to_vec(),
+66            grease_quic_bit: true,
+67            min_reset_interval: Duration::from_millis(20),
+68            rng_seed: None,
+69        }
+70    }
+71
+72    /// Supply a custom connection ID generator factory
+73    ///
+74    /// Called once by each `Endpoint` constructed from this configuration to obtain the CID
+75    /// generator which will be used to generate the CIDs used for incoming packets on all
+76    /// connections involving that  `Endpoint`. A custom CID generator allows applications to embed
+77    /// information in local connection IDs, e.g. to support stateless packet-level load balancers.
+78    ///
+79    /// Defaults to [`HashedConnectionIdGenerator`].
+80    pub fn cid_generator(
+81        &mut self,
+82        factory: Arc<dyn Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync>,
+83    ) -> &mut Self {
+84        self.connection_id_generator_factory = factory;
+85        self
+86    }
+87
+88    /// Private key used to send authenticated connection resets to peers who were
+89    /// communicating with a previous instance of this endpoint.
+90    pub fn reset_key(&mut self, key: Arc<dyn HmacKey>) -> &mut Self {
+91        self.reset_key = key;
+92        self
+93    }
+94
+95    /// Maximum UDP payload size accepted from peers (excluding UDP and IP overhead).
+96    ///
+97    /// Must be greater or equal than 1200.
+98    ///
+99    /// Defaults to 1472, which is the largest UDP payload that can be transmitted in the typical
+100    /// 1500 byte Ethernet MTU. Deployments on links with larger MTUs (e.g. loopback or Ethernet
+101    /// with jumbo frames) can raise this to improve performance at the cost of a linear increase in
+102    /// datagram receive buffer size.
+103    pub fn max_udp_payload_size(&mut self, value: u16) -> Result<&mut Self, ConfigError> {
+104        if !(1200..=65_527).contains(&value) {
+105            return Err(ConfigError::OutOfBounds);
+106        }
+107
+108        self.max_udp_payload_size = value.into();
+109        Ok(self)
+110    }
+111
+112    /// Get the current value of [`max_udp_payload_size`](Self::max_udp_payload_size)
+113    //
+114    // While most parameters don't need to be readable, this must be exposed to allow higher-level
+115    // layers, e.g. the `noq` crate, to determine how large a receive buffer to allocate to
+116    // support an externally-defined `EndpointConfig`.
+117    //
+118    // While `get_` accessors are typically unidiomatic in Rust, we favor concision for setters,
+119    // which will be used far more heavily.
+120    pub fn get_max_udp_payload_size(&self) -> u64 {
+121        self.max_udp_payload_size.into()
+122    }
+123
+124    /// Override supported QUIC versions
+125    pub fn supported_versions(&mut self, supported_versions: Vec<u32>) -> &mut Self {
+126        self.supported_versions = supported_versions;
+127        self
+128    }
+129
+130    /// Whether to accept QUIC packets containing any value for the fixed bit
+131    ///
+132    /// Enabled by default. Helps protect against protocol ossification and makes traffic less
+133    /// identifiable to observers. Disable if helping observers identify this traffic as QUIC is
+134    /// desired.
+135    pub fn grease_quic_bit(&mut self, value: bool) -> &mut Self {
+136        self.grease_quic_bit = value;
+137        self
+138    }
+139
+140    /// Minimum interval between outgoing stateless reset packets
+141    ///
+142    /// Defaults to 20ms. Limits the impact of attacks which flood an endpoint with garbage packets,
+143    /// e.g. [ISAKMP/IKE amplification]. Larger values provide a stronger defense, but may delay
+144    /// detection of some error conditions by clients. Using a [`ConnectionIdGenerator`] with a low
+145    /// rate of false positives in [`validate`](ConnectionIdGenerator::validate) reduces the risk
+146    /// incurred by a small minimum reset interval.
+147    ///
+148    /// [ISAKMP/IKE
+149    /// amplification]: https://bughunters.google.com/blog/5960150648750080/preventing-cross-service-udp-loops-in-quic#isakmp-ike-amplification-vs-quic
+150    pub fn min_reset_interval(&mut self, value: Duration) -> &mut Self {
+151        self.min_reset_interval = value;
+152        self
+153    }
+154
+155    /// Optional seed to be used internally for random number generation
+156    ///
+157    /// By default, noq will initialize an endpoint's rng using a platform entropy source.
+158    /// However, you can seed the rng yourself through this method (e.g. if you need to run noq
+159    /// deterministically or if you are using noq in an environment that doesn't have a source of
+160    /// entropy available).
+161    pub fn rng_seed(&mut self, seed: Option<[u8; 32]>) -> &mut Self {
+162        self.rng_seed = seed;
+163        self
+164    }
+165}
+166
+167impl fmt::Debug for EndpointConfig {
+168    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+169        fmt.debug_struct("EndpointConfig")
+170            // reset_key not debug
+171            .field("max_udp_payload_size", &self.max_udp_payload_size)
+172            // cid_generator_factory not debug
+173            .field("supported_versions", &self.supported_versions)
+174            .field("grease_quic_bit", &self.grease_quic_bit)
+175            .field("rng_seed", &self.rng_seed)
+176            .finish_non_exhaustive()
+177    }
+178}
+179
+180#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
+181impl Default for EndpointConfig {
+182    fn default() -> Self {
+183        #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+184        use aws_lc_rs::hmac;
+185        use rand::Rng;
+186        #[cfg(feature = "ring")]
+187        use ring::hmac;
+188
+189        let mut reset_key = [0; 64];
+190        rand::rng().fill_bytes(&mut reset_key);
+191
+192        Self::new(Arc::new(hmac::Key::new(hmac::HMAC_SHA256, &reset_key)))
+193    }
+194}
+195
+196/// Parameters governing incoming connections
+197///
+198/// Default values should be suitable for most internet applications.
+199#[derive(Clone)]
+200pub struct ServerConfig {
+201    /// Transport configuration to use for incoming connections
+202    pub transport: Arc<TransportConfig>,
+203
+204    /// TLS configuration used for incoming connections
+205    ///
+206    /// Must be set to use TLS 1.3 only.
+207    pub crypto: Arc<dyn crypto::ServerConfig>,
+208
+209    /// Configuration for sending and handling validation tokens
+210    pub validation_token: ValidationTokenConfig,
+211
+212    /// Used to generate one-time AEAD keys to protect handshake tokens
+213    pub(crate) token_key: Arc<dyn HandshakeTokenKey>,
+214
+215    /// Duration after a retry token was issued for which it's considered valid
+216    pub(crate) retry_token_lifetime: Duration,
+217
+218    /// Whether to allow clients to migrate to new addresses
+219    ///
+220    /// Improves behavior for clients that move between different internet connections or suffer NAT
+221    /// rebinding. Enabled by default.
+222    pub(crate) migration: bool,
+223
+224    pub(crate) preferred_address_v4: Option<SocketAddrV4>,
+225    pub(crate) preferred_address_v6: Option<SocketAddrV6>,
+226
+227    pub(crate) max_incoming: usize,
+228    pub(crate) incoming_buffer_size: u64,
+229    pub(crate) incoming_buffer_size_total: u64,
+230
+231    pub(crate) time_source: Arc<dyn TimeSource>,
+232}
+233
+234impl ServerConfig {
+235    /// Create a default config with a particular handshake token key
+236    pub fn new(
+237        crypto: Arc<dyn crypto::ServerConfig>,
+238        token_key: Arc<dyn HandshakeTokenKey>,
+239    ) -> Self {
+240        Self {
+241            transport: Arc::new(TransportConfig::default()),
+242            crypto,
+243
+244            token_key,
+245            retry_token_lifetime: Duration::from_secs(15),
+246
+247            migration: true,
+248
+249            validation_token: ValidationTokenConfig::default(),
+250
+251            preferred_address_v4: None,
+252            preferred_address_v6: None,
+253
+254            max_incoming: 1 << 16,
+255            incoming_buffer_size: 10 << 20,
+256            incoming_buffer_size_total: 100 << 20,
+257
+258            time_source: Arc::new(StdSystemTime),
+259        }
+260    }
+261
+262    /// Set a custom [`TransportConfig`]
+263    pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self {
+264        self.transport = transport;
+265        self
+266    }
+267
+268    /// Set a custom [`ValidationTokenConfig`]
+269    pub fn validation_token_config(
+270        &mut self,
+271        validation_token: ValidationTokenConfig,
+272    ) -> &mut Self {
+273        self.validation_token = validation_token;
+274        self
+275    }
+276
+277    /// Private key used to authenticate data included in handshake tokens
+278    pub fn token_key(&mut self, value: Arc<dyn HandshakeTokenKey>) -> &mut Self {
+279        self.token_key = value;
+280        self
+281    }
+282
+283    /// Duration after a retry token was issued for which it's considered valid
+284    ///
+285    /// Defaults to 15 seconds.
+286    pub fn retry_token_lifetime(&mut self, value: Duration) -> &mut Self {
+287        self.retry_token_lifetime = value;
+288        self
+289    }
+290
+291    /// Whether to allow clients to migrate to new addresses
+292    ///
+293    /// Improves behavior for clients that move between different internet connections or suffer NAT
+294    /// rebinding. Enabled by default.
+295    pub fn migration(&mut self, value: bool) -> &mut Self {
+296        self.migration = value;
+297        self
+298    }
+299
+300    /// The preferred IPv4 address that will be communicated to clients during handshaking
+301    ///
+302    /// If the client is able to reach this address, it will switch to it.
+303    pub fn preferred_address_v4(&mut self, address: Option<SocketAddrV4>) -> &mut Self {
+304        self.preferred_address_v4 = address;
+305        self
+306    }
+307
+308    /// The preferred IPv6 address that will be communicated to clients during handshaking
+309    ///
+310    /// If the client is able to reach this address, it will switch to it.
+311    pub fn preferred_address_v6(&mut self, address: Option<SocketAddrV6>) -> &mut Self {
+312        self.preferred_address_v6 = address;
+313        self
+314    }
+315
+316    /// Maximum number of [`Incoming`][crate::Incoming] to allow to exist at a time
+317    ///
+318    /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
+319    /// is received and stops existing when the application either accepts it or otherwise disposes
+320    /// of it. While this limit is reached, new incoming connection attempts are immediately
+321    /// refused. Larger values have greater worst-case memory consumption, but accommodate greater
+322    /// application latency in handling incoming connection attempts.
+323    ///
+324    /// The default value is set to 65536. With a typical Ethernet MTU of 1500 bytes, this limits
+325    /// memory consumption from this to under 100 MiB--a generous amount that still prevents memory
+326    /// exhaustion in most contexts.
+327    pub fn max_incoming(&mut self, max_incoming: usize) -> &mut Self {
+328        self.max_incoming = max_incoming;
+329        self
+330    }
+331
+332    /// Maximum number of received bytes to buffer for each [`Incoming`][crate::Incoming]
+333    ///
+334    /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
+335    /// is received and stops existing when the application either accepts it or otherwise disposes
+336    /// of it. This limit governs only packets received within that period, and does not include
+337    /// the first packet. Packets received in excess of this limit are dropped, which may cause
+338    /// 0-RTT or handshake data to have to be retransmitted.
+339    ///
+340    /// The default value is set to 10 MiB--an amount such that in most situations a client would
+341    /// not transmit that much 0-RTT data faster than the server handles the corresponding
+342    /// [`Incoming`][crate::Incoming].
+343    pub fn incoming_buffer_size(&mut self, incoming_buffer_size: u64) -> &mut Self {
+344        self.incoming_buffer_size = incoming_buffer_size;
+345        self
+346    }
+347
+348    /// Maximum number of received bytes to buffer for all [`Incoming`][crate::Incoming]
+349    /// collectively
+350    ///
+351    /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
+352    /// is received and stops existing when the application either accepts it or otherwise disposes
+353    /// of it. This limit governs only packets received within that period, and does not include
+354    /// the first packet. Packets received in excess of this limit are dropped, which may cause
+355    /// 0-RTT or handshake data to have to be retransmitted.
+356    ///
+357    /// The default value is set to 100 MiB--a generous amount that still prevents memory
+358    /// exhaustion in most contexts.
+359    pub fn incoming_buffer_size_total(&mut self, incoming_buffer_size_total: u64) -> &mut Self {
+360        self.incoming_buffer_size_total = incoming_buffer_size_total;
+361        self
+362    }
+363
+364    /// Object to get current [`SystemTime`]
+365    ///
+366    /// This exists to allow system time to be mocked in tests, or wherever else desired.
+367    ///
+368    /// Defaults to [`StdSystemTime`], which simply calls [`SystemTime::now()`](SystemTime::now).
+369    pub fn time_source(&mut self, time_source: Arc<dyn TimeSource>) -> &mut Self {
+370        self.time_source = time_source;
+371        self
+372    }
+373
+374    pub(crate) fn has_preferred_address(&self) -> bool {
+375        self.preferred_address_v4.is_some() || self.preferred_address_v6.is_some()
+376    }
+377}
+378
+379#[cfg(all(feature = "rustls", any(feature = "aws-lc-rs", feature = "ring")))]
+380impl ServerConfig {
+381    /// Create a server config with the given certificate chain to be presented to clients
+382    ///
+383    /// Uses a randomized handshake token key.
+384    pub fn with_single_cert(
+385        cert_chain: Vec<CertificateDer<'static>>,
+386        key: PrivateKeyDer<'static>,
+387    ) -> Result<Self, rustls::Error> {
+388        Ok(Self::with_crypto(Arc::new(QuicServerConfig::new(
+389            cert_chain, key,
+390        )?)))
+391    }
+392}
+393
+394#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
+395impl ServerConfig {
+396    /// Create a server config with the given [`crypto::ServerConfig`]
+397    ///
+398    /// Uses a randomized handshake token key.
+399    pub fn with_crypto(crypto: Arc<dyn crypto::ServerConfig>) -> Self {
+400        use crate::crypto::ring_like::RetryTokenKey;
+401
+402        let retry_token_key = RetryTokenKey::new(&mut rand::rng());
+403        Self::new(crypto, Arc::new(retry_token_key))
+404    }
+405}
+406
+407impl fmt::Debug for ServerConfig {
+408    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+409        fmt.debug_struct("ServerConfig")
+410            .field("transport", &self.transport)
+411            // crypto not debug
+412            // token not debug
+413            .field("retry_token_lifetime", &self.retry_token_lifetime)
+414            .field("validation_token", &self.validation_token)
+415            .field("migration", &self.migration)
+416            .field("preferred_address_v4", &self.preferred_address_v4)
+417            .field("preferred_address_v6", &self.preferred_address_v6)
+418            .field("max_incoming", &self.max_incoming)
+419            .field("incoming_buffer_size", &self.incoming_buffer_size)
+420            .field(
+421                "incoming_buffer_size_total",
+422                &self.incoming_buffer_size_total,
+423            )
+424            // system_time_clock not debug
+425            .finish_non_exhaustive()
+426    }
+427}
+428
+429/// Configuration for sending and handling validation tokens in incoming connections
+430///
+431/// Default values should be suitable for most internet applications.
+432///
+433/// ## QUIC Tokens
+434///
+435/// The QUIC protocol defines a concept of "[address validation][1]". Essentially, one side of a
+436/// QUIC connection may appear to be receiving QUIC packets from a particular remote UDP address,
+437/// but it will only consider that remote address "validated" once it has convincing evidence that
+438/// the address is not being [spoofed][2].
+439///
+440/// Validation is important primarily because of QUIC's "anti-amplification limit." This limit
+441/// prevents a QUIC server from sending a client more than three times the number of bytes it has
+442/// received from the client on a given address until that address is validated. This is designed
+443/// to mitigate the ability of attackers to use QUIC-based servers as reflectors in [amplification
+444/// attacks][3].
+445///
+446/// A path may become validated in several ways. The server is always considered validated by the
+447/// client. The client usually begins in an unvalidated state upon first connecting or migrating,
+448/// but then becomes validated through various mechanisms that usually take one network round trip.
+449/// However, in some cases, a client which has previously attempted to connect to a server may have
+450/// been given a one-time use cryptographically secured "token" that it can send in a subsequent
+451/// connection attempt to be validated immediately.
+452///
+453/// There are two ways these tokens can originate:
+454///
+455/// - If the server responds to an incoming connection with `retry`, a "retry token" is minted and
+456///   sent to the client, which the client immediately uses to attempt to connect again. Retry
+457///   tokens operate on short timescales, such as 15 seconds.
+458/// - If a client's path within an active connection is validated, the server may send the client
+459///   one or more "validation tokens," which the client may store for use in later connections to
+460///   the same server. Validation tokens may be valid for much longer lifetimes than retry token.
+461///
+462/// The usage of validation tokens is most impactful in situations where 0-RTT data is also being
+463/// used--in particular, in situations where the server sends the client more than three times more
+464/// 0.5-RTT data than it has received 0-RTT data. Since the successful completion of a connection
+465/// handshake implicitly causes the client's address to be validated, transmission of 0.5-RTT data
+466/// is the main situation where a server might be sending application data to an address that could
+467/// be validated by token usage earlier than it would become validated without token usage.
+468///
+469/// [1]: https://www.rfc-editor.org/rfc/rfc9000.html#section-8
+470/// [2]: https://en.wikipedia.org/wiki/IP_address_spoofing
+471/// [3]: https://en.wikipedia.org/wiki/Denial-of-service_attack#Amplification
+472///
+473/// These tokens should not be confused with "stateless reset tokens," which are similarly named
+474/// but entirely unrelated.
+475#[derive(Clone)]
+476pub struct ValidationTokenConfig {
+477    pub(crate) lifetime: Duration,
+478    pub(crate) log: Arc<dyn TokenLog>,
+479    pub(crate) sent: u32,
+480}
+481
+482impl ValidationTokenConfig {
+483    /// Duration after an address validation token was issued for which it's considered valid
+484    ///
+485    /// This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.
+486    ///
+487    /// Defaults to 2 weeks.
+488    pub fn lifetime(&mut self, value: Duration) -> &mut Self {
+489        self.lifetime = value;
+490        self
+491    }
+492
+493    #[allow(rustdoc::redundant_explicit_links)] // which links are redundant depends on features
+494    /// Set a custom [`TokenLog`]
+495    ///
+496    /// If the `bloom` feature is enabled (which it is by default), defaults to a default
+497    /// [`BloomTokenLog`][crate::BloomTokenLog], which is suitable for most internet applications.
+498    ///
+499    /// If the `bloom` feature is disabled, defaults to [`NoneTokenLog`][crate::NoneTokenLog],
+500    /// which makes the server ignore all address validation tokens (that is, tokens originating
+501    /// from NEW_TOKEN frames--retry tokens are not affected).
+502    pub fn log(&mut self, log: Arc<dyn TokenLog>) -> &mut Self {
+503        self.log = log;
+504        self
+505    }
+506
+507    /// Number of address validation tokens sent to a client when its path is validated
+508    ///
+509    /// This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.
+510    ///
+511    /// If the `bloom` feature is enabled (which it is by default), defaults to 2. Otherwise,
+512    /// defaults to 0.
+513    pub fn sent(&mut self, value: u32) -> &mut Self {
+514        self.sent = value;
+515        self
+516    }
+517}
+518
+519impl Default for ValidationTokenConfig {
+520    fn default() -> Self {
+521        #[cfg(feature = "bloom")]
+522        let log = Arc::new(BloomTokenLog::default());
+523        #[cfg(not(feature = "bloom"))]
+524        let log = Arc::new(NoneTokenLog);
+525        Self {
+526            lifetime: Duration::from_secs(2 * 7 * 24 * 60 * 60),
+527            log,
+528            sent: if cfg!(feature = "bloom") { 2 } else { 0 },
+529        }
+530    }
+531}
+532
+533impl fmt::Debug for ValidationTokenConfig {
+534    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+535        fmt.debug_struct("ServerValidationTokenConfig")
+536            .field("lifetime", &self.lifetime)
+537            // log not debug
+538            .field("sent", &self.sent)
+539            .finish_non_exhaustive()
+540    }
+541}
+542
+543/// Configuration for outgoing connections
+544///
+545/// Default values should be suitable for most internet applications.
+546#[derive(Clone)]
+547#[non_exhaustive]
+548pub struct ClientConfig {
+549    /// Transport configuration to use
+550    pub(crate) transport: Arc<TransportConfig>,
+551
+552    /// Cryptographic configuration to use
+553    pub(crate) crypto: Arc<dyn crypto::ClientConfig>,
+554
+555    /// Validation token store to use
+556    pub(crate) token_store: Arc<dyn TokenStore>,
+557
+558    /// Provider that populates the destination connection ID of Initial Packets
+559    pub(crate) initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>,
+560
+561    /// QUIC protocol version to use
+562    pub(crate) version: u32,
+563}
+564
+565impl ClientConfig {
+566    /// Create a default config with a particular cryptographic config
+567    pub fn new(crypto: Arc<dyn crypto::ClientConfig>) -> Self {
+568        Self {
+569            transport: Default::default(),
+570            crypto,
+571            token_store: Arc::new(TokenMemoryCache::default()),
+572            initial_dst_cid_provider: Arc::new(|| {
+573                RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid()
+574            }),
+575            version: 1,
+576        }
+577    }
+578
+579    /// Configure how to populate the destination CID of the initial packet when attempting to
+580    /// establish a new connection
+581    ///
+582    /// By default, it's populated with random bytes with reasonable length, so unless you have
+583    /// a good reason, you do not need to change it.
+584    ///
+585    /// When prefer to override the default, please note that the generated connection ID MUST be
+586    /// at least 8 bytes long and unpredictable, as per section 7.2 of RFC 9000.
+587    pub fn initial_dst_cid_provider(
+588        &mut self,
+589        initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>,
+590    ) -> &mut Self {
+591        self.initial_dst_cid_provider = initial_dst_cid_provider;
+592        self
+593    }
+594
+595    /// Set a custom [`TransportConfig`]
+596    pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self {
+597        self.transport = transport;
+598        self
+599    }
+600
+601    /// Set a custom [`TokenStore`]
+602    ///
+603    /// Defaults to [`TokenMemoryCache`], which is suitable for most internet applications.
+604    pub fn token_store(&mut self, store: Arc<dyn TokenStore>) -> &mut Self {
+605        self.token_store = store;
+606        self
+607    }
+608
+609    /// Set the QUIC version to use
+610    pub fn version(&mut self, version: u32) -> &mut Self {
+611        self.version = version;
+612        self
+613    }
+614}
+615
+616#[cfg(all(feature = "rustls", any(feature = "aws-lc-rs", feature = "ring")))]
+617impl ClientConfig {
+618    /// Create a client configuration that trusts the platform's native roots
+619    #[cfg(feature = "platform-verifier")]
+620    pub fn try_with_platform_verifier() -> Result<Self, rustls::Error> {
+621        Ok(Self::new(Arc::new(
+622            crypto::rustls::QuicClientConfig::with_platform_verifier()?,
+623        )))
+624    }
+625
+626    /// Create a client configuration that trusts specified trust anchors
+627    pub fn with_root_certificates(
+628        roots: Arc<rustls::RootCertStore>,
+629    ) -> Result<Self, rustls::client::VerifierBuilderError> {
+630        Ok(Self::new(Arc::new(crypto::rustls::QuicClientConfig::new(
+631            WebPkiServerVerifier::builder_with_provider(roots, configured_provider()).build()?,
+632        ))))
+633    }
+634}
+635
+636impl fmt::Debug for ClientConfig {
+637    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+638        fmt.debug_struct("ClientConfig")
+639            .field("transport", &self.transport)
+640            // crypto not debug
+641            // token_store not debug
+642            .field("version", &self.version)
+643            .finish_non_exhaustive()
+644    }
+645}
+646
+647/// Errors in the configuration of an endpoint
+648#[derive(Debug, Error, Clone, PartialEq, Eq)]
+649#[non_exhaustive]
+650pub enum ConfigError {
+651    /// Value exceeds supported bounds
+652    #[error("value exceeds supported bounds")]
+653    OutOfBounds,
+654}
+655
+656impl From<TryFromIntError> for ConfigError {
+657    fn from(_: TryFromIntError) -> Self {
+658        Self::OutOfBounds
+659    }
+660}
+661
+662impl From<VarIntBoundsExceeded> for ConfigError {
+663    fn from(_: VarIntBoundsExceeded) -> Self {
+664        Self::OutOfBounds
+665    }
+666}
+667
+668/// Object to get current [`SystemTime`]
+669///
+670/// This exists to allow system time to be mocked in tests, or wherever else desired.
+671pub trait TimeSource: Send + Sync {
+672    /// Get [`SystemTime::now()`](SystemTime::now) or the mocked equivalent
+673    fn now(&self) -> SystemTime;
+674}
+675
+676/// Default implementation of [`TimeSource`]
+677///
+678/// Implements `now` by calling [`SystemTime::now()`](SystemTime::now).
+679pub struct StdSystemTime;
+680
+681impl TimeSource for StdSystemTime {
+682    fn now(&self) -> SystemTime {
+683        SystemTime::now()
+684    }
+685}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/config/qlog.rs.html b/pr/672/docs/src/noq_proto/config/qlog.rs.html new file mode 100644 index 000000000..fbfb7fa77 --- /dev/null +++ b/pr/672/docs/src/noq_proto/config/qlog.rs.html @@ -0,0 +1,173 @@ +qlog.rs - source

noq_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/672/docs/src/noq_proto/config/transport.rs.html b/pr/672/docs/src/noq_proto/config/transport.rs.html new file mode 100644 index 000000000..2f2e14d12 --- /dev/null +++ b/pr/672/docs/src/noq_proto/config/transport.rs.html @@ -0,0 +1,908 @@ +transport.rs - source

noq_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/// Parameters governing the core QUIC state machine
+18///
+19/// Default values should be suitable for most internet applications. Applications protocols which
+20/// forbid remotely-initiated streams should set `max_concurrent_bidi_streams` and
+21/// `max_concurrent_uni_streams` to zero.
+22///
+23/// In some cases, performance or resource requirements can be improved by tuning these values to
+24/// suit a particular application and/or network connection. In particular, data window sizes can be
+25/// tuned for a particular expected round trip time, link capacity, and memory availability. Tuning
+26/// for higher bandwidths and latencies increases worst-case memory consumption, but does not impair
+27/// performance at lower bandwidths and latencies. The default configuration is tuned for a 100Mbps
+28/// link with a 100ms round trip time.
+29#[derive(Clone)]
+30pub struct TransportConfig {
+31    pub(crate) max_concurrent_bidi_streams: VarInt,
+32    pub(crate) max_concurrent_uni_streams: VarInt,
+33    pub(crate) max_idle_timeout: Option<VarInt>,
+34    pub(crate) stream_receive_window: VarInt,
+35    pub(crate) receive_window: VarInt,
+36    pub(crate) send_window: u64,
+37    pub(crate) send_fairness: bool,
+38
+39    pub(crate) packet_threshold: u32,
+40    pub(crate) time_threshold: f32,
+41    pub(crate) initial_rtt: Duration,
+42    pub(crate) initial_mtu: u16,
+43    pub(crate) min_mtu: u16,
+44    pub(crate) mtu_discovery_config: Option<MtuDiscoveryConfig>,
+45    pub(crate) pad_to_mtu: bool,
+46    pub(crate) ack_frequency_config: Option<AckFrequencyConfig>,
+47    pub(crate) max_outgoing_bytes_per_second: Option<u64>,
+48
+49    pub(crate) persistent_congestion_threshold: u32,
+50    pub(crate) keep_alive_interval: Option<Duration>,
+51    pub(crate) crypto_buffer_size: usize,
+52    pub(crate) allow_spin: bool,
+53    pub(crate) datagram_receive_buffer_size: Option<usize>,
+54    pub(crate) datagram_send_buffer_size: usize,
+55    #[cfg(test)]
+56    pub(crate) deterministic_packet_numbers: bool,
+57
+58    pub(crate) congestion_controller_factory: Arc<dyn congestion::ControllerFactory + Send + Sync>,
+59
+60    pub(crate) enable_segmentation_offload: bool,
+61
+62    pub(crate) address_discovery_role: address_discovery::Role,
+63
+64    pub(crate) max_concurrent_multipath_paths: Option<NonZeroU32>,
+65
+66    pub(crate) default_path_max_idle_timeout: Option<Duration>,
+67    pub(crate) default_path_keep_alive_interval: Option<Duration>,
+68
+69    pub(crate) max_remote_nat_traversal_addresses: Option<NonZeroU8>,
+70
+71    #[cfg(feature = "qlog")]
+72    pub(crate) qlog_factory: Option<Arc<dyn QlogFactory>>,
+73}
+74
+75impl TransportConfig {
+76    /// Maximum number of incoming bidirectional streams that may be open concurrently
+77    ///
+78    /// Must be nonzero for the peer to open any bidirectional streams.
+79    ///
+80    /// Worst-case memory use is directly proportional to `max_concurrent_bidi_streams *
+81    /// stream_receive_window`, with an upper bound proportional to `receive_window`.
+82    pub fn max_concurrent_bidi_streams(&mut self, value: VarInt) -> &mut Self {
+83        self.max_concurrent_bidi_streams = value;
+84        self
+85    }
+86
+87    /// Variant of `max_concurrent_bidi_streams` affecting unidirectional streams
+88    pub fn max_concurrent_uni_streams(&mut self, value: VarInt) -> &mut Self {
+89        self.max_concurrent_uni_streams = value;
+90        self
+91    }
+92
+93    /// Maximum duration of inactivity to accept before timing out the connection.
+94    ///
+95    /// The true idle timeout is the minimum of this and the peer's own max idle timeout. `None`
+96    /// represents an infinite timeout. Defaults to 30 seconds.
+97    ///
+98    /// **WARNING**: If a peer or its network path malfunctions or acts maliciously, an infinite
+99    /// idle timeout can result in permanently hung futures!
+100    ///
+101    /// ```
+102    /// # use std::{convert::TryInto, time::Duration};
+103    /// # use noq_proto::{TransportConfig, VarInt, VarIntBoundsExceeded};
+104    /// # fn main() -> Result<(), VarIntBoundsExceeded> {
+105    /// let mut config = TransportConfig::default();
+106    ///
+107    /// // Set the idle timeout as `VarInt`-encoded milliseconds
+108    /// config.max_idle_timeout(Some(VarInt::from_u32(10_000).into()));
+109    ///
+110    /// // Set the idle timeout as a `Duration`
+111    /// config.max_idle_timeout(Some(Duration::from_secs(10).try_into()?));
+112    /// # Ok(())
+113    /// # }
+114    /// ```
+115    pub fn max_idle_timeout(&mut self, value: Option<IdleTimeout>) -> &mut Self {
+116        self.max_idle_timeout = value.map(|t| t.0);
+117        self
+118    }
+119
+120    /// Maximum number of bytes the peer may transmit without acknowledgement on any one stream
+121    /// before becoming blocked.
+122    ///
+123    /// This should be set to at least the expected connection latency multiplied by the maximum
+124    /// desired throughput. Setting this smaller than `receive_window` helps ensure that a single
+125    /// stream doesn't monopolize receive buffers, which may otherwise occur if the application
+126    /// chooses not to read from a large stream for a time while still requiring data on other
+127    /// streams.
+128    pub fn stream_receive_window(&mut self, value: VarInt) -> &mut Self {
+129        self.stream_receive_window = value;
+130        self
+131    }
+132
+133    /// Maximum number of bytes the peer may transmit across all streams of a connection before
+134    /// becoming blocked.
+135    ///
+136    /// This should be set to at least the expected connection latency multiplied by the maximum
+137    /// desired throughput. Larger values can be useful to allow maximum throughput within a
+138    /// stream while another is blocked.
+139    pub fn receive_window(&mut self, value: VarInt) -> &mut Self {
+140        self.receive_window = value;
+141        self
+142    }
+143
+144    /// Maximum number of bytes to transmit to a peer without acknowledgment
+145    ///
+146    /// Provides an upper bound on memory when communicating with peers that issue large amounts of
+147    /// flow control credit. Endpoints that wish to handle large numbers of connections robustly
+148    /// should take care to set this low enough to guarantee memory exhaustion does not occur if
+149    /// every connection uses the entire window.
+150    pub fn send_window(&mut self, value: u64) -> &mut Self {
+151        self.send_window = value;
+152        self
+153    }
+154
+155    /// Whether to implement fair queuing for send streams having the same priority.
+156    ///
+157    /// When enabled, connections schedule data from outgoing streams having the same priority in a
+158    /// round-robin fashion. When disabled, streams are scheduled in the order they are written to.
+159    ///
+160    /// Note that this only affects streams with the same priority. Higher priority streams always
+161    /// take precedence over lower priority streams.
+162    ///
+163    /// Disabling fairness can reduce fragmentation and protocol overhead for workloads that use
+164    /// many small streams.
+165    pub fn send_fairness(&mut self, value: bool) -> &mut Self {
+166        self.send_fairness = value;
+167        self
+168    }
+169
+170    /// Maximum reordering in packet number space before FACK style loss detection considers a
+171    /// packet lost. Should not be less than 3, per RFC5681.
+172    pub fn packet_threshold(&mut self, value: u32) -> &mut Self {
+173        self.packet_threshold = value;
+174        self
+175    }
+176
+177    /// Maximum reordering in time space before time based loss detection considers a packet lost,
+178    /// as a factor of RTT
+179    pub fn time_threshold(&mut self, value: f32) -> &mut Self {
+180        self.time_threshold = value;
+181        self
+182    }
+183
+184    /// The RTT used before an RTT sample is taken
+185    pub fn initial_rtt(&mut self, value: Duration) -> &mut Self {
+186        self.initial_rtt = value;
+187        self
+188    }
+189
+190    /// The initial value to be used as the maximum UDP payload size before running MTU discovery
+191    /// (see [`TransportConfig::mtu_discovery_config`]).
+192    ///
+193    /// Must be at least 1200, which is the default, and known to be safe for typical internet
+194    /// applications. Larger values are more efficient, but increase the risk of packet loss due to
+195    /// exceeding the network path's IP MTU. If the provided value is higher than what the network
+196    /// path actually supports, packet loss will eventually trigger black hole detection and bring
+197    /// it down to [`TransportConfig::min_mtu`].
+198    pub fn initial_mtu(&mut self, value: u16) -> &mut Self {
+199        self.initial_mtu = value.max(INITIAL_MTU);
+200        self
+201    }
+202
+203    pub(crate) fn get_initial_mtu(&self) -> u16 {
+204        self.initial_mtu.max(self.min_mtu)
+205    }
+206
+207    /// The maximum UDP payload size guaranteed to be supported by the network.
+208    ///
+209    /// Must be at least 1200, which is the default, and lower than or equal to
+210    /// [`TransportConfig::initial_mtu`].
+211    ///
+212    /// Real-world MTUs can vary according to ISP, VPN, and properties of intermediate network links
+213    /// outside of either endpoint's control. Extreme care should be used when raising this value
+214    /// outside of private networks where these factors are fully controlled. If the provided value
+215    /// is higher than what the network path actually supports, the result will be unpredictable and
+216    /// catastrophic packet loss, without a possibility of repair. Prefer
+217    /// [`TransportConfig::initial_mtu`] together with
+218    /// [`TransportConfig::mtu_discovery_config`] to set a maximum UDP payload size that robustly
+219    /// adapts to the network.
+220    pub fn min_mtu(&mut self, value: u16) -> &mut Self {
+221        self.min_mtu = value.max(INITIAL_MTU);
+222        self
+223    }
+224
+225    /// Specifies the MTU discovery config (see [`MtuDiscoveryConfig`] for details).
+226    ///
+227    /// Enabled by default.
+228    pub fn mtu_discovery_config(&mut self, value: Option<MtuDiscoveryConfig>) -> &mut Self {
+229        self.mtu_discovery_config = value;
+230        self
+231    }
+232
+233    /// Pad UDP datagrams carrying application data to current maximum UDP payload size
+234    ///
+235    /// Disabled by default. UDP datagrams containing loss probes are exempt from padding.
+236    ///
+237    /// Enabling this helps mitigate traffic analysis by network observers, but it increases
+238    /// bandwidth usage. Without this mitigation precise plain text size of application datagrams as
+239    /// well as the total size of stream write bursts can be inferred by observers under certain
+240    /// conditions. This analysis requires either an uncongested connection or application datagrams
+241    /// too large to be coalesced.
+242    pub fn pad_to_mtu(&mut self, value: bool) -> &mut Self {
+243        self.pad_to_mtu = value;
+244        self
+245    }
+246
+247    /// Specifies the ACK frequency config (see [`AckFrequencyConfig`] for details)
+248    ///
+249    /// The provided configuration will be ignored if the peer does not support the acknowledgement
+250    /// frequency QUIC extension.
+251    ///
+252    /// Defaults to `None`, which disables controlling the peer's acknowledgement frequency. Even
+253    /// if set to `None`, the local side still supports the acknowledgement frequency QUIC
+254    /// extension and may use it in other ways.
+255    pub fn ack_frequency_config(&mut self, value: Option<AckFrequencyConfig>) -> &mut Self {
+256        self.ack_frequency_config = value;
+257        self
+258    }
+259
+260    /// Configures an outbound rate limit (in bytes per second) for each connection.
+261    ///
+262    /// Defaults to `None`, which disables rate limiting.
+263    pub fn max_outgoing_bytes_per_second(&mut self, value: Option<u64>) -> &mut Self {
+264        self.max_outgoing_bytes_per_second = value;
+265        self
+266    }
+267
+268    /// Number of consecutive PTOs after which network is considered to be experiencing persistent congestion.
+269    pub fn persistent_congestion_threshold(&mut self, value: u32) -> &mut Self {
+270        self.persistent_congestion_threshold = value;
+271        self
+272    }
+273
+274    /// Period of inactivity before sending a keep-alive packet
+275    ///
+276    /// Keep-alive packets prevent an inactive but otherwise healthy connection from timing out.
+277    ///
+278    /// `None` to disable, which is the default. Only one side of any given connection needs keep-alive
+279    /// enabled for the connection to be preserved. Must be set lower than the idle_timeout of both
+280    /// peers to be effective.
+281    pub fn keep_alive_interval(&mut self, value: Option<Duration>) -> &mut Self {
+282        self.keep_alive_interval = value;
+283        self
+284    }
+285
+286    /// Maximum quantity of out-of-order crypto layer data to buffer
+287    pub fn crypto_buffer_size(&mut self, value: usize) -> &mut Self {
+288        self.crypto_buffer_size = value;
+289        self
+290    }
+291
+292    /// Whether the implementation is permitted to set the spin bit on this connection
+293    ///
+294    /// This allows passive observers to easily judge the round trip time of a connection, which can
+295    /// be useful for network administration but sacrifices a small amount of privacy.
+296    pub fn allow_spin(&mut self, value: bool) -> &mut Self {
+297        self.allow_spin = value;
+298        self
+299    }
+300
+301    /// Maximum number of incoming application datagram bytes to buffer, or None to disable
+302    /// incoming datagrams
+303    ///
+304    /// The peer is forbidden to send single datagrams larger than this size. If the aggregate size
+305    /// of all datagrams that have been received from the peer but not consumed by the application
+306    /// exceeds this value, old datagrams are dropped until it is no longer exceeded.
+307    pub fn datagram_receive_buffer_size(&mut self, value: Option<usize>) -> &mut Self {
+308        self.datagram_receive_buffer_size = value;
+309        self
+310    }
+311
+312    /// Maximum number of outgoing application datagram bytes to buffer
+313    ///
+314    /// While datagrams are sent ASAP, it is possible for an application to generate data faster
+315    /// than the link, or even the underlying hardware, can transmit them. This limits the amount of
+316    /// memory that may be consumed in that case. When the send buffer is full and a new datagram is
+317    /// sent, older datagrams are dropped until sufficient space is available.
+318    pub fn datagram_send_buffer_size(&mut self, value: usize) -> &mut Self {
+319        self.datagram_send_buffer_size = value;
+320        self
+321    }
+322
+323    /// Whether to force every packet number to be used
+324    ///
+325    /// By default, packet numbers are occasionally skipped to ensure peers aren't ACKing packets
+326    /// before they see them.
+327    #[cfg(test)]
+328    pub(crate) fn deterministic_packet_numbers(&mut self, enabled: bool) -> &mut Self {
+329        self.deterministic_packet_numbers = enabled;
+330        self
+331    }
+332
+333    /// How to construct new `congestion::Controller`s
+334    ///
+335    /// Typically the refcounted configuration of a `congestion::Controller`,
+336    /// e.g. a `congestion::NewRenoConfig`.
+337    ///
+338    /// # Example
+339    /// ```
+340    /// # use noq_proto::*; use std::sync::Arc;
+341    /// let mut config = TransportConfig::default();
+342    /// config.congestion_controller_factory(Arc::new(congestion::NewRenoConfig::default()));
+343    /// ```
+344    pub fn congestion_controller_factory(
+345        &mut self,
+346        factory: Arc<dyn congestion::ControllerFactory + Send + Sync + 'static>,
+347    ) -> &mut Self {
+348        self.congestion_controller_factory = factory;
+349        self
+350    }
+351
+352    /// Whether to use "Generic Segmentation Offload" to accelerate transmits, when supported by the
+353    /// environment
+354    ///
+355    /// Defaults to `true`.
+356    ///
+357    /// GSO dramatically reduces CPU consumption when sending large numbers of packets with the same
+358    /// headers, such as when transmitting bulk data on a connection. However, it is not supported
+359    /// by all network interface drivers or packet inspection tools. `noq-udp` will attempt to
+360    /// disable GSO automatically when unavailable, but this can lead to spurious packet loss at
+361    /// startup, temporarily degrading performance.
+362    pub fn enable_segmentation_offload(&mut self, enabled: bool) -> &mut Self {
+363        self.enable_segmentation_offload = enabled;
+364        self
+365    }
+366
+367    /// Whether to send observed address reports to peers.
+368    ///
+369    /// This will aid peers in inferring their reachable address, which in most NATd networks
+370    /// will not be easily available to them.
+371    pub fn send_observed_address_reports(&mut self, enabled: bool) -> &mut Self {
+372        self.address_discovery_role.send_reports_to_peers(enabled);
+373        self
+374    }
+375
+376    /// Whether to receive observed address reports from other peers.
+377    ///
+378    /// Peers with the address discovery extension enabled that are willing to provide observed
+379    /// address reports will do so if this transport parameter is set. In general, observed address
+380    /// reports cannot be trusted. This, however, can aid the current endpoint in inferring its
+381    /// reachable address, which in most NATd networks will not be easily available.
+382    pub fn receive_observed_address_reports(&mut self, enabled: bool) -> &mut Self {
+383        self.address_discovery_role
+384            .receive_reports_from_peers(enabled);
+385        self
+386    }
+387
+388    /// Enables the Multipath Extension for QUIC.
+389    ///
+390    /// Setting this to any nonzero value will enable the Multipath Extension for QUIC,
+391    /// <https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/>.
+392    ///
+393    /// The value provided specifies the number maximum number of paths this endpoint may open
+394    /// concurrently when multipath is negotiated. For any path to be opened, the remote must
+395    /// enable multipath as well.
+396    pub fn max_concurrent_multipath_paths(&mut self, max_concurrent: u32) -> &mut Self {
+397        self.max_concurrent_multipath_paths = NonZeroU32::new(max_concurrent);
+398        self
+399    }
+400
+401    /// Sets a default per-path maximum idle timeout
+402    ///
+403    /// If the path is idle for this long the path will be abandoned. Bear in mind this will
+404    /// interact with the [`TransportConfig::max_idle_timeout`], if the last path is
+405    /// abandoned the entire connection will be closed.
+406    ///
+407    /// You can also change this using [`Connection::set_path_max_idle_timeout`] for
+408    /// existing paths.
+409    ///
+410    /// [`Connection::set_path_max_idle_timeout`]: crate::Connection::set_path_max_idle_timeout
+411    pub fn default_path_max_idle_timeout(&mut self, timeout: Option<Duration>) -> &mut Self {
+412        self.default_path_max_idle_timeout = timeout;
+413        self
+414    }
+415
+416    /// Sets a default per-path keep alive interval
+417    ///
+418    /// Note that this does not interact with the connection-wide
+419    /// [`TransportConfig::keep_alive_interval`].  This setting will keep this path active,
+420    /// [`TransportConfig::keep_alive_interval`] will keep the connection active, with no
+421    /// control over which path is used for this.
+422    ///
+423    /// You can also change this using [`Connection::set_path_keep_alive_interval`] for
+424    /// existing path.
+425    ///
+426    /// [`Connection::set_path_keep_alive_interval`]: crate::Connection::set_path_keep_alive_interval
+427    pub fn default_path_keep_alive_interval(&mut self, interval: Option<Duration>) -> &mut Self {
+428        self.default_path_keep_alive_interval = interval;
+429        self
+430    }
+431
+432    /// Get the initial max [`crate::PathId`] this endpoint allows.
+433    ///
+434    /// Returns `None` if multipath is disabled.
+435    pub(crate) fn get_initial_max_path_id(&self) -> Option<crate::PathId> {
+436        self.max_concurrent_multipath_paths
+437            // a max_concurrent_multipath_paths value of 1 only allows the first path, which
+438            // has id 0
+439            .map(|nonzero_concurrent| nonzero_concurrent.get() - 1)
+440            .map(Into::into)
+441    }
+442
+443    /// Sets the maximum number of nat traversal addresses this endpoint allows the remote to
+444    /// advertise
+445    ///
+446    /// Setting this to any nonzero value will enable n0's nat traversal protocol, loosely based in
+447    /// the Nat Traversal Extension for QUIC, see
+448    /// <https://www.ietf.org/archive/id/draft-seemann-quic-nat-traversal-02.html>
+449    ///
+450    /// This implementation expects the multipath extension to be enabled as well. if not yet
+451    /// enabled via [`Self::max_concurrent_multipath_paths`], then that setting is set to 8.
+452    pub fn max_remote_nat_traversal_addresses(&mut self, max_addresses: u8) -> &mut Self {
+453        self.max_remote_nat_traversal_addresses = NonZeroU8::new(max_addresses);
+454        if max_addresses != 0 && self.max_concurrent_multipath_paths.is_none() {
+455            self.max_concurrent_multipath_paths(8);
+456        }
+457        self
+458    }
+459
+460    /// Configures qlog capturing by setting a [`QlogFactory`].
+461    ///
+462    /// This assigns a [`QlogFactory`] that produces qlog capture configurations for
+463    /// individual connections.
+464    #[cfg(feature = "qlog")]
+465    pub fn qlog_factory(&mut self, factory: Arc<dyn QlogFactory>) -> &mut Self {
+466        self.qlog_factory = Some(factory);
+467        self
+468    }
+469
+470    /// Configures qlog capturing through the `QLOGDIR` environment variable.
+471    ///
+472    /// This uses [`QlogFileFactory::from_env`] to create a factory to write qlog traces
+473    /// into the directory set through the `QLOGDIR` environment variable.
+474    ///
+475    /// If `QLOGDIR` is not set, no traces will be written. If `QLOGDIR` is set to a path
+476    /// that does not exist, it will be created.
+477    ///
+478    /// The files will be prefixed with `prefix`.
+479    #[cfg(feature = "qlog")]
+480    pub fn qlog_from_env(&mut self, prefix: &str) -> &mut Self {
+481        self.qlog_factory(Arc::new(QlogFileFactory::from_env().with_prefix(prefix)))
+482    }
+483
+484    /// Configures qlog capturing into a directory.
+485    ///
+486    /// This uses [`QlogFileFactory`] to create a factory to write qlog traces into
+487    /// the specified directory.  The files will be prefixed with `prefix`.
+488    #[cfg(feature = "qlog")]
+489    pub fn qlog_from_path(&mut self, path: impl AsRef<Path>, prefix: &str) -> &mut Self {
+490        self.qlog_factory(Arc::new(
+491            QlogFileFactory::new(path.as_ref().to_owned()).with_prefix(prefix),
+492        ))
+493    }
+494
+495    pub(crate) fn create_qlog_sink(
+496        &self,
+497        side: Side,
+498        remote: SocketAddr,
+499        initial_dst_cid: ConnectionId,
+500        now: Instant,
+501    ) -> QlogSink {
+502        #[cfg(not(feature = "qlog"))]
+503        let sink = {
+504            let _ = (side, remote, initial_dst_cid, now);
+505            QlogSink::default()
+506        };
+507
+508        #[cfg(feature = "qlog")]
+509        let sink = {
+510            if let Some(config) = self
+511                .qlog_factory
+512                .as_ref()
+513                .and_then(|factory| factory.for_connection(side, remote, initial_dst_cid, now))
+514            {
+515                QlogSink::new(config, initial_dst_cid, side, now)
+516            } else {
+517                QlogSink::default()
+518            }
+519        };
+520
+521        sink
+522    }
+523}
+524
+525impl Default for TransportConfig {
+526    fn default() -> Self {
+527        const EXPECTED_RTT: u32 = 100; // ms
+528        const MAX_STREAM_BANDWIDTH: u32 = 12500 * 1000; // bytes/s
+529        // Window size needed to avoid pipeline
+530        // stalls
+531        const STREAM_RWND: u32 = MAX_STREAM_BANDWIDTH / 1000 * EXPECTED_RTT;
+532
+533        Self {
+534            max_concurrent_bidi_streams: 100u32.into(),
+535            max_concurrent_uni_streams: 100u32.into(),
+536            // 30 second default recommended by RFC 9308 § 3.2
+537            max_idle_timeout: Some(VarInt(30_000)),
+538            stream_receive_window: STREAM_RWND.into(),
+539            receive_window: VarInt::MAX,
+540            send_window: (8 * STREAM_RWND).into(),
+541            send_fairness: true,
+542
+543            packet_threshold: 3,
+544            time_threshold: 9.0 / 8.0,
+545            initial_rtt: Duration::from_millis(333), // per spec, intentionally distinct from EXPECTED_RTT
+546            initial_mtu: INITIAL_MTU,
+547            min_mtu: INITIAL_MTU,
+548            mtu_discovery_config: Some(MtuDiscoveryConfig::default()),
+549            pad_to_mtu: false,
+550            ack_frequency_config: None,
+551            max_outgoing_bytes_per_second: None,
+552
+553            persistent_congestion_threshold: 3,
+554            keep_alive_interval: None,
+555            crypto_buffer_size: 16 * 1024,
+556            allow_spin: true,
+557            datagram_receive_buffer_size: Some(STREAM_RWND as usize),
+558            datagram_send_buffer_size: 1024 * 1024,
+559            #[cfg(test)]
+560            deterministic_packet_numbers: false,
+561
+562            congestion_controller_factory: Arc::new(congestion::CubicConfig::default()),
+563
+564            enable_segmentation_offload: true,
+565
+566            address_discovery_role: address_discovery::Role::default(),
+567
+568            // disabled multipath by default
+569            max_concurrent_multipath_paths: None,
+570            default_path_max_idle_timeout: None,
+571            default_path_keep_alive_interval: None,
+572
+573            // nat traversal disabled by default
+574            max_remote_nat_traversal_addresses: None,
+575
+576            #[cfg(feature = "qlog")]
+577            qlog_factory: None,
+578        }
+579    }
+580}
+581
+582impl fmt::Debug for TransportConfig {
+583    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+584        let Self {
+585            max_concurrent_bidi_streams,
+586            max_concurrent_uni_streams,
+587            max_idle_timeout,
+588            stream_receive_window,
+589            receive_window,
+590            send_window,
+591            send_fairness,
+592            packet_threshold,
+593            time_threshold,
+594            initial_rtt,
+595            initial_mtu,
+596            min_mtu,
+597            mtu_discovery_config,
+598            pad_to_mtu,
+599            ack_frequency_config,
+600            max_outgoing_bytes_per_second,
+601            persistent_congestion_threshold,
+602            keep_alive_interval,
+603            crypto_buffer_size,
+604            allow_spin,
+605            datagram_receive_buffer_size,
+606            datagram_send_buffer_size,
+607            #[cfg(test)]
+608                deterministic_packet_numbers: _,
+609            congestion_controller_factory: _,
+610            enable_segmentation_offload,
+611            address_discovery_role,
+612            max_concurrent_multipath_paths,
+613            default_path_max_idle_timeout,
+614            default_path_keep_alive_interval,
+615            max_remote_nat_traversal_addresses,
+616            #[cfg(feature = "qlog")]
+617            qlog_factory,
+618        } = self;
+619        let mut s = fmt.debug_struct("TransportConfig");
+620
+621        s.field("max_concurrent_bidi_streams", max_concurrent_bidi_streams)
+622            .field("max_concurrent_uni_streams", max_concurrent_uni_streams)
+623            .field("max_idle_timeout", max_idle_timeout)
+624            .field("stream_receive_window", stream_receive_window)
+625            .field("receive_window", receive_window)
+626            .field("send_window", send_window)
+627            .field("send_fairness", send_fairness)
+628            .field("packet_threshold", packet_threshold)
+629            .field("time_threshold", time_threshold)
+630            .field("initial_rtt", initial_rtt)
+631            .field("initial_mtu", initial_mtu)
+632            .field("min_mtu", min_mtu)
+633            .field("mtu_discovery_config", mtu_discovery_config)
+634            .field("pad_to_mtu", pad_to_mtu)
+635            .field("ack_frequency_config", ack_frequency_config)
+636            .field(
+637                "max_outgoing_bytes_per_second",
+638                max_outgoing_bytes_per_second,
+639            )
+640            .field(
+641                "persistent_congestion_threshold",
+642                persistent_congestion_threshold,
+643            )
+644            .field("keep_alive_interval", keep_alive_interval)
+645            .field("crypto_buffer_size", crypto_buffer_size)
+646            .field("allow_spin", allow_spin)
+647            .field("datagram_receive_buffer_size", datagram_receive_buffer_size)
+648            .field("datagram_send_buffer_size", datagram_send_buffer_size)
+649            // congestion_controller_factory not debug
+650            .field("enable_segmentation_offload", enable_segmentation_offload)
+651            .field("address_discovery_role", address_discovery_role)
+652            .field(
+653                "max_concurrent_multipath_paths",
+654                max_concurrent_multipath_paths,
+655            )
+656            .field(
+657                "default_path_max_idle_timeout",
+658                default_path_max_idle_timeout,
+659            )
+660            .field(
+661                "default_path_keep_alive_interval",
+662                default_path_keep_alive_interval,
+663            )
+664            .field(
+665                "max_remote_nat_traversal_addresses",
+666                max_remote_nat_traversal_addresses,
+667            );
+668        #[cfg(feature = "qlog")]
+669        s.field("qlog_factory", &qlog_factory.is_some());
+670
+671        s.finish_non_exhaustive()
+672    }
+673}
+674
+675/// Parameters for controlling the peer's acknowledgement frequency
+676///
+677/// The parameters provided in this config will be sent to the peer at the beginning of the
+678/// connection, so it can take them into account when sending acknowledgements (see each parameter's
+679/// description for details on how it influences acknowledgement frequency).
+680///
+681/// noq's implementation follows the fourth draft of the
+682/// [QUIC Acknowledgement Frequency extension](https://datatracker.ietf.org/doc/html/draft-ietf-quic-ack-frequency-04).
+683/// The defaults produce behavior slightly different than the behavior without this extension,
+684/// because they change the way reordered packets are handled (see
+685/// [`AckFrequencyConfig::reordering_threshold`] for details).
+686#[derive(Clone, Debug)]
+687pub struct AckFrequencyConfig {
+688    pub(crate) ack_eliciting_threshold: VarInt,
+689    pub(crate) max_ack_delay: Option<Duration>,
+690    pub(crate) reordering_threshold: VarInt,
+691}
+692
+693impl AckFrequencyConfig {
+694    /// The ack-eliciting threshold we will request the peer to use
+695    ///
+696    /// This threshold represents the number of ack-eliciting packets an endpoint may receive
+697    /// without immediately sending an ACK.
+698    ///
+699    /// The remote peer should send at least one ACK frame when more than this number of
+700    /// ack-eliciting packets have been received. A value of 0 results in a receiver immediately
+701    /// acknowledging every ack-eliciting packet.
+702    ///
+703    /// Defaults to 1, which sends ACK frames for every other ack-eliciting packet.
+704    pub fn ack_eliciting_threshold(&mut self, value: VarInt) -> &mut Self {
+705        self.ack_eliciting_threshold = value;
+706        self
+707    }
+708
+709    /// The `max_ack_delay` we will request the peer to use
+710    ///
+711    /// This parameter represents the maximum amount of time that an endpoint waits before sending
+712    /// an ACK when the ack-eliciting threshold hasn't been reached.
+713    ///
+714    /// The effective `max_ack_delay` will be clamped to be at least the peer's `min_ack_delay`
+715    /// transport parameter, and at most the greater of the current path RTT or 25ms.
+716    ///
+717    /// Defaults to `None`, in which case the peer's original `max_ack_delay` will be used, as
+718    /// obtained from its transport parameters.
+719    pub fn max_ack_delay(&mut self, value: Option<Duration>) -> &mut Self {
+720        self.max_ack_delay = value;
+721        self
+722    }
+723
+724    /// The reordering threshold we will request the peer to use
+725    ///
+726    /// This threshold represents the amount of out-of-order packets that will trigger an endpoint
+727    /// to send an ACK, without waiting for `ack_eliciting_threshold` to be exceeded or for
+728    /// `max_ack_delay` to be elapsed.
+729    ///
+730    /// A value of 0 indicates out-of-order packets do not elicit an immediate ACK. A value of 1
+731    /// immediately acknowledges any packets that are received out of order (this is also the
+732    /// behavior when the extension is disabled).
+733    ///
+734    /// It is recommended to set this value to [`TransportConfig::packet_threshold`] minus one.
+735    /// Since the default value for [`TransportConfig::packet_threshold`] is 3, this value defaults
+736    /// to 2.
+737    pub fn reordering_threshold(&mut self, value: VarInt) -> &mut Self {
+738        self.reordering_threshold = value;
+739        self
+740    }
+741}
+742
+743impl Default for AckFrequencyConfig {
+744    fn default() -> Self {
+745        Self {
+746            ack_eliciting_threshold: VarInt(1),
+747            max_ack_delay: None,
+748            reordering_threshold: VarInt(2),
+749        }
+750    }
+751}
+752
+753/// Parameters governing MTU discovery.
+754///
+755/// # The why of MTU discovery
+756///
+757/// By design, QUIC ensures during the handshake that the network path between the client and the
+758/// server is able to transmit unfragmented UDP packets with a body of 1200 bytes. In other words,
+759/// once the connection is established, we know that the network path's maximum transmission unit
+760/// (MTU) is of at least 1200 bytes (plus IP and UDP headers). Because of this, a QUIC endpoint can
+761/// split outgoing data in packets of 1200 bytes, with confidence that the network will be able to
+762/// deliver them (if the endpoint were to send bigger packets, they could prove too big and end up
+763/// being dropped).
+764///
+765/// There is, however, a significant overhead associated to sending a packet. If the same
+766/// information can be sent in fewer packets, that results in higher throughput. The amount of
+767/// packets that need to be sent is inversely proportional to the MTU: the higher the MTU, the
+768/// bigger the packets that can be sent, and the fewer packets that are needed to transmit a given
+769/// amount of bytes.
+770///
+771/// Most networks have an MTU higher than 1200. Through MTU discovery, endpoints can detect the
+772/// path's MTU and, if it turns out to be higher, start sending bigger packets.
+773///
+774/// # MTU discovery internals
+775///
+776/// noq implements MTU discovery through DPLPMTUD (Datagram Packetization Layer Path MTU
+777/// Discovery), described in [section 14.3 of RFC
+778/// 9000](https://www.rfc-editor.org/rfc/rfc9000.html#section-14.3). This method consists of sending
+779/// QUIC packets padded to a particular size (called PMTU probes), and waiting to see if the remote
+780/// peer responds with an ACK. If an ACK is received, that means the probe arrived at the remote
+781/// peer, which in turn means that the network path's MTU is of at least the packet's size. If the
+782/// probe is lost, it is sent another 2 times before concluding that the MTU is lower than the
+783/// packet's size.
+784///
+785/// MTU discovery runs on a schedule (e.g. every 600 seconds) specified through
+786/// [`MtuDiscoveryConfig::interval`]. The first run happens right after the handshake, and
+787/// subsequent discoveries are scheduled to run when the interval has elapsed, starting from the
+788/// last time when MTU discovery completed.
+789///
+790/// Since the search space for MTUs is quite big (the smallest possible MTU is 1200, and the highest
+791/// is 65527), noq performs a binary search to keep the number of probes as low as possible. The
+792/// lower bound of the search is equal to [`TransportConfig::initial_mtu`] in the
+793/// initial MTU discovery run, and is equal to the currently discovered MTU in subsequent runs. The
+794/// upper bound is determined by the minimum of [`MtuDiscoveryConfig::upper_bound`] and the
+795/// `max_udp_payload_size` transport parameter received from the peer during the handshake.
+796///
+797/// # Black hole detection
+798///
+799/// If, at some point, the network path no longer accepts packets of the detected size, packet loss
+800/// will eventually trigger black hole detection and reset the detected MTU to 1200. In that case,
+801/// MTU discovery will be triggered after [`MtuDiscoveryConfig::black_hole_cooldown`] (ignoring the
+802/// timer that was set based on [`MtuDiscoveryConfig::interval`]).
+803///
+804/// # Interaction between peers
+805///
+806/// There is no guarantee that the MTU on the path between A and B is the same as the MTU of the
+807/// path between B and A. Therefore, each peer in the connection needs to run MTU discovery
+808/// independently in order to discover the path's MTU.
+809#[derive(Clone, Debug)]
+810pub struct MtuDiscoveryConfig {
+811    pub(crate) interval: Duration,
+812    pub(crate) upper_bound: u16,
+813    pub(crate) minimum_change: u16,
+814    pub(crate) black_hole_cooldown: Duration,
+815}
+816
+817impl MtuDiscoveryConfig {
+818    /// Specifies the time to wait after completing MTU discovery before starting a new MTU
+819    /// discovery run.
+820    ///
+821    /// Defaults to 600 seconds, as recommended by [RFC
+822    /// 8899](https://www.rfc-editor.org/rfc/rfc8899).
+823    pub fn interval(&mut self, value: Duration) -> &mut Self {
+824        self.interval = value;
+825        self
+826    }
+827
+828    /// Specifies the upper bound to the max UDP payload size that MTU discovery will search for.
+829    ///
+830    /// Defaults to 1452, to stay within Ethernet's MTU when using IPv4 and IPv6. The highest
+831    /// allowed value is 65527, which corresponds to the maximum permitted UDP payload on IPv6.
+832    ///
+833    /// It is safe to use an arbitrarily high upper bound, regardless of the network path's MTU. The
+834    /// only drawback is that MTU discovery might take more time to finish.
+835    pub fn upper_bound(&mut self, value: u16) -> &mut Self {
+836        self.upper_bound = value.min(MAX_UDP_PAYLOAD);
+837        self
+838    }
+839
+840    /// Specifies the amount of time that MTU discovery should wait after a black hole was detected
+841    /// before running again. Defaults to one minute.
+842    ///
+843    /// Black hole detection can be spuriously triggered in case of congestion, so it makes sense to
+844    /// try MTU discovery again after a short period of time.
+845    pub fn black_hole_cooldown(&mut self, value: Duration) -> &mut Self {
+846        self.black_hole_cooldown = value;
+847        self
+848    }
+849
+850    /// Specifies the minimum MTU change to stop the MTU discovery phase.
+851    /// Defaults to 20.
+852    pub fn minimum_change(&mut self, value: u16) -> &mut Self {
+853        self.minimum_change = value;
+854        self
+855    }
+856}
+857
+858impl Default for MtuDiscoveryConfig {
+859    fn default() -> Self {
+860        Self {
+861            interval: Duration::from_secs(600),
+862            upper_bound: 1452,
+863            black_hole_cooldown: Duration::from_secs(60),
+864            minimum_change: 20,
+865        }
+866    }
+867}
+868
+869/// Maximum duration of inactivity to accept before timing out the connection
+870///
+871/// This wraps an underlying [`VarInt`], representing the duration in milliseconds. Values can be
+872/// constructed by converting directly from `VarInt`, or using `TryFrom<Duration>`.
+873///
+874/// ```
+875/// # use std::{convert::TryFrom, time::Duration};
+876/// # use noq_proto::{IdleTimeout, VarIntBoundsExceeded, VarInt};
+877/// # fn main() -> Result<(), VarIntBoundsExceeded> {
+878/// // A `VarInt`-encoded value in milliseconds
+879/// let timeout = IdleTimeout::from(VarInt::from_u32(10_000));
+880///
+881/// // Try to convert a `Duration` into a `VarInt`-encoded timeout
+882/// let timeout = IdleTimeout::try_from(Duration::from_secs(10))?;
+883/// # Ok(())
+884/// # }
+885/// ```
+886#[derive(Default, Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
+887pub struct IdleTimeout(VarInt);
+888
+889impl From<VarInt> for IdleTimeout {
+890    fn from(inner: VarInt) -> Self {
+891        Self(inner)
+892    }
+893}
+894
+895impl std::convert::TryFrom<Duration> for IdleTimeout {
+896    type Error = VarIntBoundsExceeded;
+897
+898    fn try_from(timeout: Duration) -> Result<Self, Self::Error> {
+899        let inner = VarInt::try_from(timeout.as_millis())?;
+900        Ok(Self(inner))
+901    }
+902}
+903
+904impl fmt::Debug for IdleTimeout {
+905    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+906        self.0.fmt(f)
+907    }
+908}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/congestion.rs.html b/pr/672/docs/src/noq_proto/congestion.rs.html new file mode 100644 index 000000000..9c36edc6d --- /dev/null +++ b/pr/672/docs/src/noq_proto/congestion.rs.html @@ -0,0 +1,148 @@ +congestion.rs - source

noq_proto/
congestion.rs

1//! Logic for controlling the rate at which data is sent
+2
+3use crate::connection::RttEstimator;
+4use crate::{Duration, Instant};
+5use std::any::Any;
+6use std::sync::Arc;
+7
+8mod bbr3;
+9mod cubic;
+10mod new_reno;
+11
+12pub use bbr3::{Bbr3, Bbr3Config};
+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, largest_pn: u64) {}
+21
+22    /// One packet was just sent
+23    #[allow(unused_variables)]
+24    fn on_packet_sent(&mut self, now: Instant, bytes: u16, pn: u64) {}
+25
+26    /// Packet deliveries were confirmed
+27    ///
+28    /// `app_limited` indicates whether the connection was blocked on outgoing
+29    /// application data prior to receiving these acknowledgements.
+30    #[allow(unused_variables)]
+31    fn on_ack(
+32        &mut self,
+33        now: Instant,
+34        sent: Instant,
+35        bytes: u64,
+36        pn: u64,
+37        app_limited: bool,
+38        rtt: &RttEstimator,
+39    ) {
+40    }
+41
+42    /// Packets are acked in batches, all with the same `now` argument. This indicates one of those batches has completed.
+43    #[allow(unused_variables)]
+44    fn on_end_acks(
+45        &mut self,
+46        now: Instant,
+47        in_flight: u64,
+48        app_limited: bool,
+49        largest_packet_num_acked: Option<u64>,
+50    ) {
+51    }
+52
+53    /// Packets were deemed lost or marked congested
+54    ///
+55    /// `in_persistent_congestion` indicates whether all packets sent within the persistent
+56    /// congestion threshold period ending when the most recent packet in this batch was sent were
+57    /// lost.
+58    /// `lost_bytes` indicates how many bytes were lost. This value will be 0 for ECN triggers.
+59    /// `largest_lost_pn` indicates the packet number of the packet with the highest packet number
+60    /// in the congestion event.
+61    fn on_congestion_event(
+62        &mut self,
+63        now: Instant,
+64        sent: Instant,
+65        is_persistent_congestion: bool,
+66        is_ecn: bool,
+67        lost_bytes: u64,
+68        largest_lost_pn: u64,
+69    );
+70
+71    /// One packet was just lost
+72    #[allow(unused_variables)]
+73    fn on_packet_lost(&mut self, lost_bytes: u16, pn: u64, now: Instant) {}
+74
+75    /// Packets were incorrectly deemed lost
+76    ///
+77    /// This function is called when all packets that were deemed lost (for instance because
+78    /// of packet reordering) are acknowledged after the congestion event was raised.
+79    fn on_spurious_congestion_event(&mut self) {}
+80
+81    /// The known MTU for the current network path has been updated
+82    fn on_mtu_update(&mut self, new_mtu: u16);
+83
+84    /// The peer's ACK-frequency parameters have changed
+85    ///
+86    /// `ack_eliciting_threshold` is the number of ack-eliciting packets the peer may receive
+87    /// before being required to send an immediate ACK (per the QUIC ACK frequency extension).
+88    /// `requested_max_ack_delay` is the maximum delay we asked the peer to wait before sending
+89    /// an ACK when the threshold hasn't been reached.
+90    ///
+91    /// Controllers can use this to refine estimates that depend on peer ACK behavior (e.g.
+92    /// BBR's offload budget).
+93    #[allow(unused_variables)]
+94    fn on_ack_frequency_update(
+95        &mut self,
+96        ack_eliciting_threshold: u64,
+97        requested_max_ack_delay: Duration,
+98    ) {
+99    }
+100
+101    /// Number of ack-eliciting bytes that may be in flight
+102    fn window(&self) -> u64;
+103
+104    /// Retrieve implementation-specific metrics used to populate `qlog` traces when they are enabled
+105    /// This is also used to alter the pacing of the connection with
+106    /// `pacing_rate` and `send_quantum`
+107    fn metrics(&self) -> ControllerMetrics {
+108        ControllerMetrics {
+109            congestion_window: self.window(),
+110            ssthresh: None,
+111            pacing_rate: None,
+112            send_quantum: None,
+113        }
+114    }
+115
+116    /// Duplicate the controller's state
+117    fn clone_box(&self) -> Box<dyn Controller>;
+118
+119    /// Initial congestion window
+120    fn initial_window(&self) -> u64;
+121
+122    /// Returns Self for use in down-casting to extract implementation details
+123    fn into_any(self: Box<Self>) -> Box<dyn Any>;
+124}
+125
+126/// Common congestion controller metrics used both for logging purposes
+127/// but also to alter the pacing of the connection with
+128/// `pacing_rate` and `send_quantum`
+129#[derive(Default)]
+130#[non_exhaustive]
+131pub struct ControllerMetrics {
+132    /// Congestion window (bytes)
+133    pub congestion_window: u64,
+134    /// Slow start threshold (bytes)
+135    pub ssthresh: Option<u64>,
+136    /// Pacing rate (bytes/s)
+137    pub pacing_rate: Option<u64>,
+138    /// Send Quantum (bytes) used to control the size of packet bursts
+139    pub send_quantum: Option<u64>,
+140}
+141
+142/// Constructs controllers on demand
+143pub trait ControllerFactory {
+144    /// Construct a fresh `Controller`
+145    fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller>;
+146}
+147
+148const BASE_DATAGRAM_SIZE: u64 = 1200;
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/congestion/bbr3/max_filter.rs.html b/pr/672/docs/src/noq_proto/congestion/bbr3/max_filter.rs.html new file mode 100644 index 000000000..572726b6a --- /dev/null +++ b/pr/672/docs/src/noq_proto/congestion/bbr3/max_filter.rs.html @@ -0,0 +1,151 @@ +max_filter.rs - source

noq_proto/congestion/bbr3/
max_filter.rs

1use std::fmt::Debug;
+2
+3const MAX_FILTER_LEN: usize = 3;
+4
+5/// Based on Linux kernel code released here:
+6/// <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f672258391b42a5c7cc2732c9c063e56a85c8dbe>
+7///
+8/// Kathleen Nichols' algorithm for tracking the maximum
+9/// value of a data stream over some fixed time interval.  (E.g.,
+10/// the maximum Bandwidth achieved over the past 3 rounds.) It uses constant
+11/// space and constant time per update yet almost always delivers
+12/// the same maximum as an implementation that has to keep all the
+13/// data in the window.
+14///
+15/// The algorithm keeps track of the best, 2nd best & 3rd highest max
+16/// values, maintaining an invariant that the measurement time of
+17/// the n'th best >= n-1'th best. It also makes sure that the three
+18/// values are widely separated in the time window since that bounds
+19/// the worst case error when that data is monotonically increasing
+20/// over the window.
+21///
+22/// Upon getting a new max, we can forget everything earlier because
+23/// it has no value - the new max is >= everything else in the window
+24/// by definition, and it samples the most recent one. So we restart fresh on
+25/// every new max and overwrites 2nd & 3rd choices. The same property
+26/// holds for 2nd & 3rd best.
+27#[derive(Copy, Clone, Debug)]
+28pub(super) struct MaxFilter {
+29    window: u64,
+30    // sample on index 0 has the maximum value followed in descending order
+31    // by samples on index 1 and then 2
+32    samples: [MaxSample; MAX_FILTER_LEN],
+33}
+34
+35impl MaxFilter {
+36    pub(super) fn new(window: u64) -> Self {
+37        Self {
+38            window,
+39            samples: [Default::default(); MAX_FILTER_LEN],
+40        }
+41    }
+42    pub(super) fn get_max(&self) -> u64 {
+43        self.samples[0].value.unwrap_or(0)
+44    }
+45
+46    /// `current_round` represents a sequence number counting upwards from 0 monotonically
+47    /// `measurement` is what is tracked as the max values over time
+48    pub(super) fn update_max(&mut self, current_round: u64, measurement: u64) {
+49        let sample = MaxSample {
+50            round: current_round,
+51            value: Some(measurement),
+52        };
+53
+54        if self.samples[0].value.is_none()  // uninitialised
+55            ||  sample.value >= self.samples[0].value // found new max?
+56            ||  sample.round.saturating_sub(self.samples[2].round) > self.window
+57        // nothing left in window?
+58        {
+59            self.samples.fill(sample); // forget earlier samples
+60            return;
+61        }
+62
+63        if sample.value >= self.samples[1].value {
+64            self.samples[1] = sample;
+65            self.samples[2] = sample;
+66        } else if sample.value >= self.samples[2].value {
+67            self.samples[2] = sample;
+68        }
+69
+70        self.subwin_update(sample);
+71    }
+72
+73    /// As time advances, update the 1st, 2nd, and 3rd choices.
+74    fn subwin_update(&mut self, sample: MaxSample) {
+75        let dt = sample.round.saturating_sub(self.samples[0].round);
+76        if dt > self.window {
+77            /*
+78             * Passed entire window without a new sample so make 2nd
+79             * choice the new sample & 3rd choice the new 2nd choice.
+80             * we may have to iterate this since our 2nd choice
+81             * may also be outside the window (we checked on entry
+82             * that the third choice was in the window).
+83             */
+84            self.samples[0] = self.samples[1];
+85            self.samples[1] = self.samples[2];
+86            self.samples[2] = sample;
+87            if sample.round.saturating_sub(self.samples[0].round) > self.window {
+88                self.samples[0] = self.samples[1];
+89                self.samples[1] = self.samples[2];
+90                self.samples[2] = sample;
+91            }
+92        } else if self.samples[1].round == self.samples[0].round && dt > self.window / 4 {
+93            /*
+94             * We've passed a quarter of the window without a new sample
+95             * so take a 2nd choice from the 2nd quarter of the window.
+96             */
+97            self.samples[2] = sample;
+98            self.samples[1] = sample;
+99        } else if self.samples[2].round == self.samples[1].round && dt > self.window / 2 {
+100            /*
+101             * We've passed half the window without finding a new sample
+102             * so take a 3rd choice from the last half of the window
+103             */
+104            self.samples[2] = sample;
+105        }
+106    }
+107}
+108
+109impl Default for MaxFilter {
+110    fn default() -> Self {
+111        Self {
+112            window: 10,
+113            samples: [Default::default(); MAX_FILTER_LEN],
+114        }
+115    }
+116}
+117
+118#[derive(Debug, Copy, Clone, Default)]
+119struct MaxSample {
+120    /// `round` count, not a timestamp as per <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.1>
+121    /// can also be a count of cycle as per <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.6>
+122    round: u64,
+123    value: Option<u64>,
+124}
+125
+126#[cfg(test)]
+127mod test {
+128    use super::*;
+129
+130    #[test]
+131    fn test() {
+132        let round = 25;
+133        let mut max_filter = MaxFilter::default();
+134        max_filter.update_max(round + 1, 100);
+135        assert_eq!(100, max_filter.get_max());
+136        max_filter.update_max(round + 3, 120);
+137        assert_eq!(120, max_filter.get_max());
+138        max_filter.update_max(round + 5, 160);
+139        assert_eq!(160, max_filter.get_max());
+140        max_filter.update_max(round + 7, 100);
+141        assert_eq!(160, max_filter.get_max());
+142        max_filter.update_max(round + 10, 100);
+143        assert_eq!(160, max_filter.get_max());
+144        max_filter.update_max(round + 14, 100);
+145        assert_eq!(160, max_filter.get_max());
+146        max_filter.update_max(round + 16, 100);
+147        assert_eq!(100, max_filter.get_max());
+148        max_filter.update_max(round + 18, 130);
+149        assert_eq!(130, max_filter.get_max());
+150    }
+151}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/congestion/bbr3/mod.rs.html b/pr/672/docs/src/noq_proto/congestion/bbr3/mod.rs.html new file mode 100644 index 000000000..0537f6225 --- /dev/null +++ b/pr/672/docs/src/noq_proto/congestion/bbr3/mod.rs.html @@ -0,0 +1,1743 @@ +mod.rs - source

noq_proto/congestion/bbr3/
mod.rs

1mod max_filter;
+2
+3use crate::RttEstimator;
+4use crate::congestion::bbr3::max_filter::MaxFilter;
+5use crate::congestion::{Controller, ControllerFactory, ControllerMetrics};
+6use crate::{Duration, Instant};
+7use rand::{RngExt, SeedableRng};
+8use rand_pcg::Pcg32;
+9use std::any::Any;
+10use std::cmp::{max, min};
+11use std::collections::VecDeque;
+12use std::sync::Arc;
+13
+14/// equivalent to BBR.MaxBwFilterLen <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.10>
+15const MAX_BW_FILTER_LEN: usize = 2;
+16
+17/// equivalent to BBR.ExtraAckedFilterLen <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.11>
+18const EXTRA_ACKED_FILTER_LEN: usize = 10;
+19
+20/// safety mechanism to flag packets as stale within our tracking VecDeque. rounds refer to <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.1>.
+21/// The value of 10 rounds is picked because normally after max(kTimeThreshold * max(smoothed_rtt, latest_rtt), kGranularity) <https://datatracker.ietf.org/doc/html/rfc9002#section-6.1.2>
+22/// the packet should have been declared lost already, this is just to guarantee that the VecDeque doesn't grow indefinitely.
+23const ROUND_COUNT_WINDOW: u64 = 10;
+24
+25/// the minimum for the maximum datagram size <https://datatracker.ietf.org/doc/html/rfc9000#section-14>
+26const MIN_MAX_DATAGRAM_SIZE: u16 = 1200;
+27
+28/// the maximum for the maximum datagram size <https://datatracker.ietf.org/doc/html/rfc9000#section-18.2>
+29const MAX_DATAGRAM_SIZE: u64 = 65527;
+30
+31/// 1.2Mbps in bytes/sec used to determine send_quantum
+32/// this is the pacing rate used where we don't authorize a burst bigger than a full packet
+33/// inspired by a previous version of BBR2 used in cloudflare's quiche
+34const PACING_RATE_1_2MBPS: f64 = 1200.0 * 1000.0;
+35
+36/// 24Mbps in bytes/sec
+37/// this is the pacing rate used where we don't authorize a burst bigger than two full packets
+38/// inspired by a previous version of BBR2 used in cloudflare's quiche
+39const PACING_RATE_24MBPS: f64 = 24000.0 * 1000.0;
+40
+41/// 64 Kb in bytes
+42/// this is the maximum size we want for a quantum in `set_send_quantum`
+43/// inspired by a previous version of BBR2 used in cloudflare's quiche
+44const HIGH_PACE_MAX_QUANTUM: u64 = 64 * 1000;
+45
+46/// equivalent to BBR.StartupPacingGain: A constant specifying the minimum gain value for calculating the pacing rate that will allow
+47/// the sending rate to double each round (4 * ln(2) ~= 2.77)
+48/// BBRStartupPacingGain; used in Startup mode for BBR.pacing_gain. <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
+49const STARTUP_PACING_GAIN: f64 = 2.773;
+50
+51/// default pacing gain is 1, when cruising, probing for RTT or refilling <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
+52const DEFAULT_PACING_GAIN: f64 = 1.0;
+53
+54/// pacing gain when probing bandwidth down <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
+55const PROBE_BW_DOWN_PACING_GAIN: f64 = 0.9;
+56
+57/// pacing gain when probing bandwidth up <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
+58const PROBE_BW_UP_PACING_GAIN: f64 = 1.25;
+59
+60/// equivalent to BBR.PacingMarginPercent: The static discount factor of 1% used to scale BBR.bw to produce C.pacing_rate.
+61const PACING_MARGIN_PERCENT: f64 = 1.0;
+62
+63/// equivalent to BBR.DefaultCwndGain: A constant specifying the minimum gain value that allows the sending rate to double each round (2) BBRStartupCwndGain.
+64/// Used by default in most phases for BBR.cwnd_gain.
+65const DEFAULT_CWND_GAIN: f64 = 2.0;
+66
+67/// equivalent to BBR.DrainPacingGain: A constant specifying the pacing gain value used in Drain mode,
+68/// to attempt to drain the estimated queue at the bottleneck link in one round-trip or less.
+69/// As noted in BBRDrainPacingGain, any value at or below 1 / BBRStartupCwndGain = 1 / 2 = 0.5 will theoretically achieve this.
+70/// BBR uses the value 0.5, which has been shown to offer good performance when compared with other alternatives.
+71/// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.4>
+72/// <https://github.com/google/bbr/blob/master/Documentation/startup/gain/analysis/bbr_drain_gain.pdf>
+73const DRAIN_PACING_GAIN: f64 = 1.0 / DEFAULT_CWND_GAIN;
+74
+75/// cwnd gain used when probing up <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
+76const PROBE_BW_UP_CWND_GAIN: f64 = 2.25;
+77
+78/// cwnd gain used when probing RTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
+79const PROBE_RTT_CWND_GAIN: f64 = 0.5;
+80
+81/// equivalent to BBR.ProbeRTTDuration: A constant specifying the minimum duration for which ProbeRTT state holds C.inflight to BBR.MinPipeCwnd or fewer packets: 200 ms.
+82const PROBE_RTT_DURATION_MS: u64 = 200;
+83
+84/// equivalent to BBR.ProbeRTTInterval: A constant specifying the minimum time interval between ProbeRTT states: 5 secs.
+85const PROBE_RTT_INTERVAL_SEC: u64 = 5;
+86
+87/// equivalent to BBR.LossThresh: A constant specifying the maximum tolerated per-round-trip packet loss rate when probing for bandwidth (the default is 2%).
+88const LOSS_THRESH: f64 = 0.02;
+89
+90/// equivalent to BBR.Beta: A constant specifying the default multiplicative decrease to make upon each round trip during which the connection detects packet loss (the value is 0.7).
+91const BETA: f64 = 0.7;
+92
+93/// equivalent to BBR.Headroom: A constant specifying the multiplicative factor to apply to BBR.inflight_longterm when calculating
+94/// a volume of free headroom to try to leave unused in the path
+95/// (e.g. free space in the bottleneck buffer or free time slots in the bottleneck link) that can be used by cross traffic (the value is 0.15).
+96const HEADROOM: f64 = 0.15;
+97
+98/// equivalent to BBR.MinRTTFilterLen: A constant specifying the length of the BBR.min_rtt min filter window, BBR.MinRTTFilterLen is 10 secs.
+99const MIN_RTT_FILTER_LEN: u64 = 10;
+100
+101/// multiplier used to check growth when validating if the full bandwidth has been reached
+102/// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.1.2-6>
+103const FULL_BW_GROWTH: f64 = 1.25;
+104
+105/// maximum number of rounds needed before we consider that the pipe is full <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.1.2-6>
+106const MAX_FULL_BW_COUNT: u64 = 3;
+107
+108/// when setting `bw_probe_up_rounds` when raising our inflight long term slope we don't go above this
+109/// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-8>
+110const MAX_LONG_TERM_PROBE_UP_ROUNDS: u32 = 30;
+111
+112/// max number of rounds used when deciding to coexist with Reno / CUBIC <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.5.1>
+113const MAX_RENO_ROUNDS: u64 = 63;
+114
+115/// minimum amount of time to wait before probing again <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.5.3-5>
+116const MIN_PROBE_WAIT_MS: u64 = 2000;
+117
+118/// when waiting before probing again we add up to one second of added wait time
+119/// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.5.3-5>
+120const MAX_ADDED_PROBE_WAIT_MS: u64 = 1000;
+121
+122/// Substates when probing bandwidth
+123/// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3>
+124#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+125enum ProbeBwSubstate {
+126    /// Deceleration: sends slower than delivery rate to reduce queue
+127    /// equivalent to ProbeBW_DOWN <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.1>
+128    Down,
+129
+130    /// Cruising: sends at delivery rate to maintain high utilization
+131    /// equivalent to ProbeBW_CRUISE <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.2>
+132    Cruise,
+133
+134    /// Refill: sends at BBR.bw for one RTT to fill pipe before probing up
+135    /// equivalent to ProbeBW_REFILL <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.3>
+136    Refill,
+137
+138    /// Acceleration: sends faster than delivery rate to probe for more bandwidth
+139    /// equivalent to ProbeBW_UP <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.4>
+140    Up,
+141}
+142
+143/// State Machine description from BBR3
+144/// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3>
+145#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+146enum BbrState {
+147    /// Initial state: rapidly probes for bandwidth using high pacing_gain
+148    /// equivalent to Startup <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.1>
+149    Startup,
+150
+151    /// Drains queue created during Startup by using low pacing_gain (< 1.0)
+152    /// equivalent to Drain <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.2>
+153    Drain,
+154
+155    /// Steady-state phase that cycles through bandwidth probing tactics
+156    /// equivalent to ProbeBW states <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3>
+157    ProbeBw(ProbeBwSubstate),
+158
+159    /// Temporarily reduces inflight to measure true min_rtt
+160    /// equivalent to ProbeRTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.4>
+161    ProbeRtt,
+162}
+163
+164/// Ack phases used during ProbeBW states
+165/// equivalent to BBR.ack_phase states <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6>
+166#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+167enum AckPhase {
+168    /// equivalent to ACKS_PROBE_STARTING
+169    ProbeStarting,
+170    /// equivalent to ACKS_PROBE_STOPPING
+171    ProbeStopping,
+172    /// equivalent to ACKS_REFILLING
+173    Refilling,
+174    /// equivalent to ACKS_PROBE_FEEDBACK
+175    ProbeFeedback,
+176}
+177
+178/// Description of a packet for the purposes of analysis through BBR3
+179/// all volumes of data use bytes, all rates of data use bytes/sec
+180/// equivalent to P <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-4.1.2.1.2>
+181#[derive(Debug, Clone, Copy)]
+182struct BbrPacket {
+183    /// equivalent to P.delivered: C.delivered when the packet was sent from transport connection C.
+184    delivered: u64,
+185    /// equivalent to P.delivered_time: C.delivered_time when the packet was sent.
+186    delivered_time: Instant,
+187    /// equivalent to P.first_send_time: C.first_send_time when the packet was sent.
+188    first_send_time: Instant,
+189    /// equivalent to P.send_time: The pacing departure time selected when the packet was scheduled to be sent.
+190    send_time: Instant,
+191    /// equivalent to P.is_app_limited: true if C.app_limited was non-zero when the packet was sent, else false.
+192    is_app_limited: bool,
+193    /// equivalent to P.tx_in_flight: C.inflight immediately after the transmission of packet P.
+194    tx_in_flight: u64,
+195    /// packet number from the connection
+196    packet_number: u64,
+197    /// packet size in bytes
+198    size: u16,
+199    /// equivalent to P.lost: C.lost when the packet was sent
+200    lost: u64,
+201    /// used to flag acknowledgement within our VecDeque, a packet can be flagged lost after having been flagged acknowledged
+202    /// hence the necessity of this flag being set before we remove it from packets.
+203    acknowledged: bool,
+204    /// once a packet has been acknowledged on a given round it is marked for removal on the next round.
+205    stale: bool,
+206    /// used to mark packets stale if they're far from the current round <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.1>
+207    round_count: u64,
+208}
+209
+210/// Description of a per-ack rate sample state that will allow us to determine a short term evolution of the connection
+211/// equivalent to RS <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.2>
+212#[derive(Debug, Clone, Copy)]
+213struct BbrRateSample {
+214    /// equivalent to RS.delivery_rate: The delivery rate (aka bandwidth) sample obtained from the packet that has just been ACKed.
+215    delivery_rate: f64,
+216    /// equivalent to RS.is_app_limited: The P.is_app_limited from the most recent packet
+217    ///    delivered; indicates whether the rate sample is application-limited.
+218    is_app_limited: bool,
+219    /// equivalent to RS.interval: The length of the sampling interval.
+220    interval: Duration,
+221    /// equivalent to RS.delivered: The volume of data delivered between the transmission of the packet that has just been ACKed and the current time.
+222    delivered: u64,
+223    /// equivalent to RS.prior_delivered: The P.delivered count from the most recent packet delivered.
+224    prior_delivered: u64,
+225    /// equivalent to RS.prior_time: The P.delivered_time from the most recent packet delivered.
+226    prior_time: Instant,
+227    /// equivalent to RS.send_elapsed: Send time interval calculated from the most recent
+228    ///    packet delivered (see the "Send Rate" section above).
+229    send_elapsed: Duration,
+230    /// equivalent to RS.ack_elapsed: ACK time interval calculated from the most recent
+231    ///    packet delivered (see the "ACK Rate" section above).
+232    ack_elapsed: Duration,
+233    /// equivalent to RS.rtt: The RTT sample calculated based on the most recently-sent packet of the packets that have just been ACKed.
+234    rtt: Duration,
+235    /// equivalent to RS.tx_in_flight: C.inflight at the time of the transmission of the packet that has just been ACKed
+236    /// (the most recently sent packet among packets ACKed by the ACK that was just received).
+237    tx_in_flight: u64,
+238    /// equivalent to RS.newly_acked: The volume of data in bytes cumulatively or selectively acknowledged upon the ACK that was just received.
+239    newly_acked: u64,
+240    /// equivalent to RS.newly_lost: The volume of data in bytes newly marked lost upon the ACK that was just received.
+241    newly_lost: u64,
+242    /// equivalent to RS.lost: The volume of data in bytes that was declared lost between the transmission
+243    /// and acknowledgment of the packet that has just been ACKed (the most recently sent packet among packets ACKed by the ACK that was just received).
+244    lost: u64,
+245    /// equivalent to RS.last_end_seq
+246    last_end_seq: u64,
+247    /// represents the last packet that was used in the generation of this rate sample
+248    last_packet: BbrPacket,
+249}
+250
+251/// Experimental! Use at your own risk.
+252///
+253/// Aims for reduced buffer bloat and improved performance over high bandwidth-delay product networks.
+254/// Based on <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html>
+255/// equivalent to a combination of BBR and C states
+256/// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.4>
+257/// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.1>
+258#[derive(Debug, Clone)]
+259pub struct Bbr3 {
+260    /// equivalent to C.SMSS The Sender Maximum Send Size in bytes. <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.1>
+261    /// <https://www.rfc-editor.org/rfc/rfc9000#name-datagram-size>
+262    smss: u64,
+263    /// equivalent to C.InitialCwnd: The initial congestion window set by the transport protocol implementation for the connection at initialization time.
+264    initial_cwnd: u64,
+265    /// equivalent to C.delivered: The total amount of data delivered so far over the lifetime of the transport connection C.
+266    /// This MUST NOT include pure ACK packets. It SHOULD include spurious retransmissions that have been acknowledged as delivered.
+267    delivered: u64,
+268    /// equivalent to C.inflight: The connection's best estimate of the number of bytes outstanding in the network.
+269    /// This includes the number of bytes that have been sent and have not been acknowledged or marked as lost since their last transmission
+270    /// (e.g. "pipe" from RFC6675 or "bytes_in_flight" from RFC9002). This MUST NOT include pure ACK packets.
+271    inflight: u64,
+272    /// equivalent to C.is_cwnd_limited: True if the connection has fully utilized C.cwnd at any point in the last packet-timed round trip.
+273    is_cwnd_limited: bool,
+274    /// equivalent to BBR.cycle_count: The virtual time used by the BBR.max_bw filter window.
+275    /// since the BBR.max_bw_filter only needs to track samples from two time slots: the previous ProbeBW cycle and the current ProbeBW cycle.
+276    cycle_count: u64,
+277    /// equivalent to C.cwnd: The transport sender's congestion window. When transmitting data, the sending connection ensures that C.inflight does not exceed C.cwnd.
+278    cwnd: u64,
+279    /// equivalent to C.pacing_rate: The current pacing rate for a BBR flow, which controls inter-packet spacing.
+280    pacing_rate: f64,
+281    /// equivalent to C.send_quantum: The maximum size of a data aggregate scheduled and transmitted together as a unit, e.g., to amortize per-packet transmission overheads.
+282    send_quantum: u64,
+283    /// equivalent to BBR.pacing_gain: The dynamic gain factor used to scale BBR.bw to produce C.pacing_rate.
+284    pacing_gain: f64,
+285    /// default pacing gain is 1, when cruising, probing for RTT or refilling <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
+286    default_pacing_gain: f64,
+287    /// pacing gain when probing bandwidth down <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
+288    probe_bw_down_pacing_gain: f64,
+289    /// pacing gain when probing bandwidth up <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
+290    probe_bw_up_pacing_gain: f64,
+291    /// equivalent to BBR.StartupPacingGain: A constant specifying the minimum gain value for calculating the pacing rate that will allow
+292    /// the sending rate to double each round (4 * ln(2) ~= 2.77)
+293    /// BBRStartupPacingGain; used in Startup mode for BBR.pacing_gain. <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
+294    startup_pacing_gain: f64,
+295    /// equivalent to BBR.DrainPacingGain: A constant specifying the pacing gain value used in Drain mode,
+296    /// to attempt to drain the estimated queue at the bottleneck link in one round-trip or less.
+297    /// As noted in BBRDrainPacingGain, any value at or below 1 / BBRStartupCwndGain = 1 / 2 = 0.5 will theoretically achieve this.
+298    /// BBR uses the value 0.5, which has been shown to offer good performance when compared with other alternatives.
+299    /// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
+300    drain_pacing_gain: f64,
+301    /// equivalent to BBR.PacingMarginPercent: The static discount factor of 1% used to scale BBR.bw to produce C.pacing_rate.
+302    pacing_margin_percent: f64,
+303    /// equivalent to BBR.cwnd_gain: The dynamic gain factor used to scale the estimated BDP to produce a congestion window (C.cwnd).
+304    cwnd_gain: f64,
+305    /// equivalent to BBR.DefaultCwndGain: A constant specifying the minimum gain value that allows the sending rate to double each round (2) BBRStartupCwndGain.
+306    /// Used by default in most phases for BBR.cwnd_gain.
+307    default_cwnd_gain: f64,
+308    /// used to generate random numbers when deciding how long to wait before probing again
+309    /// using Pcg32 as it's a fast general purpose random number generator and fits our purpose here
+310    /// these numbers will not be security critical as they're only used to decide when to probe the connection next.
+311    probe_rng: Pcg32,
+312    /// cwnd gain used when probing up <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
+313    probe_bw_up_cwnd_gain: f64,
+314    /// cwnd gain used when probing RTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.1>
+315    probe_rtt_cwnd_gain: f64,
+316    /// equivalent to BBR.state: The current state of a BBR flow in the BBR state machine. <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-3.3>
+317    state: BbrState,
+318    /// equivalent to BBR.undo_state: The state of a BBR flow in the BBR state machine saved in case a loss episode is later declared spurious. <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-3.3>
+319    undo_state: BbrState,
+320    /// equivalent to BBR.round_count: Count of packet-timed round trips elapsed so far.
+321    round_count: u64,
+322    /// equivalent to BBR.round_start: A boolean that BBR sets to true once per packet-timed round trip, on ACKs that advance BBR.round_count.
+323    round_start: bool,
+324    /// equivalent to BBR.next_round_delivered: P.delivered value denoting the end of a packet-timed round trip.
+325    next_round_delivered: u64,
+326    /// equivalent to BBR.idle_restart: A boolean that is true if and only if a connection is restarting after being idle.
+327    idle_restart: bool,
+328    /// equivalent to BBR.MinPipeCwnd: The minimal C.cwnd value BBR targets, to allow pipelining with endpoints that follow an "ACK every other packet" delayed-ACK policy: 4 * C.SMSS.
+329    min_pipe_cwnd: u64,
+330    /// equivalent to BBR.max_bw: The windowed maximum recent bandwidth sample, obtained using the BBR delivery rate sampling algorithm in
+331    /// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-4.1>,
+332    /// measured during the current or previous bandwidth probing cycle (or during Startup, if the flow is still in that state). (Part of the long-term model.)
+333    max_bw: f64,
+334    /// equivalent to BBR.bw_shortterm: The short-term maximum sending bandwidth that the algorithm estimates is safe for matching the current network path delivery rate,
+335    /// based on any loss signals in the current bandwidth probing cycle. This is generally lower than max_bw. (Part of the short-term model.)
+336    bw_shortterm: f64,
+337    /// equivalent to BBR.undo_bw_shortterm: The short-term maximum sending bandwidth that the algorithm estimates is safe for matching the current network path delivery rate,
+338    /// based on any loss signals in the current bandwidth probing cycle. This is generally lower than max_bw. (Part of the short-term model.)
+339    /// saved state in case a loss episode is later declared spurious
+340    undo_bw_shortterm: f64,
+341    /// equivalent to BBR.bw: The maximum sending bandwidth that the algorithm estimates is appropriate for matching the current network path delivery rate,
+342    /// given all available signals in the model, at any time scale. It is the min() of max_bw and bw_shortterm.
+343    bw: f64,
+344    /// equivalent to BBR.min_rtt: The windowed minimum round-trip time sample measured over the last BBR.MinRTTFilterLen = 10 seconds.
+345    /// This attempts to estimate the two-way propagation delay of the network path when all connections sharing a bottleneck are using BBR,
+346    /// but also allows BBR to estimate the value required for a BBR.bdp estimate that allows full throughput if there are legacy loss-based Reno or CUBIC flows sharing the bottleneck.
+347    min_rtt: Duration,
+348    /// equivalent to BBR.bdp: The estimate of the network path's BDP (Bandwidth-Delay Product), computed as: BBR.bdp = BBR.bw * BBR.min_rtt.
+349    bdp: u64,
+350    /// equivalent to BBR.extra_acked: A volume of data that is the estimate of the recent degree of aggregation in the network path.
+351    extra_acked: u64,
+352    /// equivalent to BBR.offload_budget: The estimate of the minimum volume of data necessary to achieve full throughput when using sender
+353    /// (TSO/GSO) and receiver (LRO, GRO) host offload mechanisms.
+354    offload_budget: u64,
+355    /// equivalent to BBR.max_inflight: The estimate of C.inflight required to fully utilize the bottleneck bandwidth available to the flow,
+356    /// based on the BDP estimate (BBR.bdp), the aggregation estimate (BBR.extra_acked), the offload budget (BBR.offload_budget), and BBR.MinPipeCwnd.
+357    max_inflight: u64,
+358    /// equivalent to BBR.inflight_longterm: The long-term maximum inflight that the algorithm estimates will produce acceptable queue pressure,
+359    /// based on signals in the current or previous bandwidth probing cycle, as measured by loss. That is, if a flow is probing for bandwidth,
+360    /// and observes that sending a particular inflight causes a loss rate higher than the loss rate threshold,
+361    /// it sets inflight_longterm to that volume of data. (Part of the long-term model.)
+362    inflight_longterm: u64,
+363    /// equivalent to BBR.inflight_longterm: The long-term maximum inflight that the algorithm estimates will produce acceptable queue pressure,
+364    /// based on signals in the current or previous bandwidth probing cycle, as measured by loss. That is, if a flow is probing for bandwidth,
+365    /// and observes that sending a particular inflight causes a loss rate higher than the loss rate threshold,
+366    /// it sets inflight_longterm to that volume of data. (Part of the long-term model.)
+367    /// saved state in case a loss episode is later declared spurious
+368    undo_inflight_longterm: u64,
+369    /// equivalent to BBR.inflight_shortterm: Analogous to BBR.bw_shortterm,
+370    /// the short-term maximum inflight that the algorithm estimates is safe for matching the current network path delivery process,
+371    /// based on any loss signals in the current bandwidth probing cycle. This is generally lower than max_inflight or inflight_longterm. (Part of the short-term model.)
+372    inflight_shortterm: u64,
+373    /// equivalent to BBR.undo_inflight_shortterm: Analogous to BBR.bw_shortterm,
+374    /// the short-term maximum inflight that the algorithm estimates is safe for matching the current network path delivery process,
+375    /// based on any loss signals in the current bandwidth probing cycle. This is generally lower than max_inflight or inflight_longterm. (Part of the short-term model.)
+376    /// saved state in case a loss episode is later declared spurious
+377    undo_inflight_shortterm: u64,
+378    /// equivalent to BBR.bw_latest: a 1-round-trip max of delivered bandwidth (RS.delivery_rate).
+379    bw_latest: f64,
+380    /// equivalent to BBR.inflight_latest: a 1-round-trip max of delivered volume of data (RS.delivered).
+381    inflight_latest: u64,
+382    /// equivalent to BBR.max_bw_filter: A windowed max filter for RS.delivery_rate samples, for estimating BBR.max_bw.
+383    max_bw_filter: MaxFilter,
+384    /// equivalent to BBR.extra_acked_interval_start: The start of the time interval for estimating the excess amount of data acknowledged due to aggregation effects.
+385    extra_acked_interval_start: Option<Instant>,
+386    /// equivalent to BBR.extra_acked_delivered: The volume of data marked as delivered since BBR.extra_acked_interval_start.
+387    extra_acked_delivered: u64,
+388    /// equivalent to BBR.extra_acked_filter: A windowed max filter for tracking the degree of aggregation in the path.
+389    extra_acked_filter: MaxFilter,
+390    /// equivalent to BBR.full_bw_reached: A boolean that records whether BBR estimates that it has ever fully utilized its available bandwidth over the lifetime of the connection.
+391    full_bw_reached: bool,
+392    /// equivalent to BBR.full_bw_now: A boolean that records whether BBR estimates that it has fully utilized its available bandwidth since it most recetly started looking.
+393    full_bw_now: bool,
+394    /// equivalent to BBR.full_bw: A recent baseline BBR.max_bw to estimate if BBR has "filled the pipe" in Startup.
+395    full_bw: f64,
+396    /// equivalent to BBR.full_bw_count: The number of non-app-limited round trips without large increases in BBR.full_bw.
+397    full_bw_count: u64,
+398    /// equivalent to BBR.min_rtt_stamp: The wall clock time at which the current BBR.min_rtt sample was obtained.
+399    min_rtt_stamp: Option<Instant>,
+400    /// equivalent to BBR.ProbeRTTDuration: A constant specifying the minimum duration for which ProbeRTT state holds C.inflight to BBR.MinPipeCwnd or fewer packets: 200 ms.
+401    probe_rtt_duration: Duration,
+402    /// equivalent to BBR.ProbeRTTInterval: A constant specifying the minimum time interval between ProbeRTT states: 5 secs.
+403    probe_rtt_interval: Duration,
+404    /// equivalent to BBR.probe_rtt_min_delay: The minimum RTT sample recorded in the last ProbeRTTInterval.
+405    probe_rtt_min_delay: Duration,
+406    /// equivalent to BBR.probe_rtt_min_stamp: The wall clock time at which the current BBR.probe_rtt_min_delay sample was obtained.
+407    probe_rtt_min_stamp: Option<Instant>,
+408    /// equivalent to BBR.probe_rtt_expired: A boolean recording whether the BBR.probe_rtt_min_delay has expired and
+409    /// is due for a refresh with an application idle period or a transition into ProbeRTT state.
+410    probe_rtt_expired: bool,
+411    /// equivalent to C.delivered_time: The wall clock time when C.delivered was last updated. <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-4.1.1.2.1>
+412    delivered_time: Option<Instant>,
+413    /// equivalent to C.first_send_time: If packets are in flight, then this holds the send time of the packet that was most recently marked as delivered.
+414    /// Else, if the connection was recently idle, then this holds the send time of most recently sent packet.
+415    first_send_time: Option<Instant>,
+416    /// equivalent to C.app_limited: The index of the last transmitted packet marked as application-limited, or 0 if the connection is not currently application-limited.
+417    app_limited: u64,
+418    /// equivalent to C.lost: the number of bytes that have been lost during the lifetime of this connection
+419    lost: u64,
+420    /// equivalent to C.srtt: The smoothed RTT, an exponentially weighted moving average of the observed RTT of the connection.
+421    srtt: Duration,
+422    /// collection of packets in flight or just acknowledged / lost.
+423    packets: VecDeque<BbrPacket>,
+424    /// equivalent to RS: Per-ACK Rate Sample State <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.2>
+425    rs: Option<BbrRateSample>,
+426    /// equivalent to BBR.rounds_since_bw_probe: rounds since last bw probe state.
+427    rounds_since_bw_probe: u64,
+428    /// equivalent to BBR.bw_probe_wait: random wait time before entering probing state again
+429    bw_probe_wait: Duration,
+430    /// equivalent to BBR.bw_probe_up_rounds: number of rounds that have been executed in probe up state
+431    bw_probe_up_rounds: u32,
+432    /// equivalent to BBR.bw_probe_up_acks: volume of data in bytes that has been acknowledged during probe up state
+433    bw_probe_up_acks: u64,
+434    /// equivalent to BBR.probe_up_cnt: count of the number of times we've grown the cwnd during probe up state
+435    probe_up_cnt: u64,
+436    /// equivalent to BBR.cycle_stamp: timestamp when we start probing down state
+437    cycle_stamp: Option<Instant>,
+438    /// equivalent to BBR.ack_phase: ACK phase during probing states
+439    ack_phase: AckPhase,
+440    /// equivalent to BBR.bw_probe_samples: <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.2>
+441    bw_probe_samples: bool,
+442    /// equivalent to BBR.loss_round_delivered: C.delivered during the first loss of the round
+443    loss_round_delivered: u64,
+444    /// equivalent to BBR.loss_in_round: flag set to true when loss occurs during the round
+445    loss_in_round: bool,
+446    /// equivalent to BBR.probe_rtt_done_stamp: timestamp when probe RTT state is finished
+447    probe_rtt_done_stamp: Option<Instant>,
+448    /// equivalent to BBR.probe_rtt_round_done: set once per round when BBR.probe_rtt_done_stamp to check if we need to switch state
+449    probe_rtt_round_done: bool,
+450    /// equivalent to BBR.prior_cwnd: cwnd from last round
+451    prior_cwnd: u64,
+452    /// equivalent to BBR.loss_round_start: flag set to true at the very beginning of a round where loss occurred
+453    loss_round_start: bool,
+454    /// equivalent to BBR.drain_start_round: The value of round_count when Drain state started.
+455    drain_start_round: u64,
+456    /// Number of ack-eliciting packets the peer may receive before sending an immediate ACK,
+457    /// as requested via the QUIC ACK frequency extension. Used when computing `offload_budget`
+458    /// per <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.8.2>.
+459    ack_eliciting_threshold: u64,
+460    /// `max_ack_delay` we requested the peer to use via the QUIC ACK frequency extension.
+461    /// Used when computing `offload_budget` per
+462    /// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.8.2>.
+463    max_ack_delay: Duration,
+464}
+465
+466impl Bbr3 {
+467    fn new(config: Arc<Bbr3Config>, current_mtu: u16) -> Self {
+468        let probe_rng: Pcg32;
+469        if let Some(probe_seed) = config.probe_rng_seed {
+470            probe_rng = Pcg32::from_seed(probe_seed);
+471        } else {
+472            probe_rng = Pcg32::from_rng(&mut rand::rng());
+473        }
+474        let smss = min(
+475            max(MIN_MAX_DATAGRAM_SIZE, current_mtu) as u64,
+476            MAX_DATAGRAM_SIZE,
+477        );
+478        let initial_cwnd = config.initial_window;
+479        let startup_pacing_gain = config.startup_pacing_gain.unwrap_or(STARTUP_PACING_GAIN);
+480        let default_pacing_gain = config.default_pacing_gain.unwrap_or(DEFAULT_PACING_GAIN);
+481        let probe_bw_down_pacing_gain = config
+482            .probe_bw_down_pacing_gain
+483            .unwrap_or(PROBE_BW_DOWN_PACING_GAIN);
+484        let probe_bw_up_pacing_gain = config
+485            .probe_bw_up_pacing_gain
+486            .unwrap_or(PROBE_BW_UP_PACING_GAIN);
+487        let drain_pacing_gain = config.drain_pacing_gain.unwrap_or(DRAIN_PACING_GAIN);
+488        let pacing_margin_percent = config
+489            .pacing_margin_percent
+490            .unwrap_or(PACING_MARGIN_PERCENT);
+491        let default_cwnd_gain = config.default_cwnd_gain.unwrap_or(DEFAULT_CWND_GAIN);
+492        let probe_bw_up_cwnd_gain = config
+493            .probe_bw_up_cwnd_gain
+494            .unwrap_or(PROBE_BW_UP_CWND_GAIN);
+495        let probe_rtt_cwnd_gain = config.probe_rtt_cwnd_gain.unwrap_or(PROBE_RTT_CWND_GAIN);
+496        // the calculation for initial pacing rate described here <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.2-5>
+497        let nominal_bandwidth = initial_cwnd as f64 / 0.001;
+498        let pacing_rate = startup_pacing_gain * nominal_bandwidth;
+499        Self {
+500            smss,
+501            initial_cwnd,
+502            delivered: 0,
+503            inflight: 0,
+504            is_cwnd_limited: false,
+505            cycle_count: 0,
+506            cwnd: initial_cwnd,
+507            pacing_rate,
+508            send_quantum: 2 * smss, // we start high, but it will be adjusted in set_send_quantum <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.3>
+509            pacing_gain: startup_pacing_gain,
+510            startup_pacing_gain,
+511            default_pacing_gain,
+512            probe_bw_down_pacing_gain,
+513            probe_bw_up_pacing_gain,
+514            drain_pacing_gain,
+515            pacing_margin_percent,
+516            cwnd_gain: default_cwnd_gain,
+517            default_cwnd_gain,
+518            probe_rng,
+519            probe_bw_up_cwnd_gain,
+520            state: BbrState::Startup,
+521            undo_state: BbrState::Startup,
+522            round_count: 0,
+523            round_start: true,
+524            next_round_delivered: 0,
+525            idle_restart: false,
+526            min_pipe_cwnd: 4 * smss, // 4 * C.SMSS as defined in <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-2.7-4>
+527            max_bw: 0.0,
+528            bw_shortterm: f64::INFINITY,
+529            undo_bw_shortterm: f64::INFINITY,
+530            bw: 0.0,
+531            min_rtt: Duration::from_secs(u64::MAX),
+532            bdp: 0,
+533            extra_acked: 0,
+534            offload_budget: 0,
+535            max_inflight: 0,
+536            inflight_longterm: u64::MAX,
+537            undo_inflight_longterm: u64::MAX,
+538            inflight_shortterm: u64::MAX,
+539            undo_inflight_shortterm: u64::MAX,
+540            bw_latest: 0.0,
+541            inflight_latest: 0,
+542            max_bw_filter: MaxFilter::new(MAX_BW_FILTER_LEN as u64),
+543            extra_acked_interval_start: None,
+544            extra_acked_delivered: 0,
+545            extra_acked_filter: MaxFilter::new(EXTRA_ACKED_FILTER_LEN as u64),
+546            full_bw_reached: false,
+547            full_bw_now: false,
+548            full_bw: 0.0,
+549            full_bw_count: 0,
+550            min_rtt_stamp: None,
+551            probe_rtt_cwnd_gain,
+552            probe_rtt_duration: Duration::from_millis(PROBE_RTT_DURATION_MS),
+553            probe_rtt_interval: Duration::from_secs(PROBE_RTT_INTERVAL_SEC),
+554            probe_rtt_min_delay: Duration::ZERO,
+555            probe_rtt_min_stamp: None,
+556            probe_rtt_expired: false,
+557            delivered_time: None,
+558            first_send_time: None,
+559            app_limited: 0,
+560            lost: 0,
+561            srtt: Duration::ZERO,
+562            rs: None,
+563            packets: VecDeque::new(),
+564            rounds_since_bw_probe: 0,
+565            bw_probe_wait: Duration::ZERO,
+566            bw_probe_up_rounds: 0,
+567            bw_probe_up_acks: 0,
+568            probe_up_cnt: 0,
+569            cycle_stamp: None,
+570            ack_phase: AckPhase::ProbeStarting,
+571            bw_probe_samples: false,
+572            loss_round_delivered: 0,
+573            loss_in_round: false,
+574            probe_rtt_done_stamp: None,
+575            probe_rtt_round_done: false,
+576            prior_cwnd: 0,
+577            loss_round_start: false,
+578            drain_start_round: 0,
+579            // Conservative defaults that match RFC 9000 §13.2.2 behavior (ACK every other
+580            // ack-eliciting packet) and the default QUIC `max_ack_delay` of 25ms. Overridden
+581            // when the connection supplies peer ACK-frequency parameters.
+582            ack_eliciting_threshold: 1,
+583            max_ack_delay: Duration::from_millis(25),
+584        }
+585    }
+586
+587    /// equivalent to BBREnterStartup <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.1.1-3>
+588    fn enter_startup(&mut self) {
+589        self.state = BbrState::Startup;
+590        self.pacing_gain = self.startup_pacing_gain;
+591        self.cwnd_gain = self.default_cwnd_gain;
+592    }
+593
+594    /// equivalent to BBRResetFullBW <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.1.2-4>
+595    fn reset_full_bw(&mut self) {
+596        self.full_bw = 0.0;
+597        self.full_bw_count = 0;
+598        self.full_bw_now = false;
+599    }
+600
+601    /// equivalent to BBRNoteLoss <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.2-11>
+602    fn note_loss(&mut self) {
+603        if !self.loss_in_round {
+604            self.loss_round_delivered = self.delivered;
+605        }
+606        self.save_state_upon_loss();
+607        self.loss_in_round = true;
+608    }
+609
+610    /// equivalent to BBRSaveStateUponLoss <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.11.1>
+611    /// Save state in case a loss episode is later declared spurious
+612    fn save_state_upon_loss(&mut self) {
+613        self.undo_state = self.state;
+614        self.undo_bw_shortterm = self.bw_shortterm;
+615        self.undo_inflight_shortterm = self.inflight_shortterm;
+616        self.undo_inflight_longterm = self.inflight_longterm;
+617    }
+618
+619    /// equivalent to BBRInflightAtLoss <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.2-11>
+620    /// We check at what prefix of packet did losses exceed `loss_thresh`
+621    fn inflight_at_loss(&mut self, packet_size: u64) -> u64 {
+622        if let Some(rate_sample) = self.rs {
+623            let inflight_prev = rate_sample.tx_in_flight.saturating_sub(packet_size);
+624            let inflight_prev_threshold = LOSS_THRESH * inflight_prev as f64;
+625            let lost_prev = rate_sample.lost.saturating_sub(packet_size);
+626            let compared_loss = (inflight_prev_threshold.round() as u64) - lost_prev;
+627            let lost_prefix = compared_loss as f64 / (1.0 - LOSS_THRESH);
+628            let inflight_at_loss = inflight_prev + lost_prefix as u64;
+629            return inflight_at_loss;
+630        }
+631        0
+632    }
+633
+634    /// equivalent to BBRSaveCwnd <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.4-13>
+635    fn save_cwnd(&mut self) {
+636        if !self.loss_in_round && self.state != BbrState::ProbeRtt {
+637            self.prior_cwnd = self.cwnd;
+638        } else {
+639            self.prior_cwnd = max(self.prior_cwnd, self.cwnd);
+640        }
+641    }
+642
+643    /// equivalent to BBRRestoreCwnd <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.4-13>
+644    fn restore_cwnd(&mut self) {
+645        self.cwnd = max(self.cwnd, self.prior_cwnd);
+646    }
+647
+648    /// equivalent to BBRProbeRTTCwnd <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.5-1>
+649    fn probe_rtt_cwnd(&mut self) -> u64 {
+650        let mut probe_rtt_cwnd = self.bdp_multiple(self.bw, self.probe_rtt_cwnd_gain);
+651        probe_rtt_cwnd = max(probe_rtt_cwnd, self.min_pipe_cwnd);
+652        probe_rtt_cwnd
+653    }
+654
+655    /// equivalent to BBRBoundCwndForProbeRTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.5-1>
+656    fn bound_cwnd_for_probe_rtt(&mut self) {
+657        if self.state == BbrState::ProbeRtt {
+658            self.cwnd = min(self.cwnd, self.probe_rtt_cwnd());
+659        }
+660    }
+661
+662    /// equivalent to BBRTargetInflight <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.5.3-6>
+663    fn target_inflight(&self) -> u64 {
+664        min(self.bdp, self.cwnd)
+665    }
+666
+667    /// equivalent to BBRHandleInflightTooHigh <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.2-1>
+668    fn handle_inflight_too_high(&mut self, now: Instant) {
+669        self.bw_probe_samples = false;
+670        if let Some(rate_sample) = self.rs
+671            && !rate_sample.is_app_limited
+672        {
+673            self.inflight_longterm = max(
+674                rate_sample.tx_in_flight,
+675                (self.target_inflight() as f64 * BETA) as u64,
+676            );
+677        }
+678
+679        if self.state == BbrState::ProbeBw(ProbeBwSubstate::Up) {
+680            self.start_probe_bw_down(now);
+681        }
+682    }
+683
+684    /// equivalent to IsInflightTooHigh <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.2-1>
+685    fn is_inflight_too_high(&self) -> bool {
+686        if let Some(rate_sample) = self.rs {
+687            return rate_sample.lost as f64 > rate_sample.tx_in_flight as f64 * LOSS_THRESH;
+688        }
+689        false
+690    }
+691
+692    /// equivalent to BBRCheckStartupHighLoss <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.1.3>
+693    fn check_startup_high_loss(&mut self) {
+694        if self.full_bw_reached {
+695            return;
+696        }
+697
+698        if self.is_inflight_too_high() {
+699            let mut new_inflight_hi = self.bdp.max(self.inflight_latest);
+700            if let Some(rate_sample) = self.rs
+701                && new_inflight_hi < rate_sample.delivered
+702            {
+703                new_inflight_hi = rate_sample.delivered;
+704            }
+705            self.inflight_longterm = new_inflight_hi;
+706            self.full_bw_reached = true;
+707        }
+708    }
+709
+710    /// equivalent to BBREnterProbeBW <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6>
+711    fn enter_probe_bw(&mut self, now: Instant) {
+712        self.cwnd_gain = self.default_cwnd_gain;
+713        self.start_probe_bw_down(now);
+714    }
+715
+716    /// equivalent to BBRPickProbeWait <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.5.3-6>
+717    fn pick_probe_wait(&mut self) {
+718        // 0 or 1
+719        self.rounds_since_bw_probe = self.probe_rng.random_bool(0.5) as u64;
+720        self.bw_probe_wait = Duration::from_millis(
+721            MIN_PROBE_WAIT_MS + self.probe_rng.random_range(0..=MAX_ADDED_PROBE_WAIT_MS),
+722        );
+723    }
+724
+725    /// equivalent to BBRHasElapsedInPhase <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-8>
+726    fn has_elapsed_in_phase(&mut self, interval: Duration, now: Instant) -> bool {
+727        if let Some(cycle_stamp) = self.cycle_stamp {
+728            now > cycle_stamp.checked_add(interval).unwrap_or(cycle_stamp)
+729        } else {
+730            true
+731        }
+732    }
+733
+734    /// equivalent to BBRExitProbeRTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.4.4>
+735    fn exit_probe_rtt(&mut self, now: Instant) {
+736        self.reset_short_term_model();
+737        if self.full_bw_reached {
+738            self.start_probe_bw_down(now);
+739            self.start_probe_bw_cruise();
+740        } else {
+741            self.enter_startup();
+742        }
+743    }
+744
+745    /// equivalent to BBRCheckProbeRTTDone <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.4.3-4>
+746    fn check_probe_rtt_done(&mut self, now: Instant) {
+747        if let Some(probe_rtt_done_stamp) = self.probe_rtt_done_stamp
+748            && now > probe_rtt_done_stamp
+749        {
+750            self.probe_rtt_min_stamp = Some(now);
+751            self.restore_cwnd();
+752            self.exit_probe_rtt(now);
+753        }
+754    }
+755
+756    /// equivalent to BBRIsTimeToProbeBW <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.5.3-6>
+757    fn maybe_enter_probe_bw_refill(&mut self, now: Instant) -> bool {
+758        if self.has_elapsed_in_phase(self.bw_probe_wait, now)
+759            || self.is_reno_coexistence_probe_time()
+760        {
+761            self.start_probe_bw_refill();
+762            return true;
+763        }
+764        false
+765    }
+766
+767    /// equivalent to BBRIsTimeToGoDown <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-6>
+768    fn maybe_go_down(&mut self) -> bool {
+769        if self.is_cwnd_limited && self.cwnd >= self.inflight_longterm {
+770            self.reset_full_bw();
+771            if let Some(rate_sample) = self.rs {
+772                self.full_bw = rate_sample.delivery_rate;
+773            }
+774        } else if self.full_bw_now {
+775            return true;
+776        }
+777        false
+778    }
+779
+780    /// equivalent to BBRIsRenoCoexistenceProbeTime <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.5.3-6>
+781    fn is_reno_coexistence_probe_time(&self) -> bool {
+782        let reno_rounds = self.target_inflight();
+783        let rounds = min(reno_rounds, MAX_RENO_ROUNDS);
+784        self.rounds_since_bw_probe >= rounds
+785    }
+786
+787    /// equivalent to BBRBDPMultiple <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.2-2>
+788    fn bdp_multiple(&mut self, bw: f64, gain: f64) -> u64 {
+789        if self.min_rtt == Duration::from_secs(u64::MAX) {
+790            return self.initial_cwnd;
+791        }
+792        self.bdp = (bw * self.min_rtt.as_secs_f64()).round() as u64;
+793        (gain * self.bdp as f64) as u64
+794    }
+795
+796    /// equivalent to BBRUpdateOffloadBudget for QUIC per
+797    /// <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.8.2>.
+798    ///
+799    /// The delayed-ACK term accounts for the QUIC ACK frequency extension:
+800    /// `min(Ack-Eliciting Threshold, Requested Max Ack Delay * BBR.max_bw)`.
+801    fn update_offload_budget(&mut self) {
+802        let base = self.send_quantum;
+803
+804        // Ack-Eliciting Threshold is a packet count in the ACK_FREQUENCY frame; convert to
+805        // bytes using the current SMSS. A threshold of 0 requires an immediate ACK per packet,
+806        // so the delayed-ACK term contributes nothing in that case.
+807        let threshold_bytes = self.ack_eliciting_threshold.saturating_mul(self.smss);
+808        let delay_bytes = (self.max_ack_delay.as_secs_f64() * self.max_bw).round() as u64;
+809        let delayed_ack_term = min(threshold_bytes, delay_bytes);
+810
+811        self.offload_budget = base.saturating_add(delayed_ack_term);
+812    }
+813
+814    /// equivalent to BBRQuantizationBudget <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.2-2>
+815    fn quantization_budget(&mut self, inflight_cap: u64) -> u64 {
+816        self.update_offload_budget();
+817        let mut inflight_cap = max(inflight_cap, self.offload_budget);
+818        inflight_cap = max(inflight_cap, self.min_pipe_cwnd);
+819        if self.state == BbrState::ProbeBw(ProbeBwSubstate::Up) {
+820            inflight_cap += 2 * self.smss;
+821        }
+822        inflight_cap
+823    }
+824
+825    /// equivalent to BBRInflight <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.2-2>
+826    fn get_inflight(&mut self, gain: f64) -> u64 {
+827        let inflight_cap = self.bdp_multiple(self.max_bw, gain);
+828        self.quantization_budget(inflight_cap)
+829    }
+830
+831    /// equivalent to BBRUpdateMaxInflight <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.2-2>
+832    fn update_max_inflight(&mut self) {
+833        let mut inflight_cap = self.bdp_multiple(self.max_bw, self.cwnd_gain);
+834        inflight_cap += self.extra_acked;
+835        self.max_inflight = self.quantization_budget(inflight_cap);
+836    }
+837
+838    /// equivalent to BBRResetCongestionSignals <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
+839    fn reset_congestion_signals(&mut self) {
+840        self.loss_in_round = false;
+841        self.bw_latest = 0.0;
+842        self.inflight_latest = 0;
+843    }
+844
+845    /// equivalent to BBRStartRound <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.1-9>
+846    fn start_round(&mut self) {
+847        self.next_round_delivered = self.delivered;
+848        self.is_cwnd_limited = false;
+849    }
+850
+851    /// equivalent to BBRUpdateRound <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.1-9>
+852    fn update_round(&mut self, packet: BbrPacket) {
+853        if packet.delivered >= self.next_round_delivered {
+854            self.start_round();
+855            self.round_count += 1;
+856            self.rounds_since_bw_probe += 1;
+857            self.round_start = true;
+858        } else {
+859            self.round_start = false;
+860        }
+861    }
+862
+863    /// equivalent to BBRStartProbeBW_DOWN <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-4>
+864    fn start_probe_bw_down(&mut self, now: Instant) {
+865        self.reset_congestion_signals();
+866        self.probe_up_cnt = u64::MAX;
+867        self.pick_probe_wait();
+868        self.cycle_stamp = Some(now);
+869        self.ack_phase = AckPhase::ProbeStopping;
+870        self.start_round();
+871        self.pacing_gain = self.probe_bw_down_pacing_gain;
+872        self.cwnd_gain = self.default_cwnd_gain;
+873        self.state = BbrState::ProbeBw(ProbeBwSubstate::Down);
+874    }
+875
+876    /// equivalent to BBRInflightWithHeadroom <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-8>
+877    fn inflight_with_headroom(&self) -> u64 {
+878        if self.inflight_longterm == u64::MAX {
+879            return u64::MAX;
+880        }
+881        let total_headroom = max(self.smss, (HEADROOM * self.inflight_longterm as f64) as u64);
+882        if let Some(inflight_with_headroom) = self.inflight_longterm.checked_sub(total_headroom) {
+883            max(inflight_with_headroom, self.min_pipe_cwnd)
+884        } else {
+885            self.min_pipe_cwnd
+886        }
+887    }
+888
+889    /// equivalent to BBRSetPacingRateWithGain <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.2-7>
+890    fn set_pacing_rate_with_gain(&mut self, gain: f64) {
+891        let rate = gain * self.bw * (100.0 - self.pacing_margin_percent) / 100.0;
+892        if self.full_bw_reached || rate > self.pacing_rate {
+893            self.pacing_rate = rate;
+894        }
+895    }
+896
+897    /// equivalent to BBRRaiseInflightLongtermSlope <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-8>
+898    fn raise_inflight_long_term_slope(&mut self) {
+899        let growth_this_round = self
+900            .smss
+901            .checked_shl(self.bw_probe_up_rounds)
+902            .unwrap_or(u64::MAX);
+903        self.bw_probe_up_rounds = min(self.bw_probe_up_rounds + 1, MAX_LONG_TERM_PROBE_UP_ROUNDS);
+904        self.probe_up_cnt = max(self.cwnd / growth_this_round, 1);
+905    }
+906
+907    /// equivalent to BBRProbeInflightLongtermUpward <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-8>
+908    fn probe_inflight_long_term_upward(&mut self) {
+909        if !self.is_cwnd_limited || self.cwnd < self.inflight_longterm {
+910            return;
+911        }
+912        if let Some(rate_sample) = self.rs {
+913            self.bw_probe_up_acks += rate_sample.newly_acked;
+914        }
+915        if self.bw_probe_up_acks >= self.probe_up_cnt && self.probe_up_cnt > 0 {
+916            let delta = self.bw_probe_up_acks / self.probe_up_cnt;
+917            self.bw_probe_up_acks -= delta * self.probe_up_cnt;
+918            self.inflight_longterm += delta;
+919            if self.round_start {
+920                self.raise_inflight_long_term_slope();
+921            }
+922        }
+923    }
+924
+925    /// equivalent to BBRAdvanceMaxBwFilter <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.6>
+926    fn advance_max_bw_filter(&mut self) {
+927        self.cycle_count = self.cycle_count.saturating_add(1);
+928    }
+929
+930    /// equivalent to BBRAdaptLongTermModel <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-8>
+931    fn adapt_long_term_model(&mut self) {
+932        if self.ack_phase == AckPhase::ProbeStarting && self.round_start {
+933            self.ack_phase = AckPhase::ProbeFeedback;
+934        }
+935        if self.ack_phase == AckPhase::ProbeStopping
+936            && self.round_start
+937            && let BbrState::ProbeBw(_) = self.state
+938            && let Some(rate_sample) = self.rs
+939            && !rate_sample.is_app_limited
+940        {
+941            self.advance_max_bw_filter();
+942        }
+943        if !self.is_inflight_too_high() {
+944            if self.inflight_longterm == u64::MAX {
+945                return;
+946            }
+947            if let Some(rate_sample) = self.rs
+948                && rate_sample.tx_in_flight > self.inflight_longterm
+949            {
+950                self.inflight_longterm = rate_sample.tx_in_flight;
+951            }
+952            if self.state == BbrState::ProbeBw(ProbeBwSubstate::Up) {
+953                self.probe_inflight_long_term_upward();
+954            }
+955        }
+956    }
+957
+958    /// equivalent to BBRIsTimeToCruise <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-8>
+959    fn maybe_update_budget_and_time_to_cruise(&mut self) -> bool {
+960        if self.inflight > self.inflight_with_headroom() {
+961            return false;
+962        }
+963        if self.inflight > self.get_inflight(1.0) {
+964            return false;
+965        }
+966        true
+967    }
+968
+969    /// equivalent to BBRStartProbeBW_CRUISE <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.4.4-4>
+970    fn start_probe_bw_cruise(&mut self) {
+971        self.state = BbrState::ProbeBw(ProbeBwSubstate::Cruise);
+972        self.pacing_gain = self.default_pacing_gain;
+973        self.cwnd_gain = self.default_cwnd_gain;
+974    }
+975
+976    /// equivalent to BBRResetShortTermModel <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
+977    fn reset_short_term_model(&mut self) {
+978        self.bw_shortterm = f64::INFINITY;
+979        self.inflight_shortterm = u64::MAX;
+980    }
+981
+982    /// equivalent to BBRInitLowerBounds <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
+983    fn init_lower_bounds(&mut self) {
+984        if self.bw_shortterm == f64::INFINITY {
+985            self.bw_shortterm = self.max_bw;
+986        }
+987        if self.inflight_shortterm == u64::MAX {
+988            self.inflight_shortterm = self.cwnd;
+989        }
+990    }
+991
+992    /// equivalent to BBRLossLowerBounds <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
+993    fn loss_lower_bounds(&mut self) {
+994        // gives max of both f64
+995        self.bw_shortterm = [self.bw_latest, BETA * self.bw_shortterm]
+996            .iter()
+997            .copied()
+998            .fold(f64::NAN, f64::max);
+999        self.inflight_shortterm = max(
+1000            self.inflight_latest,
+1001            (BETA * self.inflight_shortterm as f64) as u64,
+1002        );
+1003    }
+1004
+1005    /// equivalent to BBRBoundBWForModel <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
+1006    fn bound_bw_for_model(&mut self) {
+1007        // gives min of both f64
+1008        self.bw = [self.max_bw, self.bw_shortterm]
+1009            .iter()
+1010            .copied()
+1011            .fold(f64::NAN, f64::min);
+1012    }
+1013
+1014    /// equivalent to BBRStartProbeBW_REFILL <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-4>
+1015    fn start_probe_bw_refill(&mut self) {
+1016        self.reset_short_term_model();
+1017        self.bw_probe_up_rounds = 0;
+1018        self.bw_probe_up_acks = 0;
+1019        self.ack_phase = AckPhase::Refilling;
+1020        self.start_round();
+1021        self.cwnd_gain = self.default_cwnd_gain;
+1022        self.pacing_gain = self.default_pacing_gain;
+1023        self.state = BbrState::ProbeBw(ProbeBwSubstate::Refill);
+1024    }
+1025
+1026    /// equivalent to BBRStartProbeBW_UP <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-4>
+1027    fn start_probe_bw_up(&mut self) {
+1028        self.ack_phase = AckPhase::ProbeStarting;
+1029        self.start_round();
+1030        self.reset_full_bw();
+1031        if let Some(rate_sample) = self.rs {
+1032            self.full_bw = rate_sample.delivery_rate;
+1033        }
+1034        self.state = BbrState::ProbeBw(ProbeBwSubstate::Up);
+1035        self.pacing_gain = self.probe_bw_up_pacing_gain;
+1036        self.cwnd_gain = self.probe_bw_up_cwnd_gain;
+1037        self.raise_inflight_long_term_slope();
+1038    }
+1039
+1040    /// equivalent to BBREnterProbeRTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.4.3-4>
+1041    fn enter_probe_rtt(&mut self) {
+1042        self.state = BbrState::ProbeRtt;
+1043        self.pacing_gain = self.default_pacing_gain;
+1044        self.cwnd_gain = self.probe_rtt_cwnd_gain;
+1045    }
+1046
+1047    /// equivalent to BBRHandleRestartFromIdle <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.4.1>
+1048    fn handle_restart_from_idle(&mut self, now: Instant) {
+1049        if self.inflight == 0 && self.app_limited != 0 {
+1050            self.idle_restart = true;
+1051            self.extra_acked_interval_start = Some(now);
+1052            match self.state {
+1053                BbrState::ProbeBw(_) => {
+1054                    self.set_pacing_rate_with_gain(1.0);
+1055                }
+1056                BbrState::ProbeRtt => {
+1057                    self.check_probe_rtt_done(now);
+1058                }
+1059                _ => {}
+1060            }
+1061        }
+1062    }
+1063
+1064    /// equivalent to BBRUpdateProbeBWCyclePhase <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.3.6-6>
+1065    fn update_probe_bw_cycle_phase(&mut self, now: Instant) {
+1066        if !self.full_bw_reached {
+1067            return;
+1068        }
+1069        self.adapt_long_term_model();
+1070        let state = self.state;
+1071        match state {
+1072            BbrState::ProbeBw(ProbeBwSubstate::Down) => {
+1073                if self.maybe_enter_probe_bw_refill(now) {
+1074                    return;
+1075                }
+1076                if self.maybe_update_budget_and_time_to_cruise() {
+1077                    self.start_probe_bw_cruise();
+1078                }
+1079            }
+1080            BbrState::ProbeBw(ProbeBwSubstate::Cruise) if self.maybe_enter_probe_bw_refill(now) => {
+1081            }
+1082            BbrState::ProbeBw(ProbeBwSubstate::Refill) if self.round_start => {
+1083                self.bw_probe_samples = true;
+1084                self.start_probe_bw_up();
+1085            }
+1086            BbrState::ProbeBw(ProbeBwSubstate::Up) if self.maybe_go_down() => {
+1087                self.start_probe_bw_down(now);
+1088            }
+1089            _ => {}
+1090        }
+1091    }
+1092
+1093    /// equivalent to BBRUpdateLatestDeliverySignals <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
+1094    fn update_latest_delivery_signals(&mut self) {
+1095        self.loss_round_start = false;
+1096        if let Some(rate_sample) = self.rs {
+1097            self.bw_latest = [self.bw_latest, rate_sample.delivery_rate]
+1098                .iter()
+1099                .copied()
+1100                .fold(f64::NAN, f64::max);
+1101            self.inflight_latest = max(self.inflight_latest, rate_sample.delivered);
+1102
+1103            if rate_sample.prior_delivered >= self.loss_round_delivered {
+1104                self.loss_round_delivered = self.delivered;
+1105                self.loss_round_start = true;
+1106            }
+1107        }
+1108    }
+1109
+1110    /// equivalent to BBRAdaptLowerBoundsFromCongestion <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
+1111    fn adapt_lower_bounds_from_congestion(&mut self) {
+1112        match self.state {
+1113            BbrState::ProbeBw(ProbeBwSubstate::Refill)
+1114            | BbrState::ProbeBw(ProbeBwSubstate::Up)
+1115            | BbrState::Startup => {}
+1116            _ => {
+1117                if self.loss_in_round {
+1118                    self.init_lower_bounds();
+1119                    self.loss_lower_bounds();
+1120                }
+1121            }
+1122        }
+1123    }
+1124
+1125    /// equivalent to BBRUpdateMaxBw <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.5>
+1126    fn update_max_bw(&mut self, p: BbrPacket) {
+1127        self.update_round(p);
+1128        if let Some(rate_sample) = self.rs
+1129            && rate_sample.delivery_rate > 0.0
+1130            && (rate_sample.delivery_rate >= self.max_bw || !rate_sample.is_app_limited)
+1131        {
+1132            self.max_bw_filter
+1133                .update_max(self.cycle_count, rate_sample.delivery_rate.round() as u64);
+1134
+1135            self.max_bw = self.max_bw_filter.get_max() as f64;
+1136        }
+1137    }
+1138
+1139    /// equivalent to BBRUpdateCongestionSignals <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
+1140    fn update_congestion_signals(&mut self, p: BbrPacket) {
+1141        self.update_max_bw(p);
+1142        if !self.loss_round_start {
+1143            return;
+1144        }
+1145        self.adapt_lower_bounds_from_congestion();
+1146        self.loss_in_round = false;
+1147    }
+1148
+1149    /// equivalent to BBRUpdateACKAggregation <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.9>
+1150    fn update_ack_aggregation(&mut self, now: Instant) {
+1151        let interval;
+1152        if let Some(extra_acked_interval_start) = self.extra_acked_interval_start {
+1153            interval = now - extra_acked_interval_start;
+1154        } else {
+1155            interval = Duration::from_secs(0);
+1156        }
+1157        let mut expected_delivered = (self.bw * interval.as_secs_f64()) as u64;
+1158        if self.extra_acked_delivered <= expected_delivered {
+1159            self.extra_acked_delivered = 0;
+1160            self.extra_acked_interval_start = Some(now);
+1161            expected_delivered = 0;
+1162        }
+1163        if let Some(rate_sample) = self.rs {
+1164            self.extra_acked_delivered += rate_sample.newly_acked;
+1165        }
+1166
+1167        let mut extra = self
+1168            .extra_acked_delivered
+1169            .saturating_sub(expected_delivered);
+1170        extra = min(extra, self.cwnd);
+1171        if self.full_bw_reached {
+1172            self.extra_acked_filter.update_max(self.round_count, extra);
+1173            self.extra_acked = self.extra_acked_filter.get_max();
+1174        } else {
+1175            self.extra_acked = extra; // In startup, just remember 1 round
+1176        }
+1177    }
+1178
+1179    /// equivalent to BBRCheckFullBWReached <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.1.2-6>
+1180    fn check_full_bw_reached(&mut self) {
+1181        if self.full_bw_now || !self.round_start {
+1182            return;
+1183        }
+1184        if let Some(rate_sample) = self.rs {
+1185            if rate_sample.is_app_limited {
+1186                return;
+1187            }
+1188            if rate_sample.delivery_rate >= self.full_bw * FULL_BW_GROWTH {
+1189                self.reset_full_bw();
+1190                self.full_bw = rate_sample.delivery_rate;
+1191                return;
+1192            }
+1193        }
+1194        self.full_bw_count += 1;
+1195        self.full_bw_now = self.full_bw_count >= MAX_FULL_BW_COUNT;
+1196        if self.full_bw_now {
+1197            self.full_bw_reached = true;
+1198        }
+1199    }
+1200
+1201    /// equivalent to BBREnterDrain <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.2>
+1202    fn enter_drain(&mut self) {
+1203        self.state = BbrState::Drain;
+1204        self.pacing_gain = self.drain_pacing_gain;
+1205        self.cwnd_gain = self.default_cwnd_gain;
+1206        self.drain_start_round = self.round_count;
+1207    }
+1208
+1209    /// equivalent to BBRCheckStartupDone <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.1.1-6>
+1210    fn check_startup_done(&mut self) {
+1211        self.check_startup_high_loss();
+1212        if self.state == BbrState::Startup && self.full_bw_reached {
+1213            self.enter_drain();
+1214        }
+1215    }
+1216
+1217    /// equivalent to BBRCheckDrainDone <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.2-3>
+1218    fn check_drain_done(&mut self, now: Instant) {
+1219        if self.state == BbrState::Drain
+1220            && (self.inflight <= self.get_inflight(1.0)
+1221                || self.round_count > self.drain_start_round + 3)
+1222        {
+1223            self.enter_probe_bw(now);
+1224        }
+1225    }
+1226
+1227    /// equivalent to BBRUpdateMinRTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.4.3>
+1228    fn update_min_rtt(&mut self, now: Instant) {
+1229        if let Some(probe_rtt_min_stamp) = self.probe_rtt_min_stamp {
+1230            self.probe_rtt_expired = now
+1231                > probe_rtt_min_stamp
+1232                    .checked_add(self.probe_rtt_interval)
+1233                    .unwrap_or(probe_rtt_min_stamp);
+1234        } else {
+1235            self.probe_rtt_expired = true;
+1236        }
+1237        if let Some(rate_sample) = self.rs
+1238            && rate_sample.rtt >= Duration::from_secs(0)
+1239            && (rate_sample.rtt < self.probe_rtt_min_delay || self.probe_rtt_expired)
+1240        {
+1241            self.probe_rtt_min_delay = rate_sample.rtt;
+1242            self.probe_rtt_min_stamp = Some(now);
+1243        }
+1244
+1245        let min_rtt_expired;
+1246        if let Some(min_rtt_stamp) = self.min_rtt_stamp {
+1247            min_rtt_expired = now
+1248                > min_rtt_stamp
+1249                    .checked_add(Duration::from_secs(MIN_RTT_FILTER_LEN))
+1250                    .unwrap_or(min_rtt_stamp);
+1251        } else {
+1252            min_rtt_expired = true;
+1253        }
+1254        if self.probe_rtt_min_delay < self.min_rtt || min_rtt_expired {
+1255            self.min_rtt = self.probe_rtt_min_delay;
+1256            self.min_rtt_stamp = self.probe_rtt_min_stamp;
+1257        }
+1258    }
+1259
+1260    /// equivalent to BBRHandleProbeRTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.4.3-4>
+1261    fn handle_probe_rtt(&mut self, now: Instant) {
+1262        if self.probe_rtt_done_stamp.is_none() && self.inflight <= self.probe_rtt_cwnd() {
+1263            self.probe_rtt_done_stamp =
+1264                Some(now.checked_add(self.probe_rtt_duration).unwrap_or(now));
+1265            self.probe_rtt_round_done = false;
+1266            self.start_round();
+1267        } else if self.probe_rtt_done_stamp.is_some() {
+1268            if self.round_start {
+1269                self.probe_rtt_round_done = true;
+1270            }
+1271            if self.probe_rtt_round_done {
+1272                self.check_probe_rtt_done(now);
+1273            }
+1274        }
+1275    }
+1276
+1277    /// equivalent to BBRCheckProbeRTT <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.3.4.3-4>
+1278    fn check_probe_rtt(&mut self, now: Instant) {
+1279        match self.state {
+1280            BbrState::ProbeRtt => {
+1281                self.handle_probe_rtt(now);
+1282            }
+1283            _ => {
+1284                if self.probe_rtt_expired && !self.idle_restart {
+1285                    self.enter_probe_rtt();
+1286                    self.save_cwnd();
+1287                    self.probe_rtt_done_stamp = None;
+1288                    self.ack_phase = AckPhase::ProbeStopping;
+1289                    self.start_round();
+1290                }
+1291            }
+1292        }
+1293        if let Some(rate_sample) = self.rs
+1294            && rate_sample.delivered > 0
+1295        {
+1296            self.idle_restart = false;
+1297        }
+1298    }
+1299
+1300    /// equivalent to BBRAdvanceLatestDeliverySignals <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.3-8>
+1301    fn advance_latest_delivery_signals(&mut self) {
+1302        if self.loss_round_start
+1303            && let Some(rate_sample) = self.rs
+1304        {
+1305            self.bw_latest = rate_sample.delivery_rate;
+1306            self.inflight_latest = rate_sample.delivered;
+1307        }
+1308    }
+1309
+1310    /// equivalent to BBRUpdateModelAndState <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.2.3>
+1311    fn update_model_and_state(&mut self, p: BbrPacket, now: Instant) {
+1312        self.update_latest_delivery_signals();
+1313        self.reset_congestion_signals();
+1314        self.update_congestion_signals(p);
+1315        self.update_ack_aggregation(now);
+1316        self.check_full_bw_reached();
+1317        self.check_startup_done();
+1318        self.check_drain_done(now);
+1319        self.update_probe_bw_cycle_phase(now);
+1320        self.update_min_rtt(now);
+1321        self.check_probe_rtt(now);
+1322        self.advance_latest_delivery_signals();
+1323        self.bound_bw_for_model();
+1324    }
+1325
+1326    /// equivalent to BBRSetPacingRate <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.2-7>
+1327    fn set_pacing_rate(&mut self) {
+1328        self.set_pacing_rate_with_gain(self.pacing_gain);
+1329    }
+1330
+1331    /// equivalent to BBRSetSendQuantum <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.3>
+1332    /// this version is based on a version of bbr2 from quiche
+1333    fn set_send_quantum(&mut self) {
+1334        self.send_quantum = match self.pacing_rate {
+1335            rate if rate < PACING_RATE_1_2MBPS => MAX_DATAGRAM_SIZE,
+1336            rate if rate < PACING_RATE_24MBPS => 2 * MAX_DATAGRAM_SIZE,
+1337            _ => min((self.pacing_rate / 1000.0) as u64, HIGH_PACE_MAX_QUANTUM),
+1338        };
+1339    }
+1340
+1341    /// equivalent to BBRBoundCwndForModel <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.7>
+1342    fn bound_cwnd_for_model(&mut self) {
+1343        let mut cap = u64::MAX;
+1344        match self.state {
+1345            BbrState::ProbeRtt => {
+1346                cap = self.inflight_with_headroom();
+1347            }
+1348            BbrState::ProbeBw(ProbeBwSubstate::Cruise) => {
+1349                cap = self.inflight_with_headroom();
+1350            }
+1351            BbrState::ProbeBw(_) => {
+1352                cap = self.inflight_longterm;
+1353            }
+1354            _ => {}
+1355        }
+1356        cap = min(cap, self.inflight_shortterm);
+1357        cap = max(cap, self.min_pipe_cwnd);
+1358        self.cwnd = min(self.cwnd, cap);
+1359    }
+1360
+1361    /// equivalent to BBRSetCwnd <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.6.4.6>
+1362    fn set_cwnd(&mut self) {
+1363        self.update_max_inflight();
+1364        if self.full_bw_reached {
+1365            if let Some(rate_sample) = self.rs {
+1366                self.cwnd = min(self.cwnd + rate_sample.newly_acked, self.max_inflight);
+1367            } else {
+1368                self.cwnd = min(self.cwnd, self.max_inflight);
+1369            }
+1370        } else if (self.cwnd < self.max_inflight || self.delivered < self.initial_cwnd)
+1371            && let Some(rate_sample) = self.rs
+1372        {
+1373            self.cwnd += rate_sample.newly_acked;
+1374        }
+1375        self.cwnd = max(self.cwnd, self.min_pipe_cwnd);
+1376        self.bound_cwnd_for_probe_rtt();
+1377        self.bound_cwnd_for_model();
+1378    }
+1379
+1380    /// equivalent to BBRUpdateControlParameters <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.2.3>
+1381    fn update_control_parameters(&mut self) {
+1382        self.set_pacing_rate();
+1383        self.set_send_quantum();
+1384        self.set_cwnd();
+1385    }
+1386
+1387    /// equivalent to IsNewestPacket <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-4.1.2.3-3>
+1388    fn is_newest_packet(&self, send_time: Instant, end_seq: u64) -> bool {
+1389        if let Some(first_send_time) = self.first_send_time {
+1390            if send_time > first_send_time {
+1391                return true;
+1392            }
+1393            if let Some(rate_sample) = self.rs
+1394                && end_seq > rate_sample.last_end_seq
+1395            {
+1396                return true;
+1397            }
+1398        }
+1399        false
+1400    }
+1401
+1402    /// equivalent to BBRHandleLostPacket <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.10.2-11>
+1403    fn process_lost_packet(&mut self, lost_bytes: u64, packet_index: usize, now: Instant) {
+1404        let p = self.packets[packet_index];
+1405        self.note_loss();
+1406        if !self.bw_probe_samples {
+1407            self.packets.remove(packet_index);
+1408            return;
+1409        }
+1410        if let Some(mut rate_sample) = self.rs {
+1411            rate_sample.newly_lost += lost_bytes;
+1412            rate_sample.tx_in_flight = p.tx_in_flight;
+1413            rate_sample.lost = self.lost.saturating_sub(p.lost);
+1414            rate_sample.is_app_limited = p.is_app_limited;
+1415            if self.is_inflight_too_high() {
+1416                rate_sample.tx_in_flight = self.inflight_at_loss(p.size as u64);
+1417                self.handle_inflight_too_high(now);
+1418            }
+1419            self.rs = Some(rate_sample);
+1420        }
+1421        self.packets.remove(packet_index);
+1422    }
+1423}
+1424impl Controller for Bbr3 {
+1425    fn on_packet_sent(&mut self, now: Instant, bytes: u16, pn: u64) {
+1426        if self.inflight == 0 {
+1427            self.first_send_time = Some(now);
+1428            self.delivered_time = Some(now);
+1429        }
+1430        let added_bytes = bytes as u64;
+1431        self.inflight += added_bytes;
+1432        self.packets.push_back(BbrPacket {
+1433            delivered: self.delivered,
+1434            delivered_time: self.delivered_time.unwrap_or(now),
+1435            first_send_time: self.first_send_time.unwrap_or(now),
+1436            send_time: now,
+1437            is_app_limited: self.app_limited != 0,
+1438            tx_in_flight: self.inflight,
+1439            packet_number: pn,
+1440            size: bytes,
+1441            lost: self.lost,
+1442            acknowledged: false,
+1443            stale: false,
+1444            round_count: self.round_count,
+1445        });
+1446        self.handle_restart_from_idle(now);
+1447    }
+1448
+1449    fn on_ack(
+1450        &mut self,
+1451        now: Instant,
+1452        sent: Instant,
+1453        bytes: u64,
+1454        pn: u64,
+1455        _app_limited: bool,
+1456        rtt: &RttEstimator,
+1457    ) {
+1458        if let Some(mut rate_sample) = self.rs {
+1459            rate_sample.newly_acked += bytes;
+1460            self.rs = Some(rate_sample);
+1461            self.delivered += bytes;
+1462            self.delivered_time = Some(now);
+1463        }
+1464        let p_index_result = self.packets.binary_search_by_key(&pn, |p| p.packet_number);
+1465        let is_newest_packet = self.is_newest_packet(sent, pn);
+1466        if let Ok(p_index) = p_index_result
+1467            && let Some(p) = self.packets.get_mut(p_index)
+1468        {
+1469            p.acknowledged = true;
+1470            if let Some(mut rate_sample) = self.rs {
+1471                rate_sample.rtt = now - p.send_time;
+1472                if is_newest_packet {
+1473                    self.srtt = rtt.get();
+1474                    rate_sample.prior_delivered = p.delivered;
+1475                    rate_sample.prior_time = p.delivered_time;
+1476                    rate_sample.is_app_limited = p.is_app_limited;
+1477                    rate_sample.tx_in_flight = p.tx_in_flight;
+1478                    rate_sample.send_elapsed = p.send_time - p.first_send_time;
+1479                    rate_sample.ack_elapsed = self.delivered_time.unwrap_or(now) - p.delivered_time;
+1480                    rate_sample.last_end_seq = pn;
+1481                    self.first_send_time = Some(p.send_time);
+1482                    rate_sample.last_packet = *p;
+1483                    self.rs = Some(rate_sample);
+1484                    self.update_model_and_state(rate_sample.last_packet, now);
+1485                    self.update_control_parameters();
+1486                }
+1487            } else {
+1488                let rate_sample = BbrRateSample {
+1489                    rtt: rtt.get(),
+1490                    prior_time: p.delivered_time,
+1491                    interval: Duration::ZERO,
+1492                    delivery_rate: 0.0,
+1493                    is_app_limited: p.is_app_limited,
+1494                    delivered: 0,
+1495                    prior_delivered: p.delivered,
+1496                    tx_in_flight: p.tx_in_flight,
+1497                    send_elapsed: p.send_time - p.first_send_time,
+1498                    ack_elapsed: self.delivered_time.unwrap_or(now) - p.delivered_time,
+1499                    newly_acked: bytes,
+1500                    newly_lost: 0,
+1501                    lost: 0,
+1502                    last_end_seq: pn,
+1503                    last_packet: *p,
+1504                };
+1505                self.rs = Some(rate_sample);
+1506                self.first_send_time = Some(p.send_time);
+1507                self.srtt = rate_sample.rtt;
+1508                self.update_model_and_state(rate_sample.last_packet, now);
+1509                self.update_control_parameters();
+1510            }
+1511        }
+1512    }
+1513
+1514    fn on_end_acks(
+1515        &mut self,
+1516        _now: Instant,
+1517        in_flight: u64,
+1518        app_limited: bool,
+1519        largest_packet_num_acked: Option<u64>,
+1520    ) {
+1521        self.inflight = in_flight;
+1522        if let Some(largest_packet_num) = largest_packet_num_acked {
+1523            if self.app_limited != 0 && largest_packet_num > self.app_limited {
+1524                self.app_limited = 0;
+1525            } else if app_limited {
+1526                self.app_limited = self.app_limited.max(largest_packet_num);
+1527            }
+1528            self.packets.retain(|&p| !p.stale);
+1529            for p in self.packets.iter_mut() {
+1530                if p.acknowledged || self.round_count - p.round_count > ROUND_COUNT_WINDOW {
+1531                    p.stale = true;
+1532                }
+1533            }
+1534            if let Some(mut rate_sample) = self.rs {
+1535                if rate_sample.prior_delivered == 0 {
+1536                    return;
+1537                }
+1538                rate_sample.interval = max(rate_sample.send_elapsed, rate_sample.ack_elapsed);
+1539                rate_sample.delivered = self.delivered.saturating_sub(rate_sample.prior_delivered);
+1540                // ignore this condition on an initially high min rtt as per <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-4.1.2.3-5>
+1541                if rate_sample.interval < self.min_rtt
+1542                    && self.min_rtt != Duration::from_secs(u64::MAX)
+1543                {
+1544                    return;
+1545                }
+1546                if rate_sample.interval != Duration::ZERO {
+1547                    rate_sample.delivery_rate =
+1548                        rate_sample.delivered as f64 / rate_sample.interval.as_secs_f64();
+1549                }
+1550                if rate_sample.delivered >= self.cwnd {
+1551                    self.is_cwnd_limited = true;
+1552                }
+1553                self.rs = Some(rate_sample);
+1554                rate_sample.newly_acked = 0;
+1555                rate_sample.lost = 0;
+1556                rate_sample.newly_lost = 0;
+1557                self.rs = Some(rate_sample);
+1558            }
+1559        }
+1560    }
+1561
+1562    fn on_congestion_event(
+1563        &mut self,
+1564        now: Instant,
+1565        _sent: Instant,
+1566        is_persistent_congestion: bool,
+1567        is_ecn: bool,
+1568        lost_bytes: u64,
+1569        largest_lost_pn: u64,
+1570    ) {
+1571        // only process ecn here, regular packet loss is detected per packet in on_packet_lost.
+1572        if is_ecn {
+1573            self.lost += lost_bytes;
+1574            let p_index_result = self
+1575                .packets
+1576                .binary_search_by_key(&largest_lost_pn, |p| p.packet_number);
+1577            if let Ok(p_index) = p_index_result {
+1578                self.process_lost_packet(lost_bytes, p_index, now);
+1579            }
+1580            if is_persistent_congestion {
+1581                self.cwnd = self.min_pipe_cwnd;
+1582            }
+1583        }
+1584    }
+1585
+1586    fn on_packet_lost(&mut self, lost_bytes: u16, pn: u64, now: Instant) {
+1587        let lost_bytes_64 = lost_bytes as u64;
+1588        self.lost += lost_bytes_64;
+1589        let p_index_result = self.packets.binary_search_by_key(&pn, |p| p.packet_number);
+1590        if let Ok(p_index) = p_index_result {
+1591            self.process_lost_packet(lost_bytes_64, p_index, now);
+1592        }
+1593    }
+1594
+1595    /// equivalent to BBRHandleSpuriousLossDetection: <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-05.html#section-5.5.11.2>
+1596    fn on_spurious_congestion_event(&mut self) {
+1597        self.loss_in_round = false;
+1598        self.reset_full_bw();
+1599        self.bw_shortterm = [self.bw_shortterm, self.undo_bw_shortterm]
+1600            .iter()
+1601            .copied()
+1602            .fold(f64::NAN, f64::max);
+1603        self.inflight_shortterm = max(self.inflight_shortterm, self.undo_inflight_shortterm);
+1604        self.inflight_longterm = max(self.inflight_longterm, self.undo_inflight_longterm);
+1605        if self.state != BbrState::ProbeRtt && self.state != self.undo_state {
+1606            if self.undo_state == BbrState::Startup {
+1607                self.enter_startup();
+1608            } else if self.undo_state == BbrState::ProbeBw(ProbeBwSubstate::Up) {
+1609                self.start_probe_bw_up();
+1610            }
+1611        }
+1612    }
+1613
+1614    fn on_mtu_update(&mut self, new_mtu: u16) {
+1615        self.smss = min(
+1616            max(MIN_MAX_DATAGRAM_SIZE, new_mtu) as u64,
+1617            MAX_DATAGRAM_SIZE,
+1618        );
+1619        self.set_cwnd();
+1620    }
+1621
+1622    fn on_ack_frequency_update(
+1623        &mut self,
+1624        ack_eliciting_threshold: u64,
+1625        requested_max_ack_delay: Duration,
+1626    ) {
+1627        self.ack_eliciting_threshold = ack_eliciting_threshold;
+1628        self.max_ack_delay = requested_max_ack_delay;
+1629    }
+1630
+1631    fn window(&self) -> u64 {
+1632        self.cwnd
+1633    }
+1634
+1635    fn metrics(&self) -> ControllerMetrics {
+1636        ControllerMetrics {
+1637            congestion_window: self.window(),
+1638            ssthresh: None,
+1639            pacing_rate: Some(self.pacing_rate.round() as u64),
+1640            send_quantum: Some(self.send_quantum),
+1641        }
+1642    }
+1643
+1644    fn clone_box(&self) -> Box<dyn Controller> {
+1645        Box::new(self.clone())
+1646    }
+1647
+1648    fn initial_window(&self) -> u64 {
+1649        self.initial_cwnd
+1650    }
+1651
+1652    fn into_any(self: Box<Self>) -> Box<dyn Any> {
+1653        self
+1654    }
+1655}
+1656
+1657/// Configuration for the `Bbr3` congestion controller
+1658///
+1659/// Different pacing_gains can be set to modify the multiplier used to
+1660/// increase the sending rates.
+1661/// Different cwnd_gains can be set to modify the multiplier used to increase
+1662/// the congestion windows.
+1663/// All of these parameters are specific to different states of the algorithm: see `BbrState`
+1664/// `pacing_margin_percent` is used to set a margin when calculating the `pacing_rate` in order
+1665/// to not send at 100% capacity when calculating pacing.
+1666#[derive(Debug, Clone)]
+1667pub struct Bbr3Config {
+1668    initial_window: u64,
+1669    probe_rng_seed: Option<[u8; 16]>,
+1670    startup_pacing_gain: Option<f64>,
+1671    default_pacing_gain: Option<f64>,
+1672    probe_bw_down_pacing_gain: Option<f64>,
+1673    probe_bw_up_pacing_gain: Option<f64>,
+1674    probe_bw_up_cwnd_gain: Option<f64>,
+1675    probe_rtt_cwnd_gain: Option<f64>,
+1676    drain_pacing_gain: Option<f64>,
+1677    pacing_margin_percent: Option<f64>,
+1678    default_cwnd_gain: Option<f64>,
+1679}
+1680
+1681impl Bbr3Config {
+1682    /// Default limit on the amount of outstanding data in bytes.
+1683    ///
+1684    /// Recommended value: `min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))`
+1685    pub fn initial_window(&mut self, value: u64) -> &mut Self {
+1686        self.initial_window = value;
+1687        self
+1688    }
+1689}
+1690
+1691impl Default for Bbr3Config {
+1692    fn default() -> Self {
+1693        Self {
+1694            initial_window: 14720.clamp(2 * MAX_DATAGRAM_SIZE, 10 * MAX_DATAGRAM_SIZE),
+1695            probe_rng_seed: None,
+1696            startup_pacing_gain: None,
+1697            default_pacing_gain: None,
+1698            probe_bw_down_pacing_gain: None,
+1699            probe_bw_up_pacing_gain: None,
+1700            probe_bw_up_cwnd_gain: None,
+1701            probe_rtt_cwnd_gain: None,
+1702            drain_pacing_gain: None,
+1703            pacing_margin_percent: None,
+1704            default_cwnd_gain: None,
+1705        }
+1706    }
+1707}
+1708
+1709impl ControllerFactory for Bbr3Config {
+1710    fn build(self: Arc<Self>, _now: Instant, current_mtu: u16) -> Box<dyn Controller> {
+1711        Box::new(Bbr3::new(self, current_mtu))
+1712    }
+1713}
+1714
+1715#[cfg(test)]
+1716mod test {
+1717    use super::*;
+1718
+1719    #[test]
+1720    fn test_probe_rng() {
+1721        let seed: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
+1722        let config = Bbr3Config {
+1723            initial_window: 14720.clamp(2 * MAX_DATAGRAM_SIZE, 10 * MAX_DATAGRAM_SIZE),
+1724            probe_rng_seed: Some(seed),
+1725            startup_pacing_gain: None,
+1726            default_pacing_gain: None,
+1727            probe_bw_down_pacing_gain: None,
+1728            probe_bw_up_pacing_gain: None,
+1729            probe_bw_up_cwnd_gain: None,
+1730            probe_rtt_cwnd_gain: None,
+1731            drain_pacing_gain: None,
+1732            pacing_margin_percent: None,
+1733            default_cwnd_gain: None,
+1734        };
+1735        let mut bbr3 = Bbr3::new(Arc::new(config), 2500);
+1736        bbr3.pick_probe_wait();
+1737        assert_eq!(bbr3.rounds_since_bw_probe, 1);
+1738        assert_eq!(bbr3.bw_probe_wait, Duration::from_millis(2652));
+1739        bbr3.pick_probe_wait();
+1740        assert_eq!(bbr3.rounds_since_bw_probe, 1);
+1741        assert_eq!(bbr3.bw_probe_wait, Duration::from_millis(2570));
+1742    }
+1743}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/congestion/cubic.rs.html b/pr/672/docs/src/noq_proto/congestion/cubic.rs.html new file mode 100644 index 000000000..5a1e6f3b5 --- /dev/null +++ b/pr/672/docs/src/noq_proto/congestion/cubic.rs.html @@ -0,0 +1,302 @@ +cubic.rs - source

noq_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        _pn: u64,
+111        app_limited: bool,
+112        rtt: &RttEstimator,
+113    ) {
+114        if app_limited
+115            || self
+116                .state
+117                .recovery_start_time
+118                .map(|recovery_start_time| sent <= recovery_start_time)
+119                .unwrap_or(false)
+120        {
+121            return;
+122        }
+123
+124        if self.state.window < self.state.ssthresh {
+125            // Slow start
+126            self.state.window += bytes;
+127        } else {
+128            // Congestion avoidance.
+129            let ca_start_time;
+130
+131            match self.state.recovery_start_time {
+132                Some(t) => ca_start_time = t,
+133                None => {
+134                    // When we come here without congestion_event() triggered,
+135                    // initialize congestion_recovery_start_time, w_max and k.
+136                    ca_start_time = now;
+137                    self.state.recovery_start_time = Some(now);
+138
+139                    self.state.w_max = self.state.window as f64;
+140                    self.state.k = 0.0;
+141                }
+142            }
+143
+144            let t = now - ca_start_time;
+145
+146            // w_cubic(t + rtt)
+147            let w_cubic = self.state.w_cubic(t + rtt.get(), self.current_mtu);
+148
+149            // w_est(t)
+150            let w_est = self.state.w_est(t, rtt.get(), self.current_mtu);
+151
+152            let mut cubic_cwnd = self.state.window;
+153
+154            if w_cubic < w_est {
+155                // TCP friendly region.
+156                cubic_cwnd = cmp::max(cubic_cwnd, w_est as u64);
+157            } else if cubic_cwnd < w_cubic as u64 {
+158                // Concave region or convex region use same increment.
+159                let cubic_inc =
+160                    (w_cubic - cubic_cwnd as f64) / cubic_cwnd as f64 * self.current_mtu as f64;
+161
+162                cubic_cwnd += cubic_inc as u64;
+163            }
+164
+165            // Update the increment and increase cwnd by MSS.
+166            self.state.cwnd_inc += cubic_cwnd - self.state.window;
+167
+168            // cwnd_inc can be more than 1 MSS in the late stage of max probing.
+169            // however RFC9002 §7.3.3 (Congestion Avoidance) limits
+170            // the increase of cwnd to 1 max_datagram_size per cwnd acknowledged.
+171            if self.state.cwnd_inc >= self.current_mtu {
+172                self.state.window += self.current_mtu;
+173                self.state.cwnd_inc = 0;
+174            }
+175        }
+176    }
+177
+178    fn on_congestion_event(
+179        &mut self,
+180        now: Instant,
+181        sent: Instant,
+182        is_persistent_congestion: bool,
+183        is_ecn: bool,
+184        _lost_bytes: u64,
+185        _largest_lost_pn: u64,
+186    ) {
+187        if self
+188            .state
+189            .recovery_start_time
+190            .map(|recovery_start_time| sent <= recovery_start_time)
+191            .unwrap_or(false)
+192        {
+193            return;
+194        }
+195
+196        // Save state in case this event ends up being spurious
+197        if !is_ecn {
+198            self.pre_congestion_state = Some(self.state.clone());
+199        }
+200
+201        self.state.recovery_start_time = Some(now);
+202
+203        // Fast convergence
+204        if (self.state.window as f64) < self.state.w_max {
+205            self.state.w_max = self.state.window as f64 * (1.0 + BETA_CUBIC) / 2.0;
+206        } else {
+207            self.state.w_max = self.state.window as f64;
+208        }
+209
+210        self.state.ssthresh = cmp::max(
+211            (self.state.w_max * BETA_CUBIC) as u64,
+212            self.minimum_window(),
+213        );
+214        self.state.window = self.state.ssthresh;
+215        self.state.k = self.state.cubic_k(self.current_mtu);
+216
+217        self.state.cwnd_inc = (self.state.cwnd_inc as f64 * BETA_CUBIC) as u64;
+218
+219        if is_persistent_congestion {
+220            self.state.recovery_start_time = None;
+221            self.state.w_max = self.state.window as f64;
+222
+223            // 4.7 Timeout - reduce ssthresh based on BETA_CUBIC
+224            self.state.ssthresh = cmp::max(
+225                (self.state.window as f64 * BETA_CUBIC) as u64,
+226                self.minimum_window(),
+227            );
+228
+229            self.state.cwnd_inc = 0;
+230
+231            self.state.window = self.minimum_window();
+232        }
+233    }
+234
+235    fn on_spurious_congestion_event(&mut self) {
+236        if let Some(prior_state) = self.pre_congestion_state.take()
+237            && self.state.window < prior_state.window
+238        {
+239            self.state = prior_state;
+240        }
+241    }
+242
+243    fn on_mtu_update(&mut self, new_mtu: u16) {
+244        self.current_mtu = new_mtu as u64;
+245        self.state.window = self.state.window.max(self.minimum_window());
+246    }
+247
+248    fn window(&self) -> u64 {
+249        self.state.window
+250    }
+251
+252    fn metrics(&self) -> super::ControllerMetrics {
+253        super::ControllerMetrics {
+254            congestion_window: self.window(),
+255            ssthresh: Some(self.state.ssthresh),
+256            pacing_rate: None,
+257            send_quantum: None,
+258        }
+259    }
+260
+261    fn clone_box(&self) -> Box<dyn Controller> {
+262        Box::new(self.clone())
+263    }
+264
+265    fn initial_window(&self) -> u64 {
+266        self.config.initial_window
+267    }
+268
+269    fn into_any(self: Box<Self>) -> Box<dyn Any> {
+270        self
+271    }
+272}
+273
+274/// Configuration for the `Cubic` congestion controller
+275#[derive(Debug, Clone)]
+276pub struct CubicConfig {
+277    initial_window: u64,
+278}
+279
+280impl CubicConfig {
+281    /// Default limit on the amount of outstanding data in bytes.
+282    ///
+283    /// Recommended value: `min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))`
+284    pub fn initial_window(&mut self, value: u64) -> &mut Self {
+285        self.initial_window = value;
+286        self
+287    }
+288}
+289
+290impl Default for CubicConfig {
+291    fn default() -> Self {
+292        Self {
+293            initial_window: 14720.clamp(2 * BASE_DATAGRAM_SIZE, 10 * BASE_DATAGRAM_SIZE),
+294        }
+295    }
+296}
+297
+298impl ControllerFactory for CubicConfig {
+299    fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller> {
+300        Box::new(Cubic::new(self, now, current_mtu))
+301    }
+302}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/congestion/new_reno.rs.html b/pr/672/docs/src/noq_proto/congestion/new_reno.rs.html new file mode 100644 index 000000000..077af3478 --- /dev/null +++ b/pr/672/docs/src/noq_proto/congestion/new_reno.rs.html @@ -0,0 +1,176 @@ +new_reno.rs - source

noq_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        _pn: u64,
+50        app_limited: bool,
+51        _rtt: &RttEstimator,
+52    ) {
+53        if app_limited || sent <= self.recovery_start_time {
+54            return;
+55        }
+56
+57        if self.window < self.ssthresh {
+58            // Slow start
+59            self.window += bytes;
+60
+61            if self.window >= self.ssthresh {
+62                // Exiting slow start
+63                // Initialize `bytes_acked` for congestion avoidance. The idea
+64                // here is that any bytes over `sshthresh` will already be counted
+65                // towards the congestion avoidance phase - independent of when
+66                // how close to `sshthresh` the `window` was when switching states,
+67                // and independent of datagram sizes.
+68                self.bytes_acked = self.window - self.ssthresh;
+69            }
+70        } else {
+71            // Congestion avoidance
+72            // This implementation uses the method which does not require
+73            // floating point math, which also increases the window by 1 datagram
+74            // for every round trip.
+75            // This mechanism is called Appropriate Byte Counting in
+76            // https://tools.ietf.org/html/rfc3465
+77            self.bytes_acked += bytes;
+78
+79            if self.bytes_acked >= self.window {
+80                self.bytes_acked -= self.window;
+81                self.window += self.current_mtu;
+82            }
+83        }
+84    }
+85
+86    fn on_congestion_event(
+87        &mut self,
+88        now: Instant,
+89        sent: Instant,
+90        is_persistent_congestion: bool,
+91        _is_ecn: bool,
+92        _lost_bytes: u64,
+93        _largest_lost_pn: u64,
+94    ) {
+95        if sent <= self.recovery_start_time {
+96            return;
+97        }
+98
+99        self.recovery_start_time = now;
+100        self.window = (self.window as f32 * self.config.loss_reduction_factor) as u64;
+101        self.window = self.window.max(self.minimum_window());
+102        self.ssthresh = self.window;
+103
+104        if is_persistent_congestion {
+105            self.window = self.minimum_window();
+106        }
+107    }
+108
+109    fn on_mtu_update(&mut self, new_mtu: u16) {
+110        self.current_mtu = new_mtu as u64;
+111        self.window = self.window.max(self.minimum_window());
+112    }
+113
+114    fn window(&self) -> u64 {
+115        self.window
+116    }
+117
+118    fn metrics(&self) -> super::ControllerMetrics {
+119        super::ControllerMetrics {
+120            congestion_window: self.window(),
+121            ssthresh: Some(self.ssthresh),
+122            pacing_rate: None,
+123            send_quantum: None,
+124        }
+125    }
+126
+127    fn clone_box(&self) -> Box<dyn Controller> {
+128        Box::new(self.clone())
+129    }
+130
+131    fn initial_window(&self) -> u64 {
+132        self.config.initial_window
+133    }
+134
+135    fn into_any(self: Box<Self>) -> Box<dyn Any> {
+136        self
+137    }
+138}
+139
+140/// Configuration for the `NewReno` congestion controller
+141#[derive(Debug, Clone)]
+142pub struct NewRenoConfig {
+143    initial_window: u64,
+144    loss_reduction_factor: f32,
+145}
+146
+147impl NewRenoConfig {
+148    /// Default limit on the amount of outstanding data in bytes.
+149    ///
+150    /// Recommended value: `min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))`
+151    pub fn initial_window(&mut self, value: u64) -> &mut Self {
+152        self.initial_window = value;
+153        self
+154    }
+155
+156    /// Reduction in congestion window when a new loss event is detected.
+157    pub fn loss_reduction_factor(&mut self, value: f32) -> &mut Self {
+158        self.loss_reduction_factor = value;
+159        self
+160    }
+161}
+162
+163impl Default for NewRenoConfig {
+164    fn default() -> Self {
+165        Self {
+166            initial_window: 14720.clamp(2 * BASE_DATAGRAM_SIZE, 10 * BASE_DATAGRAM_SIZE),
+167            loss_reduction_factor: 0.5,
+168        }
+169    }
+170}
+171
+172impl ControllerFactory for NewRenoConfig {
+173    fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller> {
+174        Box::new(NewReno::new(self, now, current_mtu))
+175    }
+176}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/ack_frequency.rs.html b/pr/672/docs/src/noq_proto/connection/ack_frequency.rs.html new file mode 100644 index 000000000..517f15560 --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/ack_frequency.rs.html @@ -0,0 +1,159 @@ +ack_frequency.rs - source

noq_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/672/docs/src/noq_proto/connection/assembler.rs.html b/pr/672/docs/src/noq_proto/connection/assembler.rs.html new file mode 100644 index 000000000..0059d322c --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/assembler.rs.html @@ -0,0 +1,846 @@ +assembler.rs - source

noq_proto/connection/
assembler.rs

1use std::{
+2    cmp::Ordering,
+3    collections::{BinaryHeap, binary_heap::PeekMut},
+4    mem,
+5};
+6
+7use bytes::{Buf, Bytes, BytesMut};
+8
+9use crate::range_set::ArrayRangeSet;
+10
+11/// Helper to assemble unordered stream frames into an ordered stream
+12#[derive(Debug, Default)]
+13pub(super) struct Assembler {
+14    state: State,
+15    data: BinaryHeap<Buffer>,
+16    /// Total number of buffered bytes, including duplicates in ordered mode.
+17    buffered: usize,
+18    /// Estimated number of allocated bytes, will never be less than `buffered`.
+19    allocated: usize,
+20    /// Number of bytes read by the application. When only ordered reads have been used, this is the
+21    /// length of the contiguous prefix of the stream which has been consumed by the application,
+22    /// aka the stream offset.
+23    bytes_read: u64,
+24    end: u64,
+25}
+26
+27impl Assembler {
+28    pub(super) fn new() -> Self {
+29        Self::default()
+30    }
+31
+32    /// Reset to the initial state
+33    pub(super) fn reinit(&mut self) {
+34        let old_data = mem::take(&mut self.data);
+35        *self = Self::default();
+36        self.data = old_data;
+37        self.data.clear();
+38    }
+39
+40    pub(super) fn ensure_ordering(&mut self, ordered: bool) -> Result<(), IllegalOrderedRead> {
+41        if ordered && !self.state.is_ordered() {
+42            return Err(IllegalOrderedRead);
+43        } else if !ordered && self.state.is_ordered() {
+44            // Enter unordered mode
+45            if !self.data.is_empty() {
+46                // Get rid of possible duplicates
+47                self.defragment();
+48            }
+49            let mut recvd = ArrayRangeSet::new();
+50            recvd.insert(0..self.bytes_read);
+51            for chunk in &self.data {
+52                recvd.insert(chunk.offset..chunk.offset + chunk.bytes.len() as u64);
+53            }
+54            self.state = State::Unordered { recvd };
+55        }
+56        Ok(())
+57    }
+58
+59    /// Get the the next chunk
+60    pub(super) fn read(&mut self, max_length: usize, ordered: bool) -> Option<Chunk> {
+61        loop {
+62            let mut chunk = self.data.peek_mut()?;
+63
+64            if ordered {
+65                if chunk.offset > self.bytes_read {
+66                    // Next chunk is after current read index
+67                    return None;
+68                } else if (chunk.offset + chunk.bytes.len() as u64) <= self.bytes_read {
+69                    // Next chunk is useless as the read index is beyond its end
+70                    self.buffered -= chunk.bytes.len();
+71                    self.allocated -= chunk.allocation_size;
+72                    PeekMut::pop(chunk);
+73                    continue;
+74                }
+75
+76                // Determine `start` and `len` of the slice of useful data in chunk
+77                let start = (self.bytes_read - chunk.offset) as usize;
+78                if start > 0 {
+79                    chunk.bytes.advance(start);
+80                    chunk.offset += start as u64;
+81                    self.buffered -= start;
+82                }
+83            }
+84
+85            return Some(if max_length < chunk.bytes.len() {
+86                self.bytes_read += max_length as u64;
+87                let offset = chunk.offset;
+88                chunk.offset += max_length as u64;
+89                self.buffered -= max_length;
+90                Chunk::new(offset, chunk.bytes.split_to(max_length))
+91            } else {
+92                self.bytes_read += chunk.bytes.len() as u64;
+93                self.buffered -= chunk.bytes.len();
+94                self.allocated -= chunk.allocation_size;
+95                let chunk = PeekMut::pop(chunk);
+96                Chunk::new(chunk.offset, chunk.bytes)
+97            });
+98        }
+99    }
+100
+101    /// Copy fragmented chunk data to new chunks backed by a single buffer
+102    ///
+103    /// This makes sure we're not unnecessarily holding on to many larger allocations.
+104    /// We merge contiguous chunks in the process of doing so.
+105    fn defragment(&mut self) {
+106        let new = BinaryHeap::with_capacity(self.data.len());
+107        let old = mem::replace(&mut self.data, new);
+108        let mut buffers = old.into_sorted_vec();
+109        self.buffered = 0;
+110        let mut fragmented_buffered = 0;
+111        let mut offset = self.bytes_read;
+112        for chunk in buffers.iter_mut().rev() {
+113            chunk.try_mark_defragment(offset);
+114            let size = chunk.bytes.len();
+115            offset = chunk.offset + size as u64;
+116            self.buffered += size;
+117            if !chunk.defragmented {
+118                fragmented_buffered += size;
+119            }
+120        }
+121        self.allocated = self.buffered;
+122        let mut buffer = BytesMut::with_capacity(fragmented_buffered);
+123        let mut offset = self.bytes_read;
+124        for chunk in buffers.into_iter().rev() {
+125            if chunk.defragmented {
+126                // bytes might be empty after try_mark_defragment
+127                if !chunk.bytes.is_empty() {
+128                    self.data.push(chunk);
+129                }
+130                continue;
+131            }
+132            // Overlap is resolved by try_mark_defragment
+133            if chunk.offset != offset + (buffer.len() as u64) {
+134                if !buffer.is_empty() {
+135                    self.data
+136                        .push(Buffer::new_defragmented(offset, buffer.split().freeze()));
+137                }
+138                offset = chunk.offset;
+139            }
+140            buffer.extend_from_slice(&chunk.bytes);
+141        }
+142        if !buffer.is_empty() {
+143            self.data
+144                .push(Buffer::new_defragmented(offset, buffer.split().freeze()));
+145        }
+146    }
+147
+148    // Note: If a packet contains many frames from the same stream, the estimated over-allocation
+149    // will be much higher because we are counting the same allocation multiple times.
+150    pub(super) fn insert(&mut self, mut offset: u64, mut bytes: Bytes, allocation_size: usize) {
+151        debug_assert!(
+152            bytes.len() <= allocation_size,
+153            "allocation_size less than bytes.len(): {:?} < {:?}",
+154            allocation_size,
+155            bytes.len()
+156        );
+157        self.end = self.end.max(offset + bytes.len() as u64);
+158        if let State::Unordered { ref mut recvd } = self.state {
+159            // Discard duplicate data
+160            let range = offset..offset + bytes.len() as u64;
+161            for duplicate in recvd.iter_range(range.clone()) {
+162                if duplicate.start > offset {
+163                    let buffer = Buffer::new(
+164                        offset,
+165                        bytes.split_to((duplicate.start - offset) as usize),
+166                        allocation_size,
+167                    );
+168                    self.buffered += buffer.bytes.len();
+169                    self.allocated += buffer.allocation_size;
+170                    self.data.push(buffer);
+171                    offset = duplicate.start;
+172                }
+173                bytes.advance((duplicate.end - offset) as usize);
+174                offset = duplicate.end;
+175            }
+176            recvd.insert(range);
+177        } else if offset < self.bytes_read {
+178            if (offset + bytes.len() as u64) <= self.bytes_read {
+179                return;
+180            } else {
+181                let diff = self.bytes_read - offset;
+182                offset += diff;
+183                bytes.advance(diff as usize);
+184            }
+185        }
+186
+187        if bytes.is_empty() {
+188            return;
+189        }
+190        let buffer = Buffer::new(offset, bytes, allocation_size);
+191        self.buffered += buffer.bytes.len();
+192        self.allocated += buffer.allocation_size;
+193        self.data.push(buffer);
+194        // `self.buffered` also counts duplicate bytes, therefore we use
+195        // `self.end - self.bytes_read` as an upper bound of buffered unique
+196        // bytes. This will cause a defragmentation if the amount of duplicate
+197        // bytes exceedes a proportion of the receive window size.
+198        let buffered = self.buffered.min((self.end - self.bytes_read) as usize);
+199        let over_allocation = self.allocated - buffered;
+200        // Rationale: on the one hand, we want to defragment rarely, ideally never
+201        // in non-pathological scenarios. However, a pathological or malicious
+202        // peer could send us one-byte frames, and since we use reference-counted
+203        // buffers in order to prevent copying, this could result in keeping a lot
+204        // of memory allocated. This limits over-allocation in proportion to the
+205        // buffered data. The constants are chosen somewhat arbitrarily and try to
+206        // balance between defragmentation overhead and over-allocation.
+207        let threshold = 32768.max(buffered * 3 / 2);
+208        if over_allocation > threshold {
+209            self.defragment()
+210        }
+211    }
+212
+213    /// Number of bytes consumed by the application
+214    pub(super) fn bytes_read(&self) -> u64 {
+215        self.bytes_read
+216    }
+217
+218    /// Discard all buffered data
+219    pub(super) fn clear(&mut self) {
+220        self.data.clear();
+221        self.buffered = 0;
+222        self.allocated = 0;
+223    }
+224}
+225
+226/// A chunk of data from the receive stream
+227#[derive(Debug, PartialEq, Eq)]
+228pub struct Chunk {
+229    /// The offset in the stream
+230    pub offset: u64,
+231    /// The contents of the chunk
+232    pub bytes: Bytes,
+233}
+234
+235impl Chunk {
+236    fn new(offset: u64, bytes: Bytes) -> Self {
+237        Self { offset, bytes }
+238    }
+239}
+240
+241#[derive(Debug, Eq)]
+242struct Buffer {
+243    offset: u64,
+244    bytes: Bytes,
+245    /// Size of the allocation behind `bytes`, if `defragmented == false`.
+246    /// Otherwise this will be set to `bytes.len()` by `try_mark_defragment`.
+247    /// Will never be less than `bytes.len()`.
+248    allocation_size: usize,
+249    defragmented: bool,
+250}
+251
+252impl Buffer {
+253    /// Constructs a new fragmented Buffer
+254    fn new(offset: u64, bytes: Bytes, allocation_size: usize) -> Self {
+255        Self {
+256            offset,
+257            bytes,
+258            allocation_size,
+259            defragmented: false,
+260        }
+261    }
+262
+263    /// Constructs a new defragmented Buffer
+264    fn new_defragmented(offset: u64, bytes: Bytes) -> Self {
+265        let allocation_size = bytes.len();
+266        Self {
+267            offset,
+268            bytes,
+269            allocation_size,
+270            defragmented: true,
+271        }
+272    }
+273
+274    /// Discards data before `offset` and flags `self` as defragmented if it has good utilization
+275    fn try_mark_defragment(&mut self, offset: u64) {
+276        let duplicate = offset.saturating_sub(self.offset) as usize;
+277        self.offset = self.offset.max(offset);
+278        if duplicate >= self.bytes.len() {
+279            // All bytes are duplicate
+280            self.bytes = Bytes::new();
+281            self.defragmented = true;
+282            self.allocation_size = 0;
+283            return;
+284        }
+285        self.bytes.advance(duplicate);
+286        // Make sure that fragmented buffers with high utilization become defragmented and
+287        // defragmented buffers remain defragmented
+288        self.defragmented = self.defragmented || self.bytes.len() * 6 / 5 >= self.allocation_size;
+289        if self.defragmented {
+290            // Make sure that defragmented buffers do not contribute to over-allocation
+291            self.allocation_size = self.bytes.len();
+292        }
+293    }
+294}
+295
+296impl Ord for Buffer {
+297    // Invert ordering based on offset (max-heap, min offset first),
+298    // prioritize longer chunks at the same offset.
+299    fn cmp(&self, other: &Self) -> Ordering {
+300        self.offset
+301            .cmp(&other.offset)
+302            .reverse()
+303            .then(self.bytes.len().cmp(&other.bytes.len()))
+304    }
+305}
+306
+307impl PartialOrd for Buffer {
+308    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+309        Some(self.cmp(other))
+310    }
+311}
+312
+313impl PartialEq for Buffer {
+314    fn eq(&self, other: &Self) -> bool {
+315        (self.offset, self.bytes.len()) == (other.offset, other.bytes.len())
+316    }
+317}
+318
+319#[derive(Debug, Default)]
+320enum State {
+321    #[default]
+322    Ordered,
+323    Unordered {
+324        /// The set of offsets that have been received from the peer, including portions not yet
+325        /// read by the application.
+326        recvd: ArrayRangeSet,
+327    },
+328}
+329
+330impl State {
+331    fn is_ordered(&self) -> bool {
+332        matches!(self, Self::Ordered)
+333    }
+334}
+335
+336/// Error indicating that an ordered read was performed on a stream after an unordered read
+337#[derive(Debug)]
+338pub(crate) struct IllegalOrderedRead;
+339
+340#[cfg(test)]
+341mod test {
+342    use super::*;
+343    use assert_matches::assert_matches;
+344
+345    #[test]
+346    fn assemble_ordered() {
+347        let mut x = Assembler::new();
+348        assert_matches!(next(&mut x, 32), None);
+349        x.insert(0, Bytes::from_static(b"123"), 3);
+350        assert_matches!(next(&mut x, 1), Some(ref y) if &y[..] == b"1");
+351        assert_matches!(next(&mut x, 3), Some(ref y) if &y[..] == b"23");
+352        x.insert(3, Bytes::from_static(b"456"), 3);
+353        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"456");
+354        x.insert(6, Bytes::from_static(b"789"), 3);
+355        x.insert(9, Bytes::from_static(b"10"), 2);
+356        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"789");
+357        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"10");
+358        assert_matches!(next(&mut x, 32), None);
+359    }
+360
+361    #[test]
+362    fn assemble_unordered() {
+363        let mut x = Assembler::new();
+364        x.ensure_ordering(false).unwrap();
+365        x.insert(3, Bytes::from_static(b"456"), 3);
+366        assert_matches!(next(&mut x, 32), None);
+367        x.insert(0, Bytes::from_static(b"123"), 3);
+368        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123");
+369        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"456");
+370        assert_matches!(next(&mut x, 32), None);
+371    }
+372
+373    #[test]
+374    fn assemble_duplicate() {
+375        let mut x = Assembler::new();
+376        x.insert(0, Bytes::from_static(b"123"), 3);
+377        x.insert(0, Bytes::from_static(b"123"), 3);
+378        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123");
+379        assert_matches!(next(&mut x, 32), None);
+380    }
+381
+382    #[test]
+383    fn assemble_duplicate_compact() {
+384        let mut x = Assembler::new();
+385        x.insert(0, Bytes::from_static(b"123"), 3);
+386        x.insert(0, Bytes::from_static(b"123"), 3);
+387        x.defragment();
+388        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123");
+389        assert_matches!(next(&mut x, 32), None);
+390    }
+391
+392    #[test]
+393    fn assemble_contained() {
+394        let mut x = Assembler::new();
+395        x.insert(0, Bytes::from_static(b"12345"), 5);
+396        x.insert(1, Bytes::from_static(b"234"), 3);
+397        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345");
+398        assert_matches!(next(&mut x, 32), None);
+399    }
+400
+401    #[test]
+402    fn assemble_contained_compact() {
+403        let mut x = Assembler::new();
+404        x.insert(0, Bytes::from_static(b"12345"), 5);
+405        x.insert(1, Bytes::from_static(b"234"), 3);
+406        x.defragment();
+407        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345");
+408        assert_matches!(next(&mut x, 32), None);
+409    }
+410
+411    #[test]
+412    fn assemble_contains() {
+413        let mut x = Assembler::new();
+414        x.insert(1, Bytes::from_static(b"234"), 3);
+415        x.insert(0, Bytes::from_static(b"12345"), 5);
+416        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345");
+417        assert_matches!(next(&mut x, 32), None);
+418    }
+419
+420    #[test]
+421    fn assemble_contains_compact() {
+422        let mut x = Assembler::new();
+423        x.insert(1, Bytes::from_static(b"234"), 3);
+424        x.insert(0, Bytes::from_static(b"12345"), 5);
+425        x.defragment();
+426        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345");
+427        assert_matches!(next(&mut x, 32), None);
+428    }
+429
+430    #[test]
+431    fn assemble_overlapping() {
+432        let mut x = Assembler::new();
+433        x.insert(0, Bytes::from_static(b"123"), 3);
+434        x.insert(1, Bytes::from_static(b"234"), 3);
+435        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123");
+436        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"4");
+437        assert_matches!(next(&mut x, 32), None);
+438    }
+439
+440    #[test]
+441    fn assemble_overlapping_compact() {
+442        let mut x = Assembler::new();
+443        x.insert(0, Bytes::from_static(b"123"), 4);
+444        x.insert(1, Bytes::from_static(b"234"), 4);
+445        x.defragment();
+446        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"1234");
+447        assert_matches!(next(&mut x, 32), None);
+448    }
+449
+450    #[test]
+451    fn assemble_complex() {
+452        let mut x = Assembler::new();
+453        x.insert(0, Bytes::from_static(b"1"), 1);
+454        x.insert(2, Bytes::from_static(b"3"), 1);
+455        x.insert(4, Bytes::from_static(b"5"), 1);
+456        x.insert(0, Bytes::from_static(b"123456"), 6);
+457        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123456");
+458        assert_matches!(next(&mut x, 32), None);
+459    }
+460
+461    #[test]
+462    fn assemble_complex_compact() {
+463        let mut x = Assembler::new();
+464        x.insert(0, Bytes::from_static(b"1"), 1);
+465        x.insert(2, Bytes::from_static(b"3"), 1);
+466        x.insert(4, Bytes::from_static(b"5"), 1);
+467        x.insert(0, Bytes::from_static(b"123456"), 6);
+468        x.defragment();
+469        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123456");
+470        assert_matches!(next(&mut x, 32), None);
+471    }
+472
+473    #[test]
+474    fn assemble_old() {
+475        let mut x = Assembler::new();
+476        x.insert(0, Bytes::from_static(b"1234"), 4);
+477        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"1234");
+478        x.insert(0, Bytes::from_static(b"1234"), 4);
+479        assert_matches!(next(&mut x, 32), None);
+480    }
+481
+482    #[test]
+483    fn compact() {
+484        let mut x = Assembler::new();
+485        x.insert(0, Bytes::from_static(b"abc"), 4);
+486        x.insert(3, Bytes::from_static(b"def"), 4);
+487        x.insert(9, Bytes::from_static(b"jkl"), 4);
+488        x.insert(12, Bytes::from_static(b"mno"), 4);
+489        x.defragment();
+490        assert_eq!(
+491            next_unordered(&mut x),
+492            Chunk::new(0, Bytes::from_static(b"abcdef"))
+493        );
+494        assert_eq!(
+495            next_unordered(&mut x),
+496            Chunk::new(9, Bytes::from_static(b"jklmno"))
+497        );
+498    }
+499
+500    #[test]
+501    fn defrag_with_missing_prefix() {
+502        let mut x = Assembler::new();
+503        x.insert(3, Bytes::from_static(b"def"), 3);
+504        x.defragment();
+505        assert_eq!(
+506            next_unordered(&mut x),
+507            Chunk::new(3, Bytes::from_static(b"def"))
+508        );
+509    }
+510
+511    #[test]
+512    fn defrag_read_chunk() {
+513        let mut x = Assembler::new();
+514        x.insert(3, Bytes::from_static(b"def"), 4);
+515        x.insert(0, Bytes::from_static(b"abc"), 4);
+516        x.insert(7, Bytes::from_static(b"hij"), 4);
+517        x.insert(11, Bytes::from_static(b"lmn"), 4);
+518        x.defragment();
+519        assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"abcdef");
+520        x.insert(5, Bytes::from_static(b"fghijklmn"), 9);
+521        assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"ghijklmn");
+522        x.insert(13, Bytes::from_static(b"nopq"), 4);
+523        assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"opq");
+524        x.insert(15, Bytes::from_static(b"pqrs"), 4);
+525        assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"rs");
+526        assert_matches!(x.read(usize::MAX, true), None);
+527    }
+528
+529    #[test]
+530    fn unordered_happy_path() {
+531        let mut x = Assembler::new();
+532        x.ensure_ordering(false).unwrap();
+533        x.insert(0, Bytes::from_static(b"abc"), 3);
+534        assert_eq!(
+535            next_unordered(&mut x),
+536            Chunk::new(0, Bytes::from_static(b"abc"))
+537        );
+538        assert_eq!(x.read(usize::MAX, false), None);
+539        x.insert(3, Bytes::from_static(b"def"), 3);
+540        assert_eq!(
+541            next_unordered(&mut x),
+542            Chunk::new(3, Bytes::from_static(b"def"))
+543        );
+544        assert_eq!(x.read(usize::MAX, false), None);
+545    }
+546
+547    #[test]
+548    fn unordered_dedup() {
+549        let mut x = Assembler::new();
+550        x.ensure_ordering(false).unwrap();
+551        x.insert(3, Bytes::from_static(b"def"), 3);
+552        assert_eq!(
+553            next_unordered(&mut x),
+554            Chunk::new(3, Bytes::from_static(b"def"))
+555        );
+556        assert_eq!(x.read(usize::MAX, false), None);
+557        x.insert(0, Bytes::from_static(b"a"), 1);
+558        x.insert(0, Bytes::from_static(b"abcdefghi"), 9);
+559        x.insert(0, Bytes::from_static(b"abcd"), 4);
+560        assert_eq!(
+561            next_unordered(&mut x),
+562            Chunk::new(0, Bytes::from_static(b"a"))
+563        );
+564        assert_eq!(
+565            next_unordered(&mut x),
+566            Chunk::new(1, Bytes::from_static(b"bc"))
+567        );
+568        assert_eq!(
+569            next_unordered(&mut x),
+570            Chunk::new(6, Bytes::from_static(b"ghi"))
+571        );
+572        assert_eq!(x.read(usize::MAX, false), None);
+573        x.insert(8, Bytes::from_static(b"ijkl"), 4);
+574        assert_eq!(
+575            next_unordered(&mut x),
+576            Chunk::new(9, Bytes::from_static(b"jkl"))
+577        );
+578        assert_eq!(x.read(usize::MAX, false), None);
+579        x.insert(12, Bytes::from_static(b"mno"), 3);
+580        assert_eq!(
+581            next_unordered(&mut x),
+582            Chunk::new(12, Bytes::from_static(b"mno"))
+583        );
+584        assert_eq!(x.read(usize::MAX, false), None);
+585        x.insert(2, Bytes::from_static(b"cde"), 3);
+586        assert_eq!(x.read(usize::MAX, false), None);
+587    }
+588
+589    #[test]
+590    fn chunks_dedup() {
+591        let mut x = Assembler::new();
+592        x.insert(3, Bytes::from_static(b"def"), 3);
+593        assert_eq!(x.read(usize::MAX, true), None);
+594        x.insert(0, Bytes::from_static(b"a"), 1);
+595        x.insert(1, Bytes::from_static(b"bcdefghi"), 9);
+596        x.insert(0, Bytes::from_static(b"abcd"), 4);
+597        assert_eq!(
+598            x.read(usize::MAX, true),
+599            Some(Chunk::new(0, Bytes::from_static(b"abcd")))
+600        );
+601        assert_eq!(
+602            x.read(usize::MAX, true),
+603            Some(Chunk::new(4, Bytes::from_static(b"efghi")))
+604        );
+605        assert_eq!(x.read(usize::MAX, true), None);
+606        x.insert(8, Bytes::from_static(b"ijkl"), 4);
+607        assert_eq!(
+608            x.read(usize::MAX, true),
+609            Some(Chunk::new(9, Bytes::from_static(b"jkl")))
+610        );
+611        assert_eq!(x.read(usize::MAX, true), None);
+612        x.insert(12, Bytes::from_static(b"mno"), 3);
+613        assert_eq!(
+614            x.read(usize::MAX, true),
+615            Some(Chunk::new(12, Bytes::from_static(b"mno")))
+616        );
+617        assert_eq!(x.read(usize::MAX, true), None);
+618        x.insert(2, Bytes::from_static(b"cde"), 3);
+619        assert_eq!(x.read(usize::MAX, true), None);
+620    }
+621
+622    #[test]
+623    fn ordered_eager_discard() {
+624        let mut x = Assembler::new();
+625        x.insert(0, Bytes::from_static(b"abc"), 3);
+626        assert_eq!(x.data.len(), 1);
+627        assert_eq!(
+628            x.read(usize::MAX, true),
+629            Some(Chunk::new(0, Bytes::from_static(b"abc")))
+630        );
+631        x.insert(0, Bytes::from_static(b"ab"), 2);
+632        assert_eq!(x.data.len(), 0);
+633        x.insert(2, Bytes::from_static(b"cd"), 2);
+634        assert_eq!(
+635            x.data.peek(),
+636            Some(&Buffer::new(3, Bytes::from_static(b"d"), 2))
+637        );
+638    }
+639
+640    #[test]
+641    fn ordered_insert_unordered_read() {
+642        let mut x = Assembler::new();
+643        x.insert(0, Bytes::from_static(b"abc"), 3);
+644        x.insert(0, Bytes::from_static(b"abc"), 3);
+645        x.ensure_ordering(false).unwrap();
+646        assert_eq!(
+647            x.read(3, false),
+648            Some(Chunk::new(0, Bytes::from_static(b"abc")))
+649        );
+650        assert_eq!(x.read(3, false), None);
+651    }
+652
+653    #[test]
+654    fn no_duplicate_after_mode_switch() {
+655        // Regression test: bytes read in ordered mode should not be returned again in unordered mode
+656        let mut x = Assembler::new();
+657        x.insert(0, Bytes::from_static(b"a"), 1);
+658        x.insert(0, Bytes::from_static(b"a"), 1); // duplicate
+659        assert_eq!(
+660            x.read(1, true),
+661            Some(Chunk::new(0, Bytes::from_static(b"a")))
+662        );
+663        x.ensure_ordering(false).unwrap();
+664        assert_eq!(x.read(1, false), None); // should be None, byte 0 already returned
+665    }
+666
+667    fn next_unordered(x: &mut Assembler) -> Chunk {
+668        x.read(usize::MAX, false).unwrap()
+669    }
+670
+671    fn next(x: &mut Assembler, size: usize) -> Option<Bytes> {
+672        x.read(size, true).map(|chunk| chunk.bytes)
+673    }
+674}
+675
+676#[cfg(all(test, not(target_family = "wasm")))]
+677mod proptests {
+678    use proptest::prelude::*;
+679    use rand::RngExt;
+680    use test_strategy::{Arbitrary, proptest};
+681
+682    use super::*;
+683
+684    const MAX_OFFSET: u64 = 512;
+685    const MAX_LEN: usize = 64;
+686
+687    #[derive(Debug, Clone, Arbitrary)]
+688    enum Op {
+689        #[weight(10)]
+690        Insert {
+691            #[strategy(0..MAX_OFFSET)]
+692            offset: u64,
+693            #[strategy(1..MAX_LEN)]
+694            len: usize,
+695        },
+696        #[weight(10)]
+697        Read {
+698            #[strategy(1..MAX_LEN)]
+699            max_len: usize,
+700        },
+701        #[weight(1)]
+702        EnsureOrdering { ordered: bool },
+703        #[weight(1)]
+704        Defragment,
+705    }
+706
+707    /// Tracks the state of the assembler for verification
+708    struct RefState {
+709        received: Vec<bool>,
+710        returned: Vec<bool>,
+711        ordered: bool,
+712    }
+713
+714    fn set_range(bits: &mut [bool], start: u64, len: usize) {
+715        for i in start..(start + len as u64).min(bits.len() as u64) {
+716            bits[i as usize] = true;
+717        }
+718    }
+719
+720    impl RefState {
+721        fn new() -> Self {
+722            Self {
+723                received: vec![false; MAX_OFFSET as usize],
+724                returned: vec![false; MAX_OFFSET as usize],
+725                ordered: true,
+726            }
+727        }
+728
+729        fn insert(&mut self, offset: u64, len: usize) {
+730            set_range(&mut self.received, offset, len);
+731        }
+732
+733        fn ensure_ordering(&mut self, ordered: bool) -> bool {
+734            if ordered && !self.ordered {
+735                return false;
+736            }
+737            self.ordered = ordered;
+738            true
+739        }
+740
+741        fn bytes_read(&self) -> u64 {
+742            self.returned.iter().filter(|&&x| x).count() as u64
+743        }
+744    }
+745
+746    fn make_data() -> Vec<u8> {
+747        use rand::SeedableRng;
+748        let mut rng = rand::rngs::StdRng::seed_from_u64(0xDEADBEEF);
+749        let mut data = vec![0u8; MAX_OFFSET as usize];
+750        rng.fill(data.as_mut_slice());
+751        data
+752    }
+753
+754    fn get_slice(data: &[u8], offset: u64, len: usize) -> Bytes {
+755        let start = offset as usize;
+756        let end = (start + len).min(data.len());
+757        Bytes::copy_from_slice(&data[start..end])
+758    }
+759
+760    fn verify_chunk(data: &[u8], chunk: &Chunk) -> bool {
+761        let start = chunk.offset as usize;
+762        chunk.bytes[..] == data[start..start + chunk.bytes.len()]
+763    }
+764
+765    #[proptest]
+766    fn assembler_matches_reference(
+767        #[strategy(proptest::collection::vec(any::<Op>(), 1..100))] ops: Vec<Op>,
+768    ) {
+769        let data = make_data();
+770        let mut asm = Assembler::new();
+771        let mut reference = RefState::new();
+772
+773        for op in ops {
+774            match op {
+775                Op::Insert { offset, len } => {
+776                    let bytes = get_slice(&data, offset, len);
+777                    asm.insert(offset, bytes, len);
+778                    reference.insert(offset, len);
+779                }
+780                Op::Read { max_len } => {
+781                    let ordered = reference.ordered;
+782                    let actual = asm.read(max_len, ordered);
+783
+784                    match actual {
+785                        None => {
+786                            // Should only be None if no unreturned received bytes available
+787                            let has_available = if ordered {
+788                                // In ordered mode, check if the first unreturned byte is received
+789                                reference
+790                                    .returned
+791                                    .iter()
+792                                    .position(|&x| !x)
+793                                    .is_some_and(|pos| reference.received[pos])
+794                            } else {
+795                                // In unordered mode, check if any unreturned received byte exists
+796                                reference
+797                                    .received
+798                                    .iter()
+799                                    .zip(&reference.returned)
+800                                    .any(|(&r, &ret)| r && !ret)
+801                            };
+802                            prop_assert!(
+803                                !has_available,
+804                                "read returned None but data was available"
+805                            );
+806                        }
+807                        Some(chunk) => {
+808                            prop_assert!(chunk.bytes.len() <= max_len, "chunk exceeds max_len");
+809                            prop_assert!(verify_chunk(&data, &chunk), "data corruption");
+810                            // Mark as returned, check for duplicates
+811                            for i in 0..chunk.bytes.len() {
+812                                let offset = chunk.offset as usize + i;
+813                                prop_assert!(
+814                                    reference.received[offset],
+815                                    "returned unreceived byte at {offset}"
+816                                );
+817                                prop_assert!(
+818                                    !reference.returned[offset],
+819                                    "duplicate byte at {offset}"
+820                                );
+821                                reference.returned[offset] = true;
+822                            }
+823                        }
+824                    }
+825                }
+826                Op::EnsureOrdering { ordered } => {
+827                    let actual = asm.ensure_ordering(ordered).is_ok();
+828                    let expected = reference.ensure_ordering(ordered);
+829                    prop_assert_eq!(actual, expected, "ensure_ordering result mismatch");
+830                }
+831                Op::Defragment => {
+832                    if asm.state.is_ordered() {
+833                        asm.defragment();
+834                    }
+835                }
+836            }
+837        }
+838
+839        // Invariant: bytes_read matches
+840        prop_assert_eq!(
+841            asm.bytes_read(),
+842            reference.bytes_read(),
+843            "bytes_read mismatch"
+844        );
+845    }
+846}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/cid_state.rs.html b/pr/672/docs/src/noq_proto/connection/cid_state.rs.html new file mode 100644 index 000000000..ea127fbbc --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/cid_state.rs.html @@ -0,0 +1,228 @@ +cid_state.rs - source

noq_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 Some(last_cid) = ids.last() else {
+145            return;
+146        };
+147        self.issued += ids.len() as u64;
+148        // Record the timestamp of CID with the largest seq number
+149        let sequence = last_cid.sequence;
+150        ids.iter().for_each(|frame| {
+151            self.active_seq.insert(frame.sequence);
+152        });
+153        self.track_lifetime(sequence, now);
+154    }
+155
+156    /// Update CidState for receipt of a `RETIRE_CONNECTION_ID` frame
+157    ///
+158    /// Returns whether a new CID can be issued, or an error if the frame was illegal.
+159    pub(crate) fn on_cid_retirement(
+160        &mut self,
+161        sequence: u64,
+162        limit: u64,
+163    ) -> Result<bool, TransportError> {
+164        if self.cid_len == 0 {
+165            return Err(TransportError::PROTOCOL_VIOLATION(
+166                "RETIRE_CONNECTION_ID when CIDs aren't in use",
+167            ));
+168        }
+169        if sequence > self.issued {
+170            debug!(
+171                sequence,
+172                "got RETIRE_CONNECTION_ID for unissued sequence number"
+173            );
+174            return Err(TransportError::PROTOCOL_VIOLATION(
+175                "RETIRE_CONNECTION_ID for unissued sequence number",
+176            ));
+177        }
+178        self.active_seq.remove(&sequence);
+179        // Consider a scenario where peer A has active remote cid 0,1,2.
+180        // Peer B first send a NEW_CONNECTION_ID with cid 3 and retire_prior_to set to 1.
+181        // Peer A processes this NEW_CONNECTION_ID frame; update remote cid to 1,2,3
+182        // and meanwhile send a RETIRE_CONNECTION_ID to retire cid 0 to peer B.
+183        // 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
+184        Ok(limit > self.active_seq.len() as u64)
+185    }
+186
+187    /// Length of local Connection IDs
+188    pub(crate) fn cid_len(&self) -> usize {
+189        self.cid_len
+190    }
+191
+192    /// The value for `retire_prior_to` field in `NEW_CONNECTION_ID` frame
+193    pub(crate) fn retire_prior_to(&self) -> u64 {
+194        self.retire_seq
+195    }
+196
+197    pub(crate) fn active_seq(&self) -> (u64, u64) {
+198        let mut min = u64::MAX;
+199        let mut max = u64::MIN;
+200        for n in self.active_seq.iter() {
+201            if n < &min {
+202                min = *n;
+203            }
+204            if n > &max {
+205                max = *n;
+206            }
+207        }
+208        (min, max)
+209    }
+210
+211    #[cfg(test)]
+212    pub(crate) fn assign_retire_seq(&mut self, v: u64) -> u64 {
+213        // Cannot retire more CIDs than what have been issued
+214        debug_assert!(v <= *self.active_seq.iter().max().unwrap() + 1);
+215        let n = v.checked_sub(self.retire_seq).unwrap();
+216        self.retire_seq = v;
+217        n
+218    }
+219}
+220
+221/// Data structure that records when issued cids should be retired
+222#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+223struct CidTimestamp {
+224    /// Highest cid sequence number created in a batch
+225    sequence: u64,
+226    /// Timestamp when cid needs to be retired
+227    timestamp: Instant,
+228}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/datagrams.rs.html b/pr/672/docs/src/noq_proto/connection/datagrams.rs.html new file mode 100644 index 000000000..74515ad08 --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/datagrams.rs.html @@ -0,0 +1,221 @@ +datagrams.rs - source

noq_proto/connection/
datagrams.rs

1use std::collections::VecDeque;
+2
+3use bytes::Bytes;
+4use thiserror::Error;
+5use tracing::{debug, trace};
+6
+7use super::Connection;
+8use crate::{
+9    FrameStats, TransportError,
+10    connection::PacketBuilder,
+11    frame::{Datagram, FrameStruct},
+12};
+13
+14/// API to control datagram traffic
+15pub struct Datagrams<'a> {
+16    pub(super) conn: &'a mut Connection,
+17}
+18
+19impl Datagrams<'_> {
+20    /// Queue an unreliable, unordered datagram for immediate transmission
+21    ///
+22    /// If `drop` is true, previously queued datagrams which are still unsent may be discarded to
+23    /// make space for this datagram, in order of oldest to newest. If `drop` is false, and there
+24    /// isn't enough space due to previously queued datagrams, this function will return
+25    /// `SendDatagramError::Blocked`. `Event::DatagramsUnblocked` will be emitted once datagrams
+26    /// have been sent.
+27    ///
+28    /// Returns `Err` iff a `len`-byte datagram cannot currently be sent.
+29    pub fn send(&mut self, data: Bytes, drop: bool) -> Result<(), SendDatagramError> {
+30        if self.conn.config.datagram_receive_buffer_size.is_none() {
+31            return Err(SendDatagramError::Disabled);
+32        }
+33        let max = self
+34            .max_size()
+35            .ok_or(SendDatagramError::UnsupportedByPeer)?;
+36        if data.len() > max {
+37            return Err(SendDatagramError::TooLarge);
+38        }
+39        if drop {
+40            while self.conn.datagrams.outgoing_total > self.conn.config.datagram_send_buffer_size {
+41                let prev = self
+42                    .conn
+43                    .datagrams
+44                    .outgoing
+45                    .pop_front()
+46                    .expect("datagrams.outgoing_total desynchronized");
+47                trace!(len = prev.data.len(), "dropping outgoing datagram");
+48                self.conn.datagrams.outgoing_total -= prev.data.len();
+49            }
+50        } else if self.conn.datagrams.outgoing_total + data.len()
+51            > self.conn.config.datagram_send_buffer_size
+52        {
+53            self.conn.datagrams.send_blocked = true;
+54            return Err(SendDatagramError::Blocked(data));
+55        }
+56        self.conn.datagrams.outgoing_total += data.len();
+57        self.conn.datagrams.outgoing.push_back(Datagram { data });
+58        Ok(())
+59    }
+60
+61    /// Compute the maximum size of datagrams that may be passed to `send_datagram`
+62    ///
+63    /// Returns `None` if datagrams are unsupported by the peer or disabled locally.
+64    ///
+65    /// This may change over the lifetime of a connection according to variation in the path MTU
+66    /// estimate. The peer can also enforce an arbitrarily small fixed limit, but if the peer's
+67    /// limit is large this is guaranteed to be a little over a kilobyte at minimum.
+68    ///
+69    /// Not necessarily the maximum size of received datagrams.
+70    ///
+71    /// When multipath is enabled, this is calculated using the smallest MTU across all
+72    /// available paths.
+73    pub fn max_size(&self) -> Option<usize> {
+74        // We use the conservative overhead bound for any packet number, reducing the budget by at
+75        // most 3 bytes, so that PN size fluctuations don't cause users sending maximum-size
+76        // datagrams to suffer avoidable packet loss.
+77        let max_size = self.conn.current_mtu() as usize
+78            - self.conn.predict_1rtt_overhead_no_pn()
+79            - Datagram::SIZE_BOUND;
+80        let limit = self
+81            .conn
+82            .peer_params
+83            .max_datagram_frame_size?
+84            .into_inner()
+85            .saturating_sub(Datagram::SIZE_BOUND as u64);
+86        Some(limit.min(max_size as u64) as usize)
+87    }
+88
+89    /// Receive an unreliable, unordered datagram
+90    pub fn recv(&mut self) -> Option<Bytes> {
+91        self.conn.datagrams.recv()
+92    }
+93
+94    /// Bytes available in the outgoing datagram buffer
+95    ///
+96    /// When greater than zero, [`send`](Self::send)ing a datagram of at most this size is
+97    /// guaranteed not to cause older datagrams to be dropped.
+98    pub fn send_buffer_space(&self) -> usize {
+99        self.conn
+100            .config
+101            .datagram_send_buffer_size
+102            .saturating_sub(self.conn.datagrams.outgoing_total)
+103    }
+104}
+105
+106#[derive(Default)]
+107pub(super) struct DatagramState {
+108    /// Number of bytes of datagrams that have been received by the local transport but not
+109    /// delivered to the application
+110    pub(super) recv_buffered: usize,
+111    pub(super) incoming: VecDeque<Datagram>,
+112    pub(super) outgoing: VecDeque<Datagram>,
+113    pub(super) outgoing_total: usize,
+114    pub(super) send_blocked: bool,
+115}
+116
+117impl DatagramState {
+118    pub(super) fn received(
+119        &mut self,
+120        datagram: Datagram,
+121        window: &Option<usize>,
+122    ) -> Result<bool, TransportError> {
+123        let window = match window {
+124            None => {
+125                return Err(TransportError::PROTOCOL_VIOLATION(
+126                    "unexpected DATAGRAM frame",
+127                ));
+128            }
+129            Some(x) => *x,
+130        };
+131
+132        if datagram.data.len() > window {
+133            return Err(TransportError::PROTOCOL_VIOLATION("oversized datagram"));
+134        }
+135
+136        let was_empty = self.recv_buffered == 0;
+137        while datagram.data.len() + self.recv_buffered > window {
+138            debug!("dropping stale datagram");
+139            self.recv();
+140        }
+141
+142        self.recv_buffered += datagram.data.len();
+143        self.incoming.push_back(datagram);
+144        Ok(was_empty)
+145    }
+146
+147    /// Discard outgoing datagrams with a payload larger than `max_payload` bytes
+148    ///
+149    /// Returns whether any datagrams were dropped.
+150    ///
+151    /// Used to ensure that reductions in MTU don't get us stuck in a state where we have a datagram
+152    /// queued but can't send it.
+153    pub(super) fn drop_oversized(&mut self, max_payload: usize) -> bool {
+154        let mut dropped_any = false;
+155        self.outgoing.retain(|datagram| {
+156            let result = datagram.data.len() < max_payload;
+157            if !result {
+158                trace!(
+159                    "dropping {} byte datagram violating {} byte limit",
+160                    datagram.data.len(),
+161                    max_payload
+162                );
+163                self.outgoing_total -= datagram.data.len();
+164                dropped_any = true;
+165            }
+166            result
+167        });
+168        dropped_any
+169    }
+170
+171    /// Attempt to write a datagram frame into `buf`, consuming it from `self.outgoing`
+172    ///
+173    /// Returns whether a frame was written. At most `max_size` bytes will be written, including
+174    /// framing.
+175    pub(super) fn write<'a, 'b>(
+176        &mut self,
+177        buf: &mut PacketBuilder<'a, 'b>,
+178        stat: &mut FrameStats,
+179    ) -> bool {
+180        let Some(datagram) = self.outgoing.pop_front() else {
+181            return false;
+182        };
+183
+184        if buf.frame_space_remaining() < datagram.size(true) {
+185            // Future work: we could be more clever about cramming small datagrams into
+186            // mostly-full packets when a larger one is queued first
+187            self.outgoing.push_front(datagram);
+188            return false;
+189        }
+190
+191        self.outgoing_total -= datagram.data.len();
+192        buf.write_frame(datagram, stat);
+193        true
+194    }
+195
+196    pub(super) fn recv(&mut self) -> Option<Bytes> {
+197        let x = self.incoming.pop_front()?.data;
+198        self.recv_buffered -= x.len();
+199        Some(x)
+200    }
+201}
+202
+203/// Errors that can arise when sending a datagram
+204#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+205pub enum SendDatagramError {
+206    /// The peer does not support receiving datagram frames
+207    #[error("datagrams not supported by peer")]
+208    UnsupportedByPeer,
+209    /// Datagram support is disabled locally
+210    #[error("datagram support disabled")]
+211    Disabled,
+212    /// The datagram is larger than the connection can currently accommodate
+213    ///
+214    /// Indicates that the path MTU minus overhead or the limit advertised by the peer has been
+215    /// exceeded.
+216    #[error("datagram too large")]
+217    TooLarge,
+218    /// Send would block
+219    #[error("datagram send blocked")]
+220    Blocked(Bytes),
+221}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/mod.rs.html b/pr/672/docs/src/noq_proto/connection/mod.rs.html new file mode 100644 index 000000000..a35b89958 --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/mod.rs.html @@ -0,0 +1,7727 @@ +mod.rs - source

noq_proto/connection/
mod.rs

1use std::{
+2    cmp,
+3    collections::{BTreeMap, VecDeque, btree_map},
+4    convert::TryFrom,
+5    fmt, io, mem,
+6    net::SocketAddr,
+7    num::{NonZeroU32, NonZeroUsize},
+8    sync::Arc,
+9};
+10
+11use bytes::{Bytes, BytesMut};
+12use frame::StreamMetaVec;
+13
+14use rand::{RngExt, SeedableRng, rngs::StdRng};
+15use rustc_hash::{FxHashMap, FxHashSet};
+16use thiserror::Error;
+17use tracing::{debug, error, trace, trace_span, warn};
+18
+19use crate::{
+20    Dir, Duration, EndpointConfig, FourTuple, Frame, INITIAL_MTU, Instant, MAX_CID_SIZE,
+21    MAX_STREAM_COUNT, MIN_INITIAL_SIZE, Side, StreamId, TIMER_GRANULARITY, TokenStore, Transmit,
+22    TransportError, TransportErrorCode, VarInt,
+23    cid_generator::ConnectionIdGenerator,
+24    cid_queue::CidQueue,
+25    config::{ServerConfig, TransportConfig},
+26    congestion::Controller,
+27    connection::{
+28        qlog::{QlogRecvPacket, QlogSink},
+29        spaces::LostPacket,
+30        stats::PathStatsMap,
+31        timer::{ConnTimer, PathTimer},
+32    },
+33    crypto::{self, Keys},
+34    frame::{
+35        self, Close, DataBlocked, Datagram, FrameStruct, NewToken, ObservedAddr, StreamDataBlocked,
+36        StreamsBlocked,
+37    },
+38    n0_nat_traversal,
+39    packet::{
+40        FixedLengthConnectionIdParser, Header, InitialHeader, InitialPacket, LongType, Packet,
+41        PacketNumber, PartialDecode, SpaceId,
+42    },
+43    range_set::ArrayRangeSet,
+44    shared::{
+45        ConnectionEvent, ConnectionEventInner, ConnectionId, DatagramConnectionEvent, EcnCodepoint,
+46        EndpointEvent, EndpointEventInner,
+47    },
+48    token::{ResetToken, Token, TokenPayload},
+49    transport_parameters::TransportParameters,
+50};
+51
+52mod ack_frequency;
+53use ack_frequency::AckFrequencyState;
+54
+55mod assembler;
+56pub use assembler::Chunk;
+57
+58mod cid_state;
+59use cid_state::CidState;
+60
+61mod datagrams;
+62use datagrams::DatagramState;
+63pub use datagrams::{Datagrams, SendDatagramError};
+64
+65mod mtud;
+66mod pacing;
+67
+68mod packet_builder;
+69use packet_builder::{PacketBuilder, PadDatagram};
+70
+71mod packet_crypto;
+72use packet_crypto::CryptoState;
+73pub(crate) use packet_crypto::EncryptionLevel;
+74
+75mod paths;
+76pub use paths::{
+77    ClosedPath, PathAbandonReason, PathEvent, PathId, PathStatus, RttEstimator, SetPathStatusError,
+78};
+79use paths::{PathData, PathState};
+80
+81pub(crate) mod qlog;
+82pub(crate) mod send_buffer;
+83
+84pub(crate) mod spaces;
+85#[cfg(fuzzing)]
+86pub use spaces::Retransmits;
+87#[cfg(not(fuzzing))]
+88use spaces::Retransmits;
+89pub(crate) use spaces::SpaceKind;
+90use spaces::{PacketSpace, SendableFrames, SentPacket, ThinRetransmits};
+91
+92mod stats;
+93pub use stats::{ConnectionStats, FrameStats, PathStats, UdpStats};
+94
+95mod streams;
+96#[cfg(fuzzing)]
+97pub use streams::StreamsState;
+98#[cfg(not(fuzzing))]
+99use streams::StreamsState;
+100pub use streams::{
+101    Chunks, ClosedStream, FinishError, ReadError, ReadableError, RecvStream, SendStream,
+102    ShouldTransmit, StreamEvent, Streams, WriteError,
+103};
+104
+105mod timer;
+106use timer::{Timer, TimerTable};
+107
+108mod transmit_buf;
+109use transmit_buf::TransmitBuf;
+110
+111mod state;
+112
+113#[cfg(not(fuzzing))]
+114use state::State;
+115#[cfg(fuzzing)]
+116pub use state::State;
+117use state::StateType;
+118
+119/// Protocol state and logic for a single QUIC connection
+120///
+121/// Objects of this type receive [`ConnectionEvent`]s and emit [`EndpointEvent`]s and application
+122/// [`Event`]s to make progress. To handle timeouts, a `Connection` returns timer updates and
+123/// expects timeouts through various methods. A number of simple getter methods are exposed
+124/// to allow callers to inspect some of the connection state.
+125///
+126/// `Connection` has roughly 4 types of methods:
+127///
+128/// - A. Simple getters, taking `&self`
+129/// - B. Handlers for incoming events from the network or system, named `handle_*`.
+130/// - C. State machine mutators, for incoming commands from the application. For convenience we
+131///   refer to this as "performing I/O" below, however as per the design of this library none of the
+132///   functions actually perform system-level I/O. For example, [`read`](RecvStream::read) and
+133///   [`write`](SendStream::write), but also things like [`reset`](SendStream::reset).
+134/// - D. Polling functions for outgoing events or actions for the caller to
+135///   take, named `poll_*`.
+136///
+137/// The simplest way to use this API correctly is to call (B) and (C) whenever
+138/// appropriate, then after each of those calls, as soon as feasible call all
+139/// polling methods (D) and deal with their outputs appropriately, e.g. by
+140/// passing it to the application or by making a system-level I/O call. You
+141/// should call the polling functions in this order:
+142///
+143/// 1. [`poll_transmit`](Self::poll_transmit)
+144/// 2. [`poll_timeout`](Self::poll_timeout)
+145/// 3. [`poll_endpoint_events`](Self::poll_endpoint_events)
+146/// 4. [`poll`](Self::poll)
+147///
+148/// Currently the only actual dependency is from (2) to (1), however additional
+149/// dependencies may be added in future, so the above order is recommended.
+150///
+151/// (A) may be called whenever desired.
+152///
+153/// Care should be made to ensure that the input events represent monotonically
+154/// increasing time. Specifically, calling [`handle_timeout`](Self::handle_timeout)
+155/// with events of the same [`Instant`] may be interleaved in any order with a
+156/// call to [`handle_event`](Self::handle_event) at that same instant; however
+157/// events or timeouts with different instants must not be interleaved.
+158pub struct Connection {
+159    endpoint_config: Arc<EndpointConfig>,
+160    config: Arc<TransportConfig>,
+161    rng: StdRng,
+162    /// Consolidated cryptographic state
+163    crypto_state: CryptoState,
+164    /// The CID we initially chose, for use during the handshake
+165    handshake_cid: ConnectionId,
+166    /// The CID the peer initially chose, for use during the handshake
+167    remote_handshake_cid: ConnectionId,
+168    /// The [`PathData`] for each path
+169    ///
+170    /// This needs to be ordered because [`Connection::poll_transmit`] needs to
+171    /// deterministically select the next PathId to send on.
+172    // TODO(flub): well does it really? But deterministic is nice for now.
+173    paths: BTreeMap<PathId, PathState>,
+174    /// Counter to uniquely identify every [`PathData`] created in this connection.
+175    ///
+176    /// Each [`PathData`] gets a [`PathData::generation`] that is unique among all
+177    /// [`PathData`]s created in the lifetime of this connection. This helps identify the
+178    /// correct path when RFC9000-style migrations happen, even when they are
+179    /// aborted.
+180    ///
+181    /// Multipath does not change this, each path can also undergo RFC9000-style
+182    /// migrations. So a single multipath path ID could see several [`PathData`]s each with
+183    /// their unique [`PathData::generation].
+184    path_generation_counter: u64,
+185    /// Whether MTU detection is supported in this environment
+186    allow_mtud: bool,
+187    state: State,
+188    side: ConnectionSide,
+189    /// Transport parameters set by the peer
+190    peer_params: TransportParameters,
+191    /// Source ConnectionId of the first packet received from the peer
+192    original_remote_cid: ConnectionId,
+193    /// Destination ConnectionId sent by the client on the first Initial
+194    initial_dst_cid: ConnectionId,
+195    /// The value that the server included in the Source Connection ID field of a Retry packet, if
+196    /// one was received
+197    retry_src_cid: Option<ConnectionId>,
+198    /// Events returned by [`Connection::poll`]
+199    events: VecDeque<Event>,
+200    endpoint_events: VecDeque<EndpointEventInner>,
+201    /// Whether the spin bit is in use for this connection
+202    spin_enabled: bool,
+203    /// Outgoing spin bit state
+204    spin: bool,
+205    /// Packet number spaces: initial, handshake, 1-RTT
+206    spaces: [PacketSpace; 3],
+207    /// Highest usable packet space.
+208    highest_space: SpaceKind,
+209    /// Negotiated idle timeout
+210    idle_timeout: Option<Duration>,
+211    timers: TimerTable,
+212    /// Number of packets received which could not be authenticated
+213    authentication_failures: u64,
+214
+215    //
+216    // Queued non-retransmittable 1-RTT data
+217    //
+218    /// If the CONNECTION_CLOSE frame needs to be sent
+219    connection_close_pending: bool,
+220
+221    //
+222    // ACK frequency
+223    //
+224    ack_frequency: AckFrequencyState,
+225
+226    //
+227    // Congestion Control
+228    //
+229    /// Whether the most recently received packet had an ECN codepoint set
+230    receiving_ecn: bool,
+231    /// Number of packets authenticated
+232    total_authed_packets: u64,
+233
+234    //
+235    // ObservedAddr
+236    //
+237    /// Sequence number for the next observed address frame sent to the peer.
+238    next_observed_addr_seq_no: VarInt,
+239
+240    streams: StreamsState,
+241    /// Active and surplus CIDs issued by the remote, for future use on new paths.
+242    ///
+243    /// These are given out before multiple paths exist, also for paths that will never
+244    /// exist.  So if multipath is supported the number of paths here will be higher than
+245    /// the actual number of paths in use.
+246    remote_cids: FxHashMap<PathId, CidQueue>,
+247    /// Attributes of CIDs generated by local endpoint
+248    ///
+249    /// Any path that is allowed to be opened is present in this map, as well as the already
+250    /// opened paths. However since CIDs are issued async by the endpoint driver via
+251    /// connection events it can not be used to know if CIDs have been issued for a path or
+252    /// not. See [`Connection::max_path_id_with_cids`] for this.
+253    local_cid_state: FxHashMap<PathId, CidState>,
+254    /// State of the unreliable datagram extension
+255    datagrams: DatagramState,
+256    /// Path level statistics.
+257    path_stats: PathStatsMap,
+258    /// Accumulated stats of all discarded paths.
+259    ///
+260    /// The connection-level stats returned by [`Self::stats`] are the sum of the stats of
+261    /// all the paths. However once a path is discarded it gets added to this field instead
+262    /// so we do not have to keep an ever growing number of paths stats in memory.
+263    partial_stats: ConnectionStats,
+264    /// QUIC version used for the connection.
+265    version: u32,
+266
+267    //
+268    // Multipath
+269    //
+270    /// Maximum number of concurrent paths
+271    ///
+272    /// Initially set from the [`TransportConfig::max_concurrent_multipath_paths`]. Even
+273    /// when multipath is disabled this will be set to 1, it is not used in that case
+274    /// though.
+275    max_concurrent_paths: NonZeroU32,
+276    /// Local maximum [`PathId`] to be used
+277    ///
+278    /// This is initially set to [`TransportConfig::get_initial_max_path_id`] when multipath
+279    /// is negotiated, or to [`PathId::ZERO`] otherwise. This is essentially the value of
+280    /// the highest MAX_PATH_ID frame sent.
+281    ///
+282    /// Any path with an ID equal or below this [`PathId`] is either:
+283    ///
+284    /// - Abandoned, if it is also in [`Connection::abandoned_paths`].
+285    /// - Open, in this case it is present in [`Connection::paths`]
+286    /// - Not yet opened, if it is in neither of these two places.
+287    ///
+288    /// Note that for not-yet-open there may or may not be any CIDs issued. See
+289    /// [`Connection::max_path_id_with_cids`].
+290    local_max_path_id: PathId,
+291    /// Remote's maximum [`PathId`] to be used
+292    ///
+293    /// This is initially set to the peer's [`TransportParameters::initial_max_path_id`] when
+294    /// multipath is negotiated, or to [`PathId::ZERO`] otherwise. A peer may increase this limit
+295    /// by sending [`Frame::MaxPathId`] frames.
+296    remote_max_path_id: PathId,
+297    /// The greatest [`PathId`] we have issued CIDs for
+298    ///
+299    /// CIDs are only issued for `min(local_max_path_id, remote_max_path_id)`. It is not
+300    /// possible to use [`Connection::local_cid_state`] to know if CIDs have been issued
+301    /// since they are issued asynchronously by the endpoint driver.
+302    max_path_id_with_cids: PathId,
+303    /// The paths already abandoned
+304    ///
+305    /// They may still have some state left in [`Connection::paths`] or
+306    /// [`Connection::local_cid_state`] since some of this has to be kept around for some
+307    /// time after a path is abandoned.
+308    // TODO(flub): Make this a more efficient data structure.  Like ranges of abandoned
+309    //    paths.  Or a set together with a minimum.  Or something.
+310    abandoned_paths: FxHashSet<PathId>,
+311
+312    /// State for n0's (<https://n0.computer>) nat traversal protocol.
+313    n0_nat_traversal: n0_nat_traversal::State,
+314    qlog: QlogSink,
+315}
+316
+317impl Connection {
+318    pub(crate) fn new(
+319        endpoint_config: Arc<EndpointConfig>,
+320        config: Arc<TransportConfig>,
+321        init_cid: ConnectionId,
+322        local_cid: ConnectionId,
+323        remote_cid: ConnectionId,
+324        network_path: FourTuple,
+325        crypto: Box<dyn crypto::Session>,
+326        cid_gen: &dyn ConnectionIdGenerator,
+327        now: Instant,
+328        version: u32,
+329        allow_mtud: bool,
+330        rng_seed: [u8; 32],
+331        side_args: SideArgs,
+332        qlog: QlogSink,
+333    ) -> Self {
+334        let pref_addr_cid = side_args.pref_addr_cid();
+335        let path_validated = side_args.path_validated();
+336        let connection_side = ConnectionSide::from(side_args);
+337        let side = connection_side.side();
+338        let mut rng = StdRng::from_seed(rng_seed);
+339        let initial_space = PacketSpace::new(now, SpaceId::Initial, &mut rng);
+340        let handshake_space = PacketSpace::new(now, SpaceId::Handshake, &mut rng);
+341        #[cfg(test)]
+342        let data_space = match config.deterministic_packet_numbers {
+343            true => PacketSpace::new_deterministic(now, SpaceId::Data),
+344            false => PacketSpace::new(now, SpaceId::Data, &mut rng),
+345        };
+346        #[cfg(not(test))]
+347        let data_space = PacketSpace::new(now, SpaceId::Data, &mut rng);
+348        let state = State::handshake(state::Handshake {
+349            remote_cid_set: side.is_server(),
+350            expected_token: Bytes::new(),
+351            client_hello: None,
+352            allow_server_migration: side.is_client(),
+353        });
+354        let local_cid_state = FxHashMap::from_iter([(
+355            PathId::ZERO,
+356            CidState::new(
+357                cid_gen.cid_len(),
+358                cid_gen.cid_lifetime(),
+359                now,
+360                if pref_addr_cid.is_some() { 2 } else { 1 },
+361            ),
+362        )]);
+363
+364        let mut path = PathData::new(network_path, allow_mtud, None, 0, now, &config);
+365        path.open_status = paths::OpenStatus::Informed;
+366        let mut this = Self {
+367            endpoint_config,
+368            crypto_state: CryptoState::new(crypto, init_cid, side, &mut rng),
+369            handshake_cid: local_cid,
+370            remote_handshake_cid: remote_cid,
+371            local_cid_state,
+372            paths: BTreeMap::from_iter([(
+373                PathId::ZERO,
+374                PathState {
+375                    data: path,
+376                    prev: None,
+377                },
+378            )]),
+379            path_generation_counter: 0,
+380            allow_mtud,
+381            state,
+382            side: connection_side,
+383            peer_params: TransportParameters::default(),
+384            original_remote_cid: remote_cid,
+385            initial_dst_cid: init_cid,
+386            retry_src_cid: None,
+387            events: VecDeque::new(),
+388            endpoint_events: VecDeque::new(),
+389            spin_enabled: config.allow_spin && rng.random_ratio(7, 8),
+390            spin: false,
+391            spaces: [initial_space, handshake_space, data_space],
+392            highest_space: SpaceKind::Initial,
+393            idle_timeout: match config.max_idle_timeout {
+394                None | Some(VarInt(0)) => None,
+395                Some(dur) => Some(Duration::from_millis(dur.0)),
+396            },
+397            timers: TimerTable::default(),
+398            authentication_failures: 0,
+399            connection_close_pending: false,
+400
+401            ack_frequency: AckFrequencyState::new(get_max_ack_delay(
+402                &TransportParameters::default(),
+403            )),
+404
+405            receiving_ecn: false,
+406            total_authed_packets: 0,
+407
+408            next_observed_addr_seq_no: 0u32.into(),
+409
+410            streams: StreamsState::new(
+411                side,
+412                config.max_concurrent_uni_streams,
+413                config.max_concurrent_bidi_streams,
+414                config.send_window,
+415                config.receive_window,
+416                config.stream_receive_window,
+417            ),
+418            datagrams: DatagramState::default(),
+419            config,
+420            remote_cids: FxHashMap::from_iter([(PathId::ZERO, CidQueue::new(remote_cid))]),
+421            rng,
+422            path_stats: Default::default(),
+423            partial_stats: ConnectionStats::default(),
+424            version,
+425
+426            // peer params are not yet known, so multipath is not enabled
+427            max_concurrent_paths: NonZeroU32::MIN,
+428            local_max_path_id: PathId::ZERO,
+429            remote_max_path_id: PathId::ZERO,
+430            max_path_id_with_cids: PathId::ZERO,
+431            abandoned_paths: Default::default(),
+432
+433            n0_nat_traversal: Default::default(),
+434            qlog,
+435        };
+436        if path_validated {
+437            this.on_path_validated(PathId::ZERO);
+438        }
+439        if side.is_client() {
+440            // Kick off the connection
+441            this.write_crypto();
+442            this.init_0rtt(now);
+443        }
+444        this.qlog
+445            .emit_tuple_assigned(PathId::ZERO, network_path, now);
+446        this
+447    }
+448
+449    /// Returns the next time at which `handle_timeout` should be called
+450    ///
+451    /// The value returned may change after:
+452    /// - the application performed some I/O on the connection
+453    /// - a call was made to `handle_event`
+454    /// - a call to `poll_transmit` returned `Some`
+455    /// - a call was made to `handle_timeout`
+456    #[must_use]
+457    pub fn poll_timeout(&mut self) -> Option<Instant> {
+458        self.timers.peek()
+459    }
+460
+461    /// Returns application-facing events
+462    ///
+463    /// Connections should be polled for events after:
+464    /// - a call was made to `handle_event`
+465    /// - a call was made to `handle_timeout`
+466    #[must_use]
+467    pub fn poll(&mut self) -> Option<Event> {
+468        if let Some(x) = self.events.pop_front() {
+469            return Some(x);
+470        }
+471
+472        if let Some(event) = self.streams.poll() {
+473            return Some(Event::Stream(event));
+474        }
+475
+476        if let Some(reason) = self.state.take_error() {
+477            return Some(Event::ConnectionLost { reason });
+478        }
+479
+480        None
+481    }
+482
+483    /// Return endpoint-facing events
+484    #[must_use]
+485    pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent> {
+486        self.endpoint_events.pop_front().map(EndpointEvent)
+487    }
+488
+489    /// Provide control over streams
+490    #[must_use]
+491    pub fn streams(&mut self) -> Streams<'_> {
+492        Streams {
+493            state: &mut self.streams,
+494            conn_state: &self.state,
+495        }
+496    }
+497
+498    /// Provide control over streams
+499    #[must_use]
+500    pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_> {
+501        assert!(id.dir() == Dir::Bi || id.initiator() != self.side.side());
+502        RecvStream {
+503            id,
+504            state: &mut self.streams,
+505            pending: &mut self.spaces[SpaceId::Data].pending,
+506        }
+507    }
+508
+509    /// Provide control over streams
+510    #[must_use]
+511    pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_> {
+512        assert!(id.dir() == Dir::Bi || id.initiator() == self.side.side());
+513        SendStream {
+514            id,
+515            state: &mut self.streams,
+516            pending: &mut self.spaces[SpaceId::Data].pending,
+517            conn_state: &self.state,
+518        }
+519    }
+520
+521    /// Opens a new path only if no path on the same network path currently exists.
+522    ///
+523    /// Returns `(path_id, true)` if the path already existed, or `(path_id, false)`
+524    /// if was opened.
+525    ///
+526    /// If `network_path` has no local IP set, then this will open a new path
+527    /// if no path exists for this remote address, independent of the existing
+528    /// path's local IP. If a local IP is set, it will match against the full
+529    /// four-tuple of existing paths. Not setting the local IP avoids having to
+530    /// guess which local interface will be used to communicate with the remote,
+531    /// should it not be known yet. We assume that if we already have a path to
+532    /// the remote, the OS is likely to use the same interface to talk to said remote.
+533    ///
+534    /// See also [`open_path`].
+535    ///
+536    /// [`open_path`]: Connection::open_path
+537    pub fn open_path_ensure(
+538        &mut self,
+539        network_path: FourTuple,
+540        initial_status: PathStatus,
+541        now: Instant,
+542    ) -> Result<(PathId, bool), PathError> {
+543        let existing_open_path = self.paths.iter().find(|(id, path)| {
+544            network_path.is_probably_same_path(&path.data.network_path)
+545                && !self.abandoned_paths.contains(*id)
+546        });
+547        match existing_open_path {
+548            Some((path_id, _state)) => Ok((*path_id, true)),
+549            None => Ok((self.open_path(network_path, initial_status, now)?, false)),
+550        }
+551    }
+552
+553    /// Opens a new path.
+554    ///
+555    /// Further errors might occur and they will be emitted in [`PathEvent::Abandoned`]
+556    /// events with this path id.  Once the path is opened and can carry application data it
+557    /// will be reported using a [`PathEvent::Established`] event.
+558    pub fn open_path(
+559        &mut self,
+560        network_path: FourTuple,
+561        initial_status: PathStatus,
+562        now: Instant,
+563    ) -> Result<PathId, PathError> {
+564        if !self.is_multipath_negotiated() {
+565            return Err(PathError::MultipathNotNegotiated);
+566        }
+567        if self.side().is_server() {
+568            return Err(PathError::ServerSideNotAllowed);
+569        }
+570
+571        let max_abandoned = self.abandoned_paths.iter().max().copied();
+572        let max_used = self.paths.keys().last().copied();
+573        let path_id = max_abandoned
+574            .max(max_used)
+575            .unwrap_or(PathId::ZERO)
+576            .saturating_add(1u8);
+577
+578        if Some(path_id) > self.max_path_id() {
+579            return Err(PathError::MaxPathIdReached);
+580        }
+581        if path_id > self.remote_max_path_id {
+582            self.spaces[SpaceId::Data].pending.paths_blocked = true;
+583            return Err(PathError::MaxPathIdReached);
+584        }
+585        if self
+586            .remote_cids
+587            .get(&path_id)
+588            .map(CidQueue::active)
+589            .is_none()
+590        {
+591            self.spaces[SpaceId::Data]
+592                .pending
+593                .path_cids_blocked
+594                .insert(path_id);
+595            return Err(PathError::RemoteCidsExhausted);
+596        }
+597
+598        let path = self.ensure_path(path_id, network_path, now, None);
+599        path.status.local_update(initial_status);
+600
+601        Ok(path_id)
+602    }
+603
+604    /// Closes a path and sends a PATH_ABANDON frame with the passed error code.
+605    ///
+606    /// Returns [`ClosePathError::LastOpenPath`] if this is the last open path.
+607    /// It does allow closing paths which have not yet been opened, as e.g. is the case
+608    /// when receiving a PATH_ABANDON from the peer for a path that was never opened locally.
+609    pub fn close_path(
+610        &mut self,
+611        now: Instant,
+612        path_id: PathId,
+613        error_code: VarInt,
+614    ) -> Result<(), ClosePathError> {
+615        self.close_path_inner(
+616            now,
+617            path_id,
+618            PathAbandonReason::ApplicationClosed { error_code },
+619        )
+620    }
+621
+622    /// Closes a path and sends a PATH_ABANDON frame.
+623    ///
+624    /// Other than [`Self::close_path`] this allows to specify the reason for the path being closed.
+625    /// Internally, this should be used over [`Self::close_path`].
+626    pub(crate) fn close_path_inner(
+627        &mut self,
+628        now: Instant,
+629        path_id: PathId,
+630        reason: PathAbandonReason,
+631    ) -> Result<(), ClosePathError> {
+632        if self.state.is_drained() {
+633            return Ok(());
+634        }
+635
+636        if !self.is_multipath_negotiated() {
+637            return Err(ClosePathError::MultipathNotNegotiated);
+638        }
+639        if self.abandoned_paths.contains(&path_id)
+640            || Some(path_id) > self.max_path_id()
+641            || !self.paths.contains_key(&path_id)
+642        {
+643            return Err(ClosePathError::ClosedPath);
+644        }
+645
+646        let is_last_path = !self
+647            .paths
+648            .keys()
+649            .any(|id| *id != path_id && !self.abandoned_paths.contains(id));
+650
+651        if is_last_path && !reason.is_remote() {
+652            return Err(ClosePathError::LastOpenPath);
+653        }
+654
+655        self.abandon_path(now, path_id, reason);
+656
+657        // When the remote abandons our last path, start a grace timer to allow
+658        // the application to open a replacement path.
+659        // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-21.html#section-3.4-8
+660        if is_last_path {
+661            // The spec suggests 1 PTO, but we use 3 * PTO to account for
+662            // packet loss when opening a replacement path. Uses initial RTT
+663            // since the abandoned path's RTT estimate is no longer valid.
+664            let rtt = RttEstimator::new(self.config.initial_rtt);
+665            let pto = rtt.pto_base() + self.ack_frequency.max_ack_delay_for_pto();
+666            let grace = pto * 3;
+667            self.timers.set(
+668                Timer::Conn(ConnTimer::NoAvailablePath),
+669                now + grace,
+670                self.qlog.with_time(now),
+671            );
+672        }
+673
+674        Ok(())
+675    }
+676
+677    /// Unconditionally abandon a path.
+678    ///
+679    /// Only to be called once sure this path should be abandoned, all checks
+680    /// should have happened before calling this.
+681    fn abandon_path(&mut self, now: Instant, path_id: PathId, reason: PathAbandonReason) {
+682        trace!(%path_id, ?reason, "abandoning path");
+683
+684        let pending_space = &mut self.spaces[SpaceId::Data].pending;
+685        // Send PATH_ABANDON
+686        pending_space
+687            .path_abandon
+688            .insert(path_id, reason.error_code());
+689
+690        // Remove pending NEW CIDs for this path
+691        pending_space.new_cids.retain(|cid| cid.path_id != path_id);
+692        pending_space.path_cids_blocked.retain(|&id| id != path_id);
+693        pending_space.path_status.retain(|&id| id != path_id);
+694
+695        // Cleanup retransmits across ALL paths (CIDs for path_id may have been transmitted on other paths)
+696        for space in self.spaces[SpaceId::Data].iter_paths_mut() {
+697            for sent_packet in space.sent_packets.values_mut() {
+698                if let Some(retransmits) = sent_packet.retransmits.get_mut() {
+699                    retransmits.new_cids.retain(|cid| cid.path_id != path_id);
+700                    retransmits.path_cids_blocked.retain(|&id| id != path_id);
+701                    retransmits.path_status.retain(|&id| id != path_id);
+702                }
+703            }
+704        }
+705
+706        // We can't send anything on abandoned paths, so we set
+707        // tail-loss probes to zero.
+708        // This likely doesn't do much, as the path won't even be tried for sending
+709        // in poll_transmit after the path is abandoned.
+710        self.spaces[SpaceId::Data].for_path(path_id).loss_probes = 0;
+711
+712        // Note: remote CIDs are NOT removed here. They are removed when the PATH_ABANDON
+713        // frame is actually written to a packet (in populate_packet). This allows sending
+714        // PATH_ABANDON on the abandoned path itself when no other path exists (#509).
+715        debug_assert!(!self.state.is_drained()); // requirement for endpoint_events, checked in `close_path_inner`
+716        self.endpoint_events
+717            .push_back(EndpointEventInner::RetireResetToken(path_id));
+718
+719        self.abandoned_paths.insert(path_id);
+720
+721        for timer in timer::PathTimer::VALUES {
+722            // match for completeness
+723            let keep_timer = match timer {
+724                // These timers deal with sending and receiving PATH_CHALLENGE and
+725                // PATH_RESPONSE, but now that the path is abandoned, we no longer care about
+726                // these frames or their timing
+727                PathTimer::PathValidationFailed
+728                | PathTimer::PathChallengeLost
+729                | PathTimer::AbandonFromValidation => false,
+730                // These timers deal with the lifetime of the path. Now that the path is abandoned,
+731                // these are not relevant.
+732                PathTimer::PathKeepAlive | PathTimer::PathIdle => false,
+733                // The path has already been informed that outstanding acks should be sent
+734                // immediately
+735                PathTimer::MaxAckDelay => false,
+736                // This timer should not be set, for completeness it's not kept as it's set when
+737                // the PATH_ABANDON frame is sent.
+738                PathTimer::PathDrained => false,
+739                // Sent packets still need to be identified as lost to trigger timely
+740                // retransmission.
+741                PathTimer::LossDetection => true,
+742                // This path should not be used for sending after the PATH_ABANDON frame is sent.
+743                // However, any outstanding data that should be sent before PATH_ABANDON, should
+744                // still respect pacing.
+745                PathTimer::Pacing => true,
+746            };
+747
+748            if !keep_timer {
+749                let qlog = self.qlog.with_time(now);
+750                self.timers.stop(Timer::PerPath(path_id, timer), qlog);
+751            }
+752        }
+753
+754        // Set the loss detection timer again, as now it should only be set
+755        // for time-based loss detection, not tail-loss probes, but currently it
+756        // could still be set to a tail-loss probe.
+757        // This will reset it to the next time-based loss time, if applicable.
+758        self.set_loss_detection_timer(now, path_id);
+759
+760        // Emit event to the application.
+761        self.events.push_back(Event::Path(PathEvent::Abandoned {
+762            id: path_id,
+763            reason,
+764        }));
+765    }
+766
+767    /// Gets the [`PathData`] for a known [`PathId`].
+768    ///
+769    /// Will panic if the path_id does not reference any known path.
+770    #[track_caller]
+771    fn path_data(&self, path_id: PathId) -> &PathData {
+772        if let Some(data) = self.paths.get(&path_id) {
+773            &data.data
+774        } else {
+775            panic!(
+776                "unknown path: {path_id}, currently known paths: {:?}",
+777                self.paths.keys().collect::<Vec<_>>()
+778            );
+779        }
+780    }
+781
+782    /// Gets the [`PathData`] for a known [`PathId`].
+783    ///
+784    /// Will panic if the path_id does not reference any known path.
+785    #[track_caller]
+786    fn path_data_mut(&mut self, path_id: PathId) -> &mut PathData {
+787        &mut self.paths.get_mut(&path_id).expect("known path").data
+788    }
+789
+790    /// Gets a reference to the [`PathData`] for a [`PathId`]
+791    fn path(&self, path_id: PathId) -> Option<&PathData> {
+792        self.paths.get(&path_id).map(|path_state| &path_state.data)
+793    }
+794
+795    /// Gets a mutable reference to the [`PathData`] for a [`PathId`]
+796    fn path_mut(&mut self, path_id: PathId) -> Option<&mut PathData> {
+797        self.paths
+798            .get_mut(&path_id)
+799            .map(|path_state| &mut path_state.data)
+800    }
+801
+802    /// Returns all known paths.
+803    ///
+804    /// There is no guarantee any of these paths are open or usable.
+805    pub fn paths(&self) -> Vec<PathId> {
+806        self.paths.keys().copied().collect()
+807    }
+808
+809    /// Gets the local [`PathStatus`] for a known [`PathId`]
+810    pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath> {
+811        self.path(path_id)
+812            .map(PathData::local_status)
+813            .ok_or(ClosedPath { _private: () })
+814    }
+815
+816    /// Returns the path's network path represented as a 4-tuple.
+817    pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath> {
+818        self.path(path_id)
+819            .map(|path| path.network_path)
+820            .ok_or(ClosedPath { _private: () })
+821    }
+822
+823    /// Sets the [`PathStatus`] for a known [`PathId`]
+824    ///
+825    /// Returns the previous path status on success.
+826    pub fn set_path_status(
+827        &mut self,
+828        path_id: PathId,
+829        status: PathStatus,
+830    ) -> Result<PathStatus, SetPathStatusError> {
+831        if !self.is_multipath_negotiated() {
+832            return Err(SetPathStatusError::MultipathNotNegotiated);
+833        }
+834        let path = self
+835            .path_mut(path_id)
+836            .ok_or(SetPathStatusError::ClosedPath)?;
+837        let prev = match path.status.local_update(status) {
+838            Some(prev) => {
+839                self.spaces[SpaceId::Data]
+840                    .pending
+841                    .path_status
+842                    .insert(path_id);
+843                prev
+844            }
+845            None => path.local_status(),
+846        };
+847        Ok(prev)
+848    }
+849
+850    /// Returns the remote path status
+851    // TODO(flub): Probably should also be some kind of path event?  Not even sure if I like
+852    //    this as an API, but for now it allows me to write a test easily.
+853    // TODO(flub): Technically this should be a Result<Option<PathSTatus>>?
+854    pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus> {
+855        self.path(path_id).and_then(|path| path.remote_status())
+856    }
+857
+858    /// Sets the max_idle_timeout for a specific path.
+859    ///
+860    /// The PathIdle timer is immediately re-armed accounting for already-elapsed
+861    /// idle time. Setting `None` disables the timeout and stops the timer.
+862    ///
+863    /// See [`TransportConfig::default_path_max_idle_timeout`] for details.
+864    ///
+865    /// Returns the previous value of the setting.
+866    pub fn set_path_max_idle_timeout(
+867        &mut self,
+868        now: Instant,
+869        path_id: PathId,
+870        timeout: Option<Duration>,
+871    ) -> Result<Option<Duration>, ClosedPath> {
+872        let path = self
+873            .paths
+874            .get_mut(&path_id)
+875            .ok_or(ClosedPath { _private: () })?;
+876        let prev = std::mem::replace(&mut path.data.idle_timeout, timeout);
+877
+878        // Adjust the PathIdle timer, accounting for already-elapsed idle time.
+879        if !self.state.is_closed() {
+880            if let Some(new_timeout) = timeout {
+881                let timer = Timer::PerPath(path_id, PathTimer::PathIdle);
+882                let deadline = match (prev, self.timers.get(timer)) {
+883                    (Some(old_timeout), Some(old_deadline)) => {
+884                        let last_activity = old_deadline.checked_sub(old_timeout).unwrap_or(now);
+885                        last_activity + new_timeout
+886                    }
+887                    _ => now + new_timeout,
+888                };
+889                self.timers.set(timer, deadline, self.qlog.with_time(now));
+890            } else {
+891                self.timers.stop(
+892                    Timer::PerPath(path_id, PathTimer::PathIdle),
+893                    self.qlog.with_time(now),
+894                );
+895            }
+896        }
+897
+898        Ok(prev)
+899    }
+900
+901    /// Sets the keep_alive_interval for a specific path
+902    ///
+903    /// See [`TransportConfig::default_path_keep_alive_interval`] for details.
+904    ///
+905    /// Returns the previous value of the setting.
+906    pub fn set_path_keep_alive_interval(
+907        &mut self,
+908        path_id: PathId,
+909        interval: Option<Duration>,
+910    ) -> Result<Option<Duration>, ClosedPath> {
+911        let path = self
+912            .paths
+913            .get_mut(&path_id)
+914            .ok_or(ClosedPath { _private: () })?;
+915        Ok(std::mem::replace(&mut path.data.keep_alive, interval))
+916    }
+917
+918    /// Find an open, validated path that's on the same network path as the given network path.
+919    ///
+920    /// Returns the first path matching, even if there's multiple.
+921    fn find_validated_path_on_network_path(
+922        &self,
+923        network_path: FourTuple,
+924    ) -> Option<(&PathId, &PathState)> {
+925        self.paths.iter().find(|(path_id, path_state)| {
+926            path_state.data.validated
+927                // Would this use the same network path, if network_path were used to send right now?
+928                && network_path.is_probably_same_path(&path_state.data.network_path)
+929                && !self.abandoned_paths.contains(path_id)
+930        })
+931        // TODO(@divma): we might want to ensure the path has been recently active to consider the
+932        // address validated
+933        // matheus23: Perhaps looking at !self.abandoned_paths.contains(path_id) is enough, given keep-alives?
+934    }
+935
+936    /// Creates the [`PathData`] for a new [`PathId`].
+937    ///
+938    /// Called for incoming packets as well as when opening a new path locally.
+939    fn ensure_path(
+940        &mut self,
+941        path_id: PathId,
+942        network_path: FourTuple,
+943        now: Instant,
+944        pn: Option<u64>,
+945    ) -> &mut PathData {
+946        let valid_path = self.find_validated_path_on_network_path(network_path);
+947        let validated = valid_path.is_some();
+948        let initial_rtt = valid_path.map(|(_, path)| path.data.rtt.conservative());
+949        let vacant_entry = match self.paths.entry(path_id) {
+950            btree_map::Entry::Vacant(vacant_entry) => vacant_entry,
+951            btree_map::Entry::Occupied(occupied_entry) => {
+952                return &mut occupied_entry.into_mut().data;
+953            }
+954        };
+955
+956        debug!(%validated, %path_id, %network_path, "path added");
+957
+958        // A new path was added. Cancel any pending NoAvailablePath grace timer.
+959        self.timers.stop(
+960            Timer::Conn(ConnTimer::NoAvailablePath),
+961            self.qlog.with_time(now),
+962        );
+963        let peer_max_udp_payload_size =
+964            u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX);
+965        self.path_generation_counter = self.path_generation_counter.wrapping_add(1);
+966        let mut data = PathData::new(
+967            network_path,
+968            self.allow_mtud,
+969            Some(peer_max_udp_payload_size),
+970            self.path_generation_counter,
+971            now,
+972            &self.config,
+973        );
+974
+975        data.validated = validated;
+976        if let Some(initial_rtt) = initial_rtt {
+977            data.rtt.reset_initial_rtt(initial_rtt);
+978        }
+979
+980        // To open a path locally we need to send a packet on the path. Sending a challenge
+981        // guarantees this.
+982        data.pending_on_path_challenge = true;
+983
+984        let path = vacant_entry.insert(PathState { data, prev: None });
+985
+986        let mut pn_space = spaces::PacketNumberSpace::new(now, SpaceId::Data, &mut self.rng);
+987        if let Some(pn) = pn {
+988            pn_space.dedup.insert(pn);
+989        }
+990        self.spaces[SpaceId::Data]
+991            .number_spaces
+992            .insert(path_id, pn_space);
+993        self.qlog.emit_tuple_assigned(path_id, network_path, now);
+994
+995        // If the remote opened this path we may not have CIDs for it. For locally opened
+996        // paths the caller should have already made sure we have CIDs and refused to open
+997        // it if there were none.
+998        if !self.remote_cids.contains_key(&path_id) {
+999            debug!(%path_id, "Remote opened path without issuing CIDs");
+1000            self.spaces[SpaceId::Data]
+1001                .pending
+1002                .path_cids_blocked
+1003                .insert(path_id);
+1004            // Do not abandon this path right away. CIDs might be in-flight still and arrive
+1005            // soon. It is up to the remote to handle this situation.
+1006        }
+1007
+1008        &mut path.data
+1009    }
+1010
+1011    /// Returns packets to transmit
+1012    ///
+1013    /// Connections should be polled for transmit after:
+1014    /// - the application performed some I/O on the connection
+1015    /// - a call was made to `handle_event`
+1016    /// - a call was made to `handle_timeout`
+1017    ///
+1018    /// `max_datagrams` specifies how many datagrams can be returned inside a
+1019    /// single Transmit using GSO. This must be at least 1.
+1020    #[must_use]
+1021    pub fn poll_transmit(
+1022        &mut self,
+1023        now: Instant,
+1024        max_datagrams: NonZeroUsize,
+1025        buf: &mut Vec<u8>,
+1026    ) -> Option<Transmit> {
+1027        let max_datagrams = match self.config.enable_segmentation_offload {
+1028            false => NonZeroUsize::MIN,
+1029            true => max_datagrams,
+1030        };
+1031
+1032        // Each call to poll_transmit can only send datagrams to one destination, because
+1033        // all datagrams in a GSO batch are for the same destination.  Therefore only
+1034        // datagrams for one destination address are produced for each poll_transmit call.
+1035
+1036        // Check whether we need to send a close message
+1037        let connection_close_pending = match self.state.as_type() {
+1038            StateType::Drained => {
+1039                for path in self.paths.values_mut() {
+1040                    path.data.app_limited = true;
+1041                }
+1042                return None;
+1043            }
+1044            StateType::Draining | StateType::Closed => {
+1045                // self.connection_close_pending is only reset once the associated packet
+1046                // had been encoded successfully
+1047                if !self.connection_close_pending {
+1048                    for path in self.paths.values_mut() {
+1049                        path.data.app_limited = true;
+1050                    }
+1051                    return None;
+1052                }
+1053                true
+1054            }
+1055            _ => false,
+1056        };
+1057
+1058        // Schedule an ACK_FREQUENCY frame if a new one needs to be sent.
+1059        if let Some(config) = &self.config.ack_frequency_config {
+1060            let rtt = self
+1061                .paths
+1062                .values()
+1063                .map(|p| p.data.rtt.get())
+1064                .min()
+1065                .expect("one path exists");
+1066            self.spaces[SpaceId::Data].pending.ack_frequency = self
+1067                .ack_frequency
+1068                .should_send_ack_frequency(rtt, config, &self.peer_params)
+1069                && self.highest_space == SpaceKind::Data
+1070                && self.peer_supports_ack_frequency();
+1071        }
+1072
+1073        let mut next_path_id = self.paths.first_entry().map(|e| *e.key());
+1074        while let Some(path_id) = next_path_id {
+1075            if !connection_close_pending
+1076                && let Some(transmit) = self.poll_transmit_off_path(now, buf, path_id)
+1077            {
+1078                return Some(transmit);
+1079            }
+1080
+1081            let info = self.scheduling_info(path_id);
+1082            if let Some(transmit) = self.poll_transmit_on_path(
+1083                now,
+1084                buf,
+1085                path_id,
+1086                max_datagrams,
+1087                &info,
+1088                connection_close_pending,
+1089            ) {
+1090                return Some(transmit);
+1091            }
+1092
+1093            // Continue checking other paths, tail-loss probes may need to be sent
+1094            // in all spaces.
+1095            debug_assert!(
+1096                buf.is_empty(),
+1097                "nothing to send on path but buffer not empty"
+1098            );
+1099
+1100            next_path_id = self.paths.keys().find(|i| **i > path_id).copied();
+1101        }
+1102
+1103        // We didn't produce any application data packet
+1104        debug_assert!(
+1105            buf.is_empty(),
+1106            "there was data in the buffer, but it was not sent"
+1107        );
+1108
+1109        if self.state.is_established() {
+1110            // Try MTU probing now
+1111            let mut next_path_id = self.paths.first_entry().map(|e| *e.key());
+1112            while let Some(path_id) = next_path_id {
+1113                if let Some(transmit) = self.poll_transmit_mtu_probe(now, buf, path_id) {
+1114                    return Some(transmit);
+1115                }
+1116                next_path_id = self.paths.keys().find(|i| **i > path_id).copied();
+1117            }
+1118        }
+1119
+1120        None
+1121    }
+1122
+1123    /// Computes the packet scheduling information for this path.
+1124    ///
+1125    /// While this information is only returned for a single path, it is important to know
+1126    /// that this information remains static for the entire span of a single
+1127    /// [`Connection::poll_transmit`] call. In other words, the return value is purely
+1128    /// functional and only depends on the [`PathId`] **during a single** `poll_transmit`
+1129    /// call. It can be computed up-front for all paths but we don't do that because it
+1130    /// involves an allocation.
+1131    ///
+1132    /// See the inline comments for how the  packet scheduling works.
+1133    ///
+1134    /// # Panics
+1135    ///
+1136    /// This will panic if called for a path for which we do not have any [`PathData`], like
+1137    /// so many other functions we have. But this is the only one to document this in its
+1138    /// doc comment. Maybe that should change. Eventually we'll refactor things for this
+1139    /// panic to go away.
+1140    fn scheduling_info(&self, path_id: PathId) -> PathSchedulingInfo {
+1141        // Such a space is preferred for SpaceKind::Data frames.
+1142        let have_validated_status_available_space = self.paths.iter().any(|(path_id, path)| {
+1143            self.remote_cids.contains_key(path_id)
+1144                && !self.abandoned_paths.contains(path_id)
+1145                && path.data.validated
+1146                && path.data.local_status() == PathStatus::Available
+1147        });
+1148
+1149        // Such a space is able to send SpaceKind::Data frames.
+1150        let have_validated_space = self.paths.iter().any(|(path_id, path)| {
+1151            self.remote_cids.contains_key(path_id)
+1152                && !self.abandoned_paths.contains(path_id)
+1153                && path.data.validated
+1154        });
+1155
+1156        let is_handshaking = self.is_handshaking();
+1157        let has_cids = self.remote_cids.contains_key(&path_id);
+1158        let is_abandoned = self.abandoned_paths.contains(&path_id);
+1159        let path_data = self.path_data(path_id);
+1160        let validated = path_data.validated;
+1161        let status = path_data.local_status();
+1162
+1163        // This is the core packet scheduling, whether this space ID may send
+1164        // SpaceKind::Data frames.
+1165        let may_send_data = has_cids
+1166            && !is_abandoned
+1167            && if is_handshaking {
+1168                // There is only one path during the handshake. We want to
+1169                // already send 0-RTT and 0.5-RTT (permitting anti-amplification
+1170                // limit) data.
+1171                true
+1172            } else if !validated {
+1173                // TODO(flub): When we have a network change we might end up
+1174                //    having to abandon all paths and re-open new ones to the
+1175                //    same remotes. This leaves us without any validated
+1176                //    path. Perhaps we should have a way to figure out if the
+1177                //    path is to a previously-validated remote address and allow
+1178                //    sending data to such remotes immediately.
+1179                false
+1180            } else {
+1181                match status {
+1182                    PathStatus::Available => {
+1183                        // Best possible space to send data on.
+1184                        true
+1185                    }
+1186                    PathStatus::Backup => {
+1187                        // If there is a status-available path we prefer that.
+1188                        !have_validated_status_available_space
+1189                    }
+1190                }
+1191            };
+1192
+1193        // CONNECTION_CLOSE is allowed to be sent on a non-validated
+1194        // path. Particularly during the handshake we want to send it before the
+1195        // path is validated. Later if there is no validated path available we
+1196        // will also accept sending it on an un-validated path.
+1197        let may_send_close = has_cids
+1198            && !is_abandoned
+1199            && if !validated && have_validated_status_available_space {
+1200                // We have a better space to send on.
+1201                false
+1202            } else {
+1203                // No other validated space, this is as good as it gets.
+1204                true
+1205            };
+1206
+1207        // PATH_ABANDON is normally sent together with other SpaceKind::Data frames. But if
+1208        // there is no other validated space to send it on, it can be sent on the path to be
+1209        // abandoned itself if that was validated.
+1210        let may_self_abandon = has_cids && validated && !have_validated_space;
+1211
+1212        PathSchedulingInfo {
+1213            is_abandoned,
+1214            may_send_data,
+1215            may_send_close,
+1216            may_self_abandon,
+1217        }
+1218    }
+1219
+1220    fn build_transmit(&mut self, path_id: PathId, transmit: TransmitBuf<'_>) -> Transmit {
+1221        debug_assert!(
+1222            !transmit.is_empty(),
+1223            "must not be called with an empty transmit buffer"
+1224        );
+1225
+1226        let network_path = self.path_data(path_id).network_path;
+1227        trace!(
+1228            segment_size = transmit.segment_size(),
+1229            last_datagram_len = transmit.len() % transmit.segment_size(),
+1230            %network_path,
+1231            "sending {} bytes in {} datagrams",
+1232            transmit.len(),
+1233            transmit.num_datagrams()
+1234        );
+1235        self.path_data_mut(path_id)
+1236            .inc_total_sent(transmit.len() as u64);
+1237
+1238        self.path_stats
+1239            .for_path(path_id)
+1240            .udp_tx
+1241            .on_sent(transmit.num_datagrams() as u64, transmit.len());
+1242
+1243        Transmit {
+1244            destination: network_path.remote,
+1245            size: transmit.len(),
+1246            ecn: if self.path_data(path_id).sending_ecn {
+1247                Some(EcnCodepoint::Ect0)
+1248            } else {
+1249                None
+1250            },
+1251            segment_size: match transmit.num_datagrams() {
+1252                1 => None,
+1253                _ => Some(transmit.segment_size()),
+1254            },
+1255            src_ip: network_path.local_ip,
+1256        }
+1257    }
+1258
+1259    /// poll_transmit logic for off-path data.
+1260    fn poll_transmit_off_path(
+1261        &mut self,
+1262        now: Instant,
+1263        buf: &mut Vec<u8>,
+1264        path_id: PathId,
+1265    ) -> Option<Transmit> {
+1266        if let Some(challenge) = self.send_prev_path_challenge(now, buf, path_id) {
+1267            return Some(challenge);
+1268        }
+1269        if let Some(response) = self.send_off_path_path_response(now, buf, path_id) {
+1270            return Some(response);
+1271        }
+1272        if let Some(challenge) = self.send_nat_traversal_path_challenge(now, buf, path_id) {
+1273            return Some(challenge);
+1274        }
+1275        None
+1276    }
+1277
+1278    /// poll_transmit logic for on-path data.
+1279    ///
+1280    /// This is not quite the same as for a multipath packet space, since [`PathId::ZERO`]
+1281    /// has 3 packet spaces, which this handles.
+1282    ///
+1283    /// See [`Self::poll_transmit_off_path`] for off-path data.
+1284    #[must_use]
+1285    fn poll_transmit_on_path(
+1286        &mut self,
+1287        now: Instant,
+1288        buf: &mut Vec<u8>,
+1289        path_id: PathId,
+1290        max_datagrams: NonZeroUsize,
+1291        scheduling_info: &PathSchedulingInfo,
+1292        connection_close_pending: bool,
+1293    ) -> Option<Transmit> {
+1294        // Check if there is at least one active CID to use for sending
+1295        let Some(remote_cid) = self.remote_cids.get(&path_id).map(CidQueue::active) else {
+1296            if !self.abandoned_paths.contains(&path_id) {
+1297                debug!(%path_id, "no remote CIDs for path");
+1298            }
+1299            return None;
+1300        };
+1301
+1302        // Whether the last packet in the datagram must be padded so the datagram takes up
+1303        // an exact size. An earlier space can decide to not fill an entire datagram and
+1304        // require the next space to fill it further. But may need a specific size of the
+1305        // datagram containing the packet. The final packet built in the datagram must pad
+1306        // to this size.
+1307        let mut pad_datagram = PadDatagram::No;
+1308
+1309        // The packet number of the last built packet. This is kept kept across spaces.
+1310        // QUIC is supposed to have a single congestion controller for the Initial,
+1311        // Handshake and Data(PathId::ZERO) spaces.
+1312        let mut last_packet_number = None;
+1313
+1314        // If we end up not sending anything, we need to know if that was because there was
+1315        // nothing to send or because we were congestion blocked.
+1316        let mut congestion_blocked = false;
+1317
+1318        // Set the segment size to this path's MTU for on-path data.
+1319        let pmtu = self.path_data(path_id).current_mtu().into();
+1320        let mut transmit = TransmitBuf::new(buf, max_datagrams, pmtu);
+1321
+1322        // Iterate over the available spaces.
+1323        for space_id in SpaceId::iter() {
+1324            // Only PathId::ZERO uses non Data space ids.
+1325            if path_id != PathId::ZERO && space_id != SpaceId::Data {
+1326                continue;
+1327            }
+1328            match self.poll_transmit_path_space(
+1329                now,
+1330                &mut transmit,
+1331                path_id,
+1332                space_id,
+1333                remote_cid,
+1334                scheduling_info,
+1335                connection_close_pending,
+1336                pad_datagram,
+1337            ) {
+1338                PollPathSpaceStatus::NothingToSend {
+1339                    congestion_blocked: cb,
+1340                } => {
+1341                    congestion_blocked |= cb;
+1342                    // Continue checking other spaces, tail-loss probes may need to be sent
+1343                    // in all spaces.
+1344                }
+1345                PollPathSpaceStatus::WrotePacket {
+1346                    last_packet_number: pn,
+1347                    pad_datagram: pad,
+1348                } => {
+1349                    debug_assert!(!transmit.is_empty(), "transmit must contain packets");
+1350                    last_packet_number = Some(pn);
+1351                    pad_datagram = pad;
+1352                    // Always check higher spaces. If the transmit is full or they have
+1353                    // nothing to send they will not write packets. But if they can, they
+1354                    // must always be allowed to add to this transmit because coalescing may
+1355                    // be required.
+1356                    continue;
+1357                }
+1358                PollPathSpaceStatus::Send {
+1359                    last_packet_number: pn,
+1360                } => {
+1361                    debug_assert!(!transmit.is_empty(), "transmit must contain packets");
+1362                    last_packet_number = Some(pn);
+1363                    break;
+1364                }
+1365            }
+1366        }
+1367
+1368        if last_packet_number.is_some() || congestion_blocked {
+1369            self.qlog.emit_recovery_metrics(
+1370                path_id,
+1371                &mut self
+1372                    .paths
+1373                    .get_mut(&path_id)
+1374                    .expect("path_id was iterated from self.paths above")
+1375                    .data,
+1376                now,
+1377            );
+1378        }
+1379
+1380        self.path_data_mut(path_id).app_limited =
+1381            last_packet_number.is_none() && !congestion_blocked;
+1382
+1383        match last_packet_number {
+1384            Some(last_packet_number) => {
+1385                // Note that when sending in multiple spaces the last packet number will be
+1386                // the one from the highest space.
+1387                self.path_data_mut(path_id).congestion.on_sent(
+1388                    now,
+1389                    transmit.len() as u64,
+1390                    last_packet_number,
+1391                );
+1392                Some(self.build_transmit(path_id, transmit))
+1393            }
+1394            None => None,
+1395        }
+1396    }
+1397
+1398    /// poll_transmit logic for a QUIC-MULTIPATH packet number space (PathID + SpaceId).
+1399    #[must_use]
+1400    fn poll_transmit_path_space(
+1401        &mut self,
+1402        now: Instant,
+1403        transmit: &mut TransmitBuf<'_>,
+1404        path_id: PathId,
+1405        space_id: SpaceId,
+1406        remote_cid: ConnectionId,
+1407        scheduling_info: &PathSchedulingInfo,
+1408        // If we need to send a CONNECTION_CLOSE frame.
+1409        connection_close_pending: bool,
+1410        // Whether the current datagram needs to be padded to a certain size.
+1411        mut pad_datagram: PadDatagram,
+1412    ) -> PollPathSpaceStatus {
+1413        // Keep track of the last packet number we wrote. If None we did not write any
+1414        // packets.
+1415        let mut last_packet_number = None;
+1416
+1417        // Each loop of this may build one packet. It works logically as follows:
+1418        //
+1419        // - Check if something *needs* to be sent in this space and *can* be sent.
+1420        //   - If not, return to the caller who will call us again for the next space.
+1421        // - Start a new datagram.
+1422        //   - Unless coalescing the packet into an existing datagram.
+1423        // - Write the packet header and payload.
+1424        // - Check if coalescing a next packet into the datagram is possible.
+1425        // - If coalescing, finish packet without padding to leave space in the datagram.
+1426        // - If not coalescing, complete the datagram:
+1427        //   - Finish packet with padding.
+1428        //   - Set the transmit segment size if this is the first datagram.
+1429        // - Loop: next iteration will exit the loop if nothing more to send in this
+1430        //   space. The TransmitBuf will contain a started datagram with space if
+1431        //   coalescing, or completely filled datagram if not coalescing.
+1432        loop {
+1433            // Determine if anything can be sent in this packet number space.
+1434            let max_packet_size = if transmit.datagram_remaining_mut() > 0 {
+1435                // A datagram is started already, we are coalescing another packet into it.
+1436                transmit.datagram_remaining_mut()
+1437            } else {
+1438                // A new datagram needs to be started.
+1439                transmit.segment_size()
+1440            };
+1441            let can_send =
+1442                self.space_can_send(space_id, path_id, max_packet_size, connection_close_pending);
+1443            let needs_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0;
+1444            let space_will_send = {
+1445                if scheduling_info.is_abandoned {
+1446                    // If this path is abandoned then we might still have to send
+1447                    // PATH_ABANDON itself on it if there was no better space
+1448                    // available. Otherwise we want to send the PATH_ABANDON as permitted by
+1449                    // may_send_data however.
+1450                    scheduling_info.may_self_abandon
+1451                        && self.spaces[space_id]
+1452                            .pending
+1453                            .path_abandon
+1454                            .contains_key(&path_id)
+1455                } else if can_send.close && scheduling_info.may_send_close {
+1456                    // This is the best path to send a CONNECTION_CLOSE on.
+1457                    true
+1458                } else if needs_loss_probe || can_send.space_specific {
+1459                    // We always send a loss probe or space-specific frames if the path is
+1460                    // not abandoned.
+1461                    true
+1462                } else {
+1463                    // Anything else we only send if we're the best path for SpaceKind::Data
+1464                    // frames.
+1465                    !can_send.is_empty() && scheduling_info.may_send_data
+1466                }
+1467            };
+1468
+1469            if !space_will_send {
+1470                // Nothing more to send. Previous iterations of this loop may have built
+1471                // packets already.
+1472                return match last_packet_number {
+1473                    Some(pn) => PollPathSpaceStatus::WrotePacket {
+1474                        last_packet_number: pn,
+1475                        pad_datagram,
+1476                    },
+1477                    None => {
+1478                        // Only log for spaces which have crypto.
+1479                        if self.crypto_state.has_keys(space_id.encryption_level())
+1480                            || (space_id == SpaceId::Data
+1481                                && self.crypto_state.has_keys(EncryptionLevel::ZeroRtt))
+1482                        {
+1483                            trace!(?space_id, %path_id, "nothing to send in space");
+1484                        }
+1485                        PollPathSpaceStatus::NothingToSend {
+1486                            congestion_blocked: false,
+1487                        }
+1488                    }
+1489                };
+1490            }
+1491
+1492            // We want to send on this space, check congestion control if we can. But only
+1493            // if we will need to start a new datagram. If we are coalescing into an already
+1494            // started datagram we do not need to check congestion control again.
+1495            if transmit.datagram_remaining_mut() == 0 {
+1496                let congestion_blocked =
+1497                    self.path_congestion_check(space_id, path_id, transmit, &can_send, now);
+1498                if congestion_blocked != PathBlocked::No {
+1499                    // Previous iterations of this loop may have built packets already.
+1500                    return match last_packet_number {
+1501                        Some(pn) => PollPathSpaceStatus::WrotePacket {
+1502                            last_packet_number: pn,
+1503                            pad_datagram,
+1504                        },
+1505                        None => {
+1506                            return PollPathSpaceStatus::NothingToSend {
+1507                                congestion_blocked: true,
+1508                            };
+1509                        }
+1510                    };
+1511                }
+1512
+1513                // If the datagram is full (or there never was one started), we need to start a
+1514                // new one.
+1515                if transmit.num_datagrams() >= transmit.max_datagrams().get() {
+1516                    // No more datagrams allowed.
+1517                    // Previous iterations of this loop may have built packets already.
+1518                    return match last_packet_number {
+1519                        Some(pn) => PollPathSpaceStatus::WrotePacket {
+1520                            last_packet_number: pn,
+1521                            pad_datagram,
+1522                        },
+1523                        None => {
+1524                            return PollPathSpaceStatus::NothingToSend {
+1525                                congestion_blocked: false,
+1526                            };
+1527                        }
+1528                    };
+1529                }
+1530
+1531                if needs_loss_probe {
+1532                    // Ensure we have something to send for a tail-loss probe.
+1533                    let request_immediate_ack =
+1534                        space_id == SpaceId::Data && self.peer_supports_ack_frequency();
+1535                    self.spaces[space_id].queue_tail_loss_probe(
+1536                        path_id,
+1537                        request_immediate_ack,
+1538                        &self.streams,
+1539                    );
+1540
+1541                    self.spaces[space_id].for_path(path_id).loss_probes -= 1; // needs_loss_probe ensures loss_probes > 0
+1542
+1543                    // Clamp the datagram to at most the minimum MTU to ensure that loss
+1544                    // probes can get through and enable recovery even if the path MTU
+1545                    // has shrank unexpectedly.
+1546                    transmit.start_new_datagram_with_size(std::cmp::min(
+1547                        usize::from(INITIAL_MTU),
+1548                        transmit.segment_size(),
+1549                    ));
+1550                } else {
+1551                    transmit.start_new_datagram();
+1552                }
+1553                trace!(count = transmit.num_datagrams(), "new datagram started");
+1554
+1555                // We started a new datagram, we decide later if it needs padding.
+1556                pad_datagram = PadDatagram::No;
+1557            }
+1558
+1559            // If coalescing another packet into the existing datagram, there should
+1560            // still be enough space for a whole packet.
+1561            if transmit.datagram_start_offset() < transmit.len() {
+1562                debug_assert!(transmit.datagram_remaining_mut() >= MIN_PACKET_SPACE);
+1563            }
+1564
+1565            //
+1566            // From here on, we've determined that a packet will definitely be sent.
+1567            //
+1568
+1569            if self.crypto_state.has_keys(EncryptionLevel::Initial)
+1570                && space_id == SpaceId::Handshake
+1571                && self.side.is_client()
+1572            {
+1573                // A client stops both sending and processing Initial packets when it
+1574                // sends its first Handshake packet.
+1575                self.discard_space(now, SpaceKind::Initial);
+1576            }
+1577            if let Some(ref mut prev) = self.crypto_state.prev_crypto {
+1578                prev.update_unacked = false;
+1579            }
+1580
+1581            let Some(mut builder) =
+1582                PacketBuilder::new(now, space_id, path_id, remote_cid, transmit, self)
+1583            else {
+1584                // Confidentiality limit is exceeded and the connection has been killed. We
+1585                // should not send any other packets. This works in a roundabout way: We
+1586                // have started a datagram but not written anything into it. So even if we
+1587                // get called again for another space we will see an already started
+1588                // datagram and try and start another packet here. Then be stopped by the
+1589                // same confidentiality limit.
+1590                return PollPathSpaceStatus::NothingToSend {
+1591                    congestion_blocked: false,
+1592                };
+1593            };
+1594            last_packet_number = Some(builder.packet_number);
+1595
+1596            if space_id == SpaceId::Initial
+1597                && (self.side.is_client() || can_send.is_ack_eliciting())
+1598            {
+1599                // https://www.rfc-editor.org/rfc/rfc9000.html#section-14.1
+1600                pad_datagram |= PadDatagram::ToMinMtu;
+1601            }
+1602            if space_id == SpaceId::Data && self.config.pad_to_mtu {
+1603                pad_datagram |= PadDatagram::ToSegmentSize;
+1604            }
+1605
+1606            if scheduling_info.may_send_close && can_send.close {
+1607                trace!("sending CONNECTION_CLOSE");
+1608                // Encode ACKs before the ConnectionClose message, to give the receiver
+1609                // a better approximate on what data has been processed. This is
+1610                // especially important with ack delay, since the peer might not
+1611                // have gotten any other ACK for the data earlier on.
+1612                let is_multipath_negotiated = self.is_multipath_negotiated();
+1613                for path_id in self.spaces[space_id]
+1614                    .number_spaces
+1615                    .iter()
+1616                    .filter(|(_, pns)| !pns.pending_acks.ranges().is_empty())
+1617                    .map(|(&path_id, _)| path_id)
+1618                    .collect::<Vec<_>>()
+1619                {
+1620                    Self::populate_acks(
+1621                        now,
+1622                        self.receiving_ecn,
+1623                        path_id,
+1624                        space_id,
+1625                        &mut self.spaces[space_id],
+1626                        is_multipath_negotiated,
+1627                        &mut builder,
+1628                        &mut self.path_stats.for_path(path_id).frame_tx,
+1629                        self.crypto_state.has_keys(space_id.encryption_level()),
+1630                    );
+1631                }
+1632
+1633                // Since there only 64 ACK frames there will always be enough space
+1634                // to encode the ConnectionClose frame too. However we still have the
+1635                // check here to prevent crashes if something changes.
+1636
+1637                // TODO(flub): This needs fixing for multipath, to ensure we can always
+1638                //    write the CONNECTION_CLOSE even if we have many PATH_ACKs to send:
+1639                //    https://github.com/n0-computer/noq/issues/367.
+1640                debug_assert!(
+1641                    builder.frame_space_remaining() > frame::ConnectionClose::SIZE_BOUND,
+1642                    "ACKs should leave space for ConnectionClose"
+1643                );
+1644                let stats = &mut self.path_stats.for_path(path_id).frame_tx;
+1645                if frame::ConnectionClose::SIZE_BOUND < builder.frame_space_remaining() {
+1646                    let max_frame_size = builder.frame_space_remaining();
+1647                    let close: Close = match self.state.as_type() {
+1648                        StateType::Closed => {
+1649                            let reason: Close =
+1650                                self.state.as_closed().expect("checked").clone().into();
+1651                            if space_id == SpaceId::Data || reason.is_transport_layer() {
+1652                                reason
+1653                            } else {
+1654                                TransportError::APPLICATION_ERROR("").into()
+1655                            }
+1656                        }
+1657                        StateType::Draining => TransportError::NO_ERROR("").into(),
+1658                        _ => unreachable!(
+1659                            "tried to make a close packet when the connection wasn't closed"
+1660                        ),
+1661                    };
+1662                    builder.write_frame(close.encoder(max_frame_size), stats);
+1663                }
+1664                let last_pn = builder.packet_number;
+1665                builder.finish_and_track(now, self, path_id, pad_datagram);
+1666                if space_id.kind() == self.highest_space {
+1667                    // Don't send another close packet. Even with multipath we only send
+1668                    // CONNECTION_CLOSE on a single path since we expect our paths to work.
+1669                    self.connection_close_pending = false;
+1670                }
+1671                // Send a close frame in every possible space for robustness, per
+1672                // RFC9000 "Immediate Close during the Handshake". Don't bother trying
+1673                // to send anything else.
+1674                // TODO(flub): This breaks during the handshake if we can not coalesce
+1675                //    packets due to space reasons: the next space would either fail a
+1676                //    debug_assert checking for enough packet space or produce an invalid
+1677                //    packet. We need to keep track of per-space pending CONNECTION_CLOSE to
+1678                //    be able to send these across multiple calls to poll_transmit. Then
+1679                //    check for coalescing space here because initial packets need to be in
+1680                //    padded datagrams. And also add space checks for CONNECTION_CLOSE in
+1681                //    space_can_send so it would stop a GSO batch if the datagram is too
+1682                //    small for another CONNECTION_CLOSE packet.
+1683                return PollPathSpaceStatus::WrotePacket {
+1684                    last_packet_number: last_pn,
+1685                    pad_datagram,
+1686                };
+1687            }
+1688
+1689            self.populate_packet(now, space_id, path_id, scheduling_info, &mut builder);
+1690
+1691            // ACK-only packets should only be sent when explicitly allowed. If we write them due to
+1692            // any other reason, there is a bug which leads to one component announcing write
+1693            // readiness while not writing any data. This degrades performance. The condition is
+1694            // only checked if the full MTU is available and when potentially large fixed-size
+1695            // frames aren't queued, so that lack of space in the datagram isn't the reason for just
+1696            // writing ACKs.
+1697            debug_assert!(
+1698                !(builder.sent_frames().is_ack_only(&self.streams)
+1699                    && !can_send.acks
+1700                    && (can_send.other || can_send.space_specific)
+1701                    && builder.buf.segment_size()
+1702                        == self.path_data(path_id).current_mtu() as usize
+1703                    && self.datagrams.outgoing.is_empty()),
+1704                "SendableFrames was {can_send:?}, but only ACKs have been written"
+1705            );
+1706            if builder.sent_frames().requires_padding {
+1707                pad_datagram |= PadDatagram::ToMinMtu;
+1708            }
+1709
+1710            for (path_id, _pn) in builder.sent_frames().largest_acked.iter() {
+1711                self.spaces[space_id]
+1712                    .for_path(*path_id)
+1713                    .pending_acks
+1714                    .acks_sent();
+1715                self.timers.stop(
+1716                    Timer::PerPath(*path_id, PathTimer::MaxAckDelay),
+1717                    self.qlog.with_time(now),
+1718                );
+1719            }
+1720
+1721            // Now we need to finish the packet.  Before we do so we need to know if we will
+1722            // be coalescing the next packet into this one, or will be ending the datagram
+1723            // as well.  Because if this is the last packet in the datagram more padding
+1724            // might be needed because of the packet type, or to fill the GSO segment size.
+1725
+1726            // Are we allowed to coalesce AND is there enough space for another *packet* in
+1727            // this datagram AND will we definitely send another packet?
+1728            if builder.can_coalesce && path_id == PathId::ZERO && {
+1729                let max_packet_size = builder
+1730                    .buf
+1731                    .datagram_remaining_mut()
+1732                    .saturating_sub(builder.predict_packet_end());
+1733                max_packet_size > MIN_PACKET_SPACE
+1734                    && self.has_pending_packet(space_id, max_packet_size, connection_close_pending)
+1735            } {
+1736                // We can append/coalesce the next packet into the current
+1737                // datagram. Finish the current packet without adding extra padding.
+1738                trace!("will coalesce with next packet");
+1739                builder.finish_and_track(now, self, path_id, PadDatagram::No);
+1740            } else {
+1741                // We need a new datagram for the next packet.  Finish the current
+1742                // packet with padding.
+1743                // TODO(flub): if there isn't any more data to be sent, this will still pad
+1744                //    to the segment size and only discover there is nothing to send before
+1745                //    starting the next packet. That is wasting up to 32 bytes.
+1746                if builder.buf.num_datagrams() > 1 && matches!(pad_datagram, PadDatagram::No) {
+1747                    // If too many padding bytes would be required to continue the
+1748                    // GSO batch after this packet, end the GSO batch here. Ensures
+1749                    // that fixed-size frames with heterogeneous sizes
+1750                    // (e.g. application datagrams) won't inadvertently waste large
+1751                    // amounts of bandwidth. The exact threshold is a bit arbitrary
+1752                    // and might benefit from further tuning, though there's no
+1753                    // universally optimal value.
+1754                    const MAX_PADDING: usize = 32;
+1755                    if builder.buf.datagram_remaining_mut()
+1756                        > builder.predict_packet_end() + MAX_PADDING
+1757                    {
+1758                        trace!(
+1759                            "GSO truncated by demand for {} padding bytes",
+1760                            builder.buf.datagram_remaining_mut() - builder.predict_packet_end()
+1761                        );
+1762                        let last_pn = builder.packet_number;
+1763                        builder.finish_and_track(now, self, path_id, PadDatagram::No);
+1764                        return PollPathSpaceStatus::Send {
+1765                            last_packet_number: last_pn,
+1766                        };
+1767                    }
+1768
+1769                    // Pad the current datagram to GSO segment size so it can be
+1770                    // included in the GSO batch.
+1771                    builder.finish_and_track(now, self, path_id, PadDatagram::ToSegmentSize);
+1772                } else {
+1773                    builder.finish_and_track(now, self, path_id, pad_datagram);
+1774                }
+1775
+1776                // If this is the first datagram we set the segment size to the size of the
+1777                // first datagram.
+1778                if transmit.num_datagrams() == 1 {
+1779                    transmit.clip_segment_size();
+1780                }
+1781            }
+1782        }
+1783    }
+1784
+1785    fn poll_transmit_mtu_probe(
+1786        &mut self,
+1787        now: Instant,
+1788        buf: &mut Vec<u8>,
+1789        path_id: PathId,
+1790    ) -> Option<Transmit> {
+1791        let (active_cid, probe_size) = self.get_mtu_probe_data(now, path_id)?;
+1792
+1793        // We are definitely sending a DPLPMTUD probe.
+1794        let mut transmit = TransmitBuf::new(buf, NonZeroUsize::MIN, probe_size as usize);
+1795        transmit.start_new_datagram_with_size(probe_size as usize);
+1796
+1797        let mut builder =
+1798            PacketBuilder::new(now, SpaceId::Data, path_id, active_cid, &mut transmit, self)?;
+1799
+1800        // We implement MTU probes as ping packets padded up to the probe size
+1801        trace!(?probe_size, "writing MTUD probe");
+1802        builder.write_frame(frame::Ping, &mut self.path_stats.for_path(path_id).frame_tx);
+1803
+1804        // If supported by the peer, we want no delays to the probe's ACK
+1805        if self.peer_supports_ack_frequency() {
+1806            builder.write_frame(
+1807                frame::ImmediateAck,
+1808                &mut self.path_stats.for_path(path_id).frame_tx,
+1809            );
+1810        }
+1811
+1812        builder.finish_and_track(now, self, path_id, PadDatagram::ToSize(probe_size));
+1813
+1814        self.path_stats.for_path(path_id).sent_plpmtud_probes += 1;
+1815
+1816        Some(self.build_transmit(path_id, transmit))
+1817    }
+1818
+1819    /// Returns the CID and probe size if a DPLPMTUD probe is needed.
+1820    ///
+1821    /// We MTU probe all paths for which all of the following is true:
+1822    /// - We have an active destination CID for the path.
+1823    /// - The remote address *and* path are validated.
+1824    /// - The path is not abandoned.
+1825    /// - The MTU Discovery subsystem wants to probe the path.
+1826    fn get_mtu_probe_data(&mut self, now: Instant, path_id: PathId) -> Option<(ConnectionId, u16)> {
+1827        let active_cid = self.remote_cids.get(&path_id).map(CidQueue::active)?;
+1828        let is_eligible = self.path_data(path_id).validated
+1829            && !self.path_data(path_id).is_validating_path()
+1830            && !self.abandoned_paths.contains(&path_id);
+1831
+1832        if !is_eligible {
+1833            return None;
+1834        }
+1835        let next_pn = self.spaces[SpaceId::Data]
+1836            .for_path(path_id)
+1837            .peek_tx_number();
+1838        let probe_size = self
+1839            .path_data_mut(path_id)
+1840            .mtud
+1841            .poll_transmit(now, next_pn)?;
+1842
+1843        Some((active_cid, probe_size))
+1844    }
+1845
+1846    /// Returns true if there is a further packet to send on [`PathId::ZERO`].
+1847    ///
+1848    /// In other words this is predicting whether the next call to
+1849    /// [`Connection::space_can_send`] issued will return some frames to be sent. Including
+1850    /// having to predict which packet number space it will be invoked with. This depends on
+1851    /// how both [`Connection::poll_transmit_on_path`] and
+1852    /// [`Connection::poll_transmit_path_space`] behave.
+1853    ///
+1854    /// This is needed to determine if packet coalescing can happen. Because the last packet
+1855    /// in a datagram may need to be padded and thus we must know if another packet will
+1856    /// follow or not.
+1857    ///
+1858    /// The next packet can be either in the same space, or in one of the following spaces
+1859    /// on the same path. Because a 0-RTT packet can be coalesced with a 1-RTT packet and
+1860    /// both are in the Data(PathId::ZERO) space. Previous spaces are not checked, because
+1861    /// packets are built from Initial to Handshake to Data spaces.
+1862    fn has_pending_packet(
+1863        &mut self,
+1864        current_space_id: SpaceId,
+1865        max_packet_size: usize,
+1866        connection_close_pending: bool,
+1867    ) -> bool {
+1868        let mut space_id = current_space_id;
+1869        loop {
+1870            let can_send = self.space_can_send(
+1871                space_id,
+1872                PathId::ZERO,
+1873                max_packet_size,
+1874                connection_close_pending,
+1875            );
+1876            if !can_send.is_empty() {
+1877                return true;
+1878            }
+1879            match space_id.next() {
+1880                Some(next_space_id) => space_id = next_space_id,
+1881                None => break,
+1882            }
+1883        }
+1884        false
+1885    }
+1886
+1887    /// Checks if creating a new datagram would be blocked by congestion control
+1888    fn path_congestion_check(
+1889        &mut self,
+1890        space_id: SpaceId,
+1891        path_id: PathId,
+1892        transmit: &TransmitBuf<'_>,
+1893        can_send: &SendableFrames,
+1894        now: Instant,
+1895    ) -> PathBlocked {
+1896        // Anti-amplification is only based on `total_sent`, which gets updated after
+1897        // the transmit is sent. Therefore we pass the amount of bytes for datagrams
+1898        // that are already created, as well as 1 byte for starting another datagram. If
+1899        // there is any anti-amplification budget left, we always allow a full MTU to be
+1900        // sent (see https://github.com/quinn-rs/quinn/issues/1082).
+1901        if self.side().is_server()
+1902            && self
+1903                .path_data(path_id)
+1904                .anti_amplification_blocked(transmit.len() as u64 + 1)
+1905        {
+1906            trace!(?space_id, %path_id, "blocked by anti-amplification");
+1907            return PathBlocked::AntiAmplification;
+1908        }
+1909
+1910        // Congestion control check.
+1911        // Tail loss probes must not be blocked by congestion, or a deadlock could arise.
+1912        let bytes_to_send = transmit.segment_size() as u64;
+1913        let need_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0;
+1914
+1915        if can_send.other && !need_loss_probe && !can_send.close {
+1916            let path = self.path_data(path_id);
+1917            if path.in_flight.bytes + bytes_to_send >= path.congestion.window() {
+1918                trace!(
+1919                    ?space_id,
+1920                    %path_id,
+1921                    in_flight=%path.in_flight.bytes,
+1922                    congestion_window=%path.congestion.window(),
+1923                    "blocked by congestion control",
+1924                );
+1925                return PathBlocked::Congestion;
+1926            }
+1927        }
+1928
+1929        // Pacing check.
+1930        if let Some(delay) = self.path_data_mut(path_id).pacing_delay(bytes_to_send, now) {
+1931            let resume_time = now + delay;
+1932            self.timers.set(
+1933                Timer::PerPath(path_id, PathTimer::Pacing),
+1934                resume_time,
+1935                self.qlog.with_time(now),
+1936            );
+1937            // Loss probes and CONNECTION_CLOSE should be subject to pacing, even though
+1938            // they are not congestion controlled.
+1939            trace!(?space_id, %path_id, ?delay, "blocked by pacing");
+1940            return PathBlocked::Pacing;
+1941        }
+1942
+1943        PathBlocked::No
+1944    }
+1945
+1946    /// Send PATH_CHALLENGE for a previous path if necessary
+1947    ///
+1948    /// QUIC-TRANSPORT section 9.3.3
+1949    /// <https://www.rfc-editor.org/rfc/rfc9000.html#name-off-path-packet-forwarding>
+1950    fn send_prev_path_challenge(
+1951        &mut self,
+1952        now: Instant,
+1953        buf: &mut Vec<u8>,
+1954        path_id: PathId,
+1955    ) -> Option<Transmit> {
+1956        let (prev_cid, prev_path) = self.paths.get_mut(&path_id)?.prev.as_mut()?;
+1957        if !prev_path.pending_on_path_challenge {
+1958            return None;
+1959        };
+1960        prev_path.pending_on_path_challenge = false;
+1961        let token = self.rng.random();
+1962        let network_path = prev_path.network_path;
+1963        prev_path.record_path_challenge_sent(now, token, network_path);
+1964
+1965        debug_assert_eq!(
+1966            self.highest_space,
+1967            SpaceKind::Data,
+1968            "PATH_CHALLENGE queued without 1-RTT keys"
+1969        );
+1970        let buf = &mut TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into());
+1971        buf.start_new_datagram();
+1972
+1973        // Use the previous CID to avoid linking the new path with the previous path. We
+1974        // don't bother accounting for possible retirement of that prev_cid because this is
+1975        // sent once, immediately after migration, when the CID is known to be valid. Even
+1976        // if a post-migration packet caused the CID to be retired, it's fair to pretend
+1977        // this is sent first.
+1978        let mut builder = PacketBuilder::new(now, SpaceId::Data, path_id, *prev_cid, buf, self)?;
+1979        let challenge = frame::PathChallenge(token);
+1980        let stats = &mut self.path_stats.for_path(path_id).frame_tx;
+1981        builder.write_frame_with_log_msg(challenge, stats, Some("validating previous path"));
+1982
+1983        // An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame
+1984        // to at least the smallest allowed maximum datagram size of 1200 bytes,
+1985        // unless the anti-amplification limit for the path does not permit
+1986        // sending a datagram of this size
+1987        builder.pad_to(MIN_INITIAL_SIZE);
+1988
+1989        builder.finish(self, now);
+1990        self.path_stats
+1991            .for_path(path_id)
+1992            .udp_tx
+1993            .on_sent(1, buf.len());
+1994
+1995        trace!(
+1996            dst = ?network_path.remote,
+1997            src = ?network_path.local_ip,
+1998            len = buf.len(),
+1999            "sending prev_path off-path challenge",
+2000        );
+2001        Some(Transmit {
+2002            destination: network_path.remote,
+2003            size: buf.len(),
+2004            ecn: None,
+2005            segment_size: None,
+2006            src_ip: network_path.local_ip,
+2007        })
+2008    }
+2009
+2010    fn send_off_path_path_response(
+2011        &mut self,
+2012        now: Instant,
+2013        buf: &mut Vec<u8>,
+2014        path_id: PathId,
+2015    ) -> Option<Transmit> {
+2016        let path = self.paths.get_mut(&path_id).map(|state| &mut state.data)?;
+2017        let cid_queue = self.remote_cids.get_mut(&path_id)?;
+2018        let (token, network_path) = path.path_responses.pop_off_path(path.network_path)?;
+2019
+2020        // TODO: make off-path probes unlinkable.
+2021        let cid = cid_queue.active();
+2022
+2023        let frame = frame::PathResponse(token);
+2024
+2025        let buf = &mut TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into());
+2026        buf.start_new_datagram();
+2027
+2028        let mut builder = PacketBuilder::new(now, SpaceId::Data, path_id, cid, buf, self)?;
+2029        let stats = &mut self.path_stats.for_path(path_id).frame_tx;
+2030        builder.write_frame_with_log_msg(frame, stats, Some("(off-path)"));
+2031
+2032        // If we are a client doing NAT traversal, always include a PATH_CHALLENGE with any
+2033        // off-path PATH_RESPONSE. No need to schedule any retries for this, if NAT
+2034        // traversal is taking place then this remote already is being probed with
+2035        // retries, this only speeds up a successful traversal.
+2036        if self
+2037            .find_validated_path_on_network_path(network_path)
+2038            .is_none()
+2039            && self.n0_nat_traversal.client_side().is_ok()
+2040        {
+2041            let token = self.rng.random();
+2042            let stats = &mut self.path_stats.for_path(path_id).frame_tx;
+2043            builder.write_frame(frame::PathChallenge(token), stats);
+2044            let ip_port = (network_path.remote.ip(), network_path.remote.port());
+2045            self.n0_nat_traversal.mark_probe_sent(ip_port, token);
+2046        }
+2047
+2048        // Off-path: not tracked in congestion control. The packet is sent to a
+2049        // different destination than path_id's network path.
+2050        builder.pad_to(MIN_INITIAL_SIZE);
+2051        builder.finish(self, now);
+2052
+2053        let size = buf.len();
+2054        self.path_stats.for_path(path_id).udp_tx.on_sent(1, size);
+2055
+2056        trace!(
+2057            dst = ?network_path.remote,
+2058            src = ?network_path.local_ip,
+2059            len = buf.len(),
+2060            "sending off-path PATH_RESPONSE",
+2061        );
+2062        Some(Transmit {
+2063            destination: network_path.remote,
+2064            size,
+2065            ecn: None,
+2066            segment_size: None,
+2067            src_ip: network_path.local_ip,
+2068        })
+2069    }
+2070
+2071    /// Send a nat traversal challenge (off-path) on this path if possible.
+2072    fn send_nat_traversal_path_challenge(
+2073        &mut self,
+2074        now: Instant,
+2075        buf: &mut Vec<u8>,
+2076        path_id: PathId,
+2077    ) -> Option<Transmit> {
+2078        let remote = self.n0_nat_traversal.next_probe_addr()?;
+2079
+2080        if !self.paths.get(&path_id)?.data.validated {
+2081            // Path is not usable for probing
+2082            return None;
+2083        }
+2084
+2085        // TODO: Using the active CID here makes the paths linkable. This is a violation of
+2086        //    RFC9000 but something we want to accept in the short term. Eventually we aim
+2087        //    to fix up the supply of CIDs sufficiently so that we can keep paths unlinkable
+2088        //    again.
+2089        let Some(cid) = self
+2090            .remote_cids
+2091            .get(&path_id)
+2092            .map(|cid_queue| cid_queue.active())
+2093        else {
+2094            trace!(%path_id, "Not sending NAT traversal probe for path with no CIDs");
+2095            return None;
+2096        };
+2097        let token = self.rng.random();
+2098
+2099        let frame = frame::PathChallenge(token);
+2100
+2101        let mut buf = TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into());
+2102        buf.start_new_datagram();
+2103
+2104        let mut builder = PacketBuilder::new(now, SpaceId::Data, path_id, cid, &mut buf, self)?;
+2105        let stats = &mut self.path_stats.for_path(path_id).frame_tx;
+2106        builder.write_frame_with_log_msg(frame, stats, Some("(nat-traversal)"));
+2107        // Off-path: not tracked in congestion control. The packet is sent to a
+2108        // different destination than path_id's network path.
+2109        builder.finish(self, now);
+2110
+2111        // Mark as sent after packet build succeeds.
+2112        self.n0_nat_traversal.mark_probe_sent(remote, token);
+2113
+2114        let size = buf.len();
+2115        self.path_stats.for_path(path_id).udp_tx.on_sent(1, size);
+2116
+2117        trace!(dst = ?remote, len = buf.len(), "sending off-path NAT probe");
+2118        Some(Transmit {
+2119            destination: remote.into(),
+2120            size,
+2121            ecn: None,
+2122            segment_size: None,
+2123            src_ip: None,
+2124        })
+2125    }
+2126
+2127    /// Indicate what types of frames are ready to send for the given space.
+2128    ///
+2129    /// Only for on-path data.
+2130    ///
+2131    /// *packet_size* is the number of bytes available to build the next packet.
+2132    /// *connection_close_pending* indicates whether a CONNECTION_CLOSE frame needs to be
+2133    /// sent.
+2134    fn space_can_send(
+2135        &mut self,
+2136        space_id: SpaceId,
+2137        path_id: PathId,
+2138        packet_size: usize,
+2139        connection_close_pending: bool,
+2140    ) -> SendableFrames {
+2141        let space = &mut self.spaces[space_id];
+2142        let space_has_crypto = self.crypto_state.has_keys(space_id.encryption_level());
+2143
+2144        if !space_has_crypto
+2145            && (space_id != SpaceId::Data
+2146                || !self.crypto_state.has_keys(EncryptionLevel::ZeroRtt)
+2147                || self.side.is_server())
+2148        {
+2149            // Nothing to send in this space
+2150            return SendableFrames::empty();
+2151        }
+2152
+2153        let mut can_send = space.can_send(path_id, &self.streams);
+2154
+2155        // Check for 1RTT space.
+2156        if space_id == SpaceId::Data {
+2157            let pn = space.for_path(path_id).peek_tx_number();
+2158            // Number of bytes available for frames if this is a 1-RTT packet. We're
+2159            // guaranteed to be able to send an individual frame at least this large in the
+2160            // next 1-RTT packet. This could be generalized to support every space, but it's
+2161            // only needed to handle large fixed-size frames, which only exist in 1-RTT
+2162            // (application datagrams).
+2163            let frame_space_1rtt =
+2164                packet_size.saturating_sub(self.predict_1rtt_overhead(pn, path_id));
+2165            can_send |= self.can_send_1rtt(path_id, frame_space_1rtt);
+2166        }
+2167
+2168        can_send.close = connection_close_pending && space_has_crypto;
+2169
+2170        can_send
+2171    }
+2172
+2173    /// Process `ConnectionEvent`s generated by the associated `Endpoint`
+2174    ///
+2175    /// Will execute protocol logic upon receipt of a connection event, in turn preparing signals
+2176    /// (including application `Event`s, `EndpointEvent`s and outgoing datagrams) that should be
+2177    /// extracted through the relevant methods.
+2178    pub fn handle_event(&mut self, event: ConnectionEvent) {
+2179        use ConnectionEventInner::*;
+2180        match event.0 {
+2181            Datagram(DatagramConnectionEvent {
+2182                now,
+2183                network_path,
+2184                path_id,
+2185                ecn,
+2186                first_decode,
+2187                remaining,
+2188            }) => {
+2189                let span = trace_span!("pkt", %path_id);
+2190                let _guard = span.enter();
+2191
+2192                if self.early_discard_packet(network_path, path_id) {
+2193                    // A return value of true indicates we should discard this packet.
+2194                    return;
+2195                }
+2196
+2197                let was_anti_amplification_blocked = self
+2198                    .path(path_id)
+2199                    .map(|path| path.anti_amplification_blocked(1))
+2200                    // We never tried to send on an non-existing (new) path so have not been
+2201                    // anti-amplification blocked for it previously.
+2202                    .unwrap_or(false);
+2203
+2204                let rx = &mut self.path_stats.for_path(path_id).udp_rx;
+2205                rx.datagrams += 1;
+2206                rx.bytes += first_decode.len() as u64;
+2207                let data_len = first_decode.len();
+2208
+2209                self.handle_decode(now, network_path, path_id, ecn, first_decode);
+2210                // The current `path` might have changed inside `handle_decode` since the packet
+2211                // could have triggered a migration. The packet might also belong to an unknown
+2212                // path and have been rejected. Make sure the data received is accounted for the
+2213                // most recent path by accessing `path` after `handle_decode`.
+2214                if let Some(path) = self.path_mut(path_id) {
+2215                    path.inc_total_recvd(data_len as u64);
+2216                }
+2217
+2218                if let Some(data) = remaining {
+2219                    self.path_stats.for_path(path_id).udp_rx.bytes += data.len() as u64;
+2220                    self.handle_coalesced(now, network_path, path_id, ecn, data);
+2221                }
+2222
+2223                if let Some(path) = self.paths.get_mut(&path_id) {
+2224                    self.qlog
+2225                        .emit_recovery_metrics(path_id, &mut path.data, now);
+2226                }
+2227
+2228                if was_anti_amplification_blocked {
+2229                    // A prior attempt to set the loss detection timer may have failed due to
+2230                    // anti-amplification, so ensure it's set now. Prevents a handshake deadlock if
+2231                    // the server's first flight is lost.
+2232                    self.set_loss_detection_timer(now, path_id);
+2233                }
+2234            }
+2235            NewIdentifiers(ids, now, cid_len, cid_lifetime) => {
+2236                let path_id = ids.first().map(|issued| issued.path_id).unwrap_or_default();
+2237                debug_assert!(ids.iter().all(|issued| issued.path_id == path_id));
+2238
+2239                // Path may have been abandoned while this reply was in flight,
+2240                // retire the CIDs instead of queuing them.
+2241                if self.abandoned_paths.contains(&path_id) {
+2242                    if !self.state.is_drained() {
+2243                        for issued in &ids {
+2244                            self.endpoint_events
+2245                                .push_back(EndpointEventInner::RetireConnectionId(
+2246                                    now,
+2247                                    path_id,
+2248                                    issued.sequence,
+2249                                    false,
+2250                                ));
+2251                        }
+2252                    }
+2253                    return;
+2254                }
+2255
+2256                let cid_state = self
+2257                    .local_cid_state
+2258                    .entry(path_id)
+2259                    .or_insert_with(|| CidState::new(cid_len, cid_lifetime, now, 0));
+2260                cid_state.new_cids(&ids, now);
+2261
+2262                ids.into_iter().rev().for_each(|frame| {
+2263                    self.spaces[SpaceId::Data].pending.new_cids.push(frame);
+2264                });
+2265                // Always update Timer::PushNewCid
+2266                self.reset_cid_retirement(now);
+2267            }
+2268        }
+2269    }
+2270
+2271    /// Returns whether a packet can be discarded early.
+2272    ///
+2273    /// Packets sent on the wrong network path can be entirely ignored, saving further
+2274    /// processing.
+2275    ///
+2276    /// Returns true if a packet coming in for this `path_id` over given `network_path`
+2277    /// should be discarded.
+2278    fn early_discard_packet(&mut self, network_path: FourTuple, path_id: PathId) -> bool {
+2279        if self.is_handshaking() && path_id != PathId::ZERO {
+2280            debug!(%network_path, %path_id, "discarding multipath packet during handshake");
+2281            return true;
+2282        }
+2283
+2284        let peer_may_probe = self.peer_may_probe();
+2285        let local_ip_may_migrate = self.local_ip_may_migrate();
+2286
+2287        // If this packet could initiate a migration and we're a client or a server that
+2288        // forbids migration, drop the datagram. This could be relaxed to heuristically
+2289        // permit NAT-rebinding-like migration.
+2290        if let Some(known_path) = self.path_mut(path_id) {
+2291            if network_path.remote != known_path.network_path.remote && !peer_may_probe {
+2292                trace!(
+2293                    %path_id,
+2294                    %network_path,
+2295                    %known_path.network_path,
+2296                    "discarding packet from unrecognized peer"
+2297                );
+2298                return true;
+2299            }
+2300
+2301            if known_path.network_path.local_ip.is_some()
+2302                && network_path.local_ip.is_some()
+2303                && known_path.network_path.local_ip != network_path.local_ip
+2304                && !local_ip_may_migrate
+2305            {
+2306                trace!(
+2307                    %path_id,
+2308                    %network_path,
+2309                    %known_path.network_path,
+2310                    "discarding packet sent to incorrect interface"
+2311                );
+2312                return true;
+2313            }
+2314        }
+2315        false
+2316    }
+2317
+2318    /// Whether the peer may probe new paths.
+2319    ///
+2320    /// RFC9000 §9 and QNT both have probing packets which may arrive from new paths. This
+2321    /// indicates whether these are allowed or not. This is a strict superset from
+2322    /// [`Self::peer_may_migrate`]: every network path that may be migrated to, may also
+2323    /// be probed. But e.g. servers may not migrate, but can be allowed to probe.
+2324    // TODO(flub): In RFC9000 the server is allowed to send off-path probing packets
+2325    //    once the client has been probing such a 4-tuple. These probes are currently
+2326    //    not yet recognised and will end up being discarded because of this.
+2327    //    See https://github.com/n0-computer/noq/issues/607.
+2328    fn peer_may_probe(&self) -> bool {
+2329        match &self.side {
+2330            ConnectionSide::Client { .. } => {
+2331                if let Some(hs) = self.state.as_handshake() {
+2332                    hs.allow_server_migration
+2333                } else {
+2334                    self.n0_nat_traversal.is_negotiated() && self.is_handshake_confirmed()
+2335                }
+2336            }
+2337            ConnectionSide::Server { server_config } => {
+2338                self.is_handshake_confirmed()
+2339                    && (server_config.migration || self.n0_nat_traversal.is_negotiated())
+2340            }
+2341        }
+2342    }
+2343
+2344    /// Whether the peer's remote address may migrate.
+2345    ///
+2346    /// In RFC9000 only the client may migrate.
+2347    ///
+2348    /// QUIC relies on stable endpoints during the handshake. So other than the server's
+2349    /// preferred_address transport parameter no side may migrate before the handshake is
+2350    /// completed.
+2351    ///
+2352    /// It is noteworthy that for iroh we allow server migrations during the handshake when
+2353    /// [`state::Handshake::allow_server_migration`] is enabled, but that is handled earlier
+2354    /// in [`Self::handle_packet`] and without probing the current and previous paths.
+2355    fn peer_may_migrate(&self) -> bool {
+2356        match &self.side {
+2357            ConnectionSide::Server { server_config } => {
+2358                server_config.migration && self.is_handshake_confirmed()
+2359            }
+2360            ConnectionSide::Client { .. } => false,
+2361        }
+2362    }
+2363
+2364    /// Whether our local IP address is allowed to change with new incoming packets.
+2365    ///
+2366    /// Incoming packets show us the local IP address we received a packet on, which could
+2367    /// be different from what we thought due to e.g. NAT rebinding or moving from mobile
+2368    /// data to WiFi without being notified of the network change.
+2369    ///
+2370    /// This is only allowed to happen after the handshake is confirmed and when we are the
+2371    /// client. Unless QNT is negotiated in which case the server is also allowed to
+2372    /// migrate.
+2373    ///
+2374    /// Be aware that probing packets, which do not exist in Multipath without QNT, are
+2375    /// exempt from this.
+2376    fn local_ip_may_migrate(&self) -> bool {
+2377        (self.side.is_client() || self.n0_nat_traversal.is_negotiated())
+2378            && self.is_handshake_confirmed()
+2379    }
+2380    /// Process timer expirations
+2381    ///
+2382    /// Executes protocol logic, potentially preparing signals (including application `Event`s,
+2383    /// `EndpointEvent`s and outgoing datagrams) that should be extracted through the relevant
+2384    /// methods.
+2385    ///
+2386    /// It is most efficient to call this immediately after the system clock reaches the latest
+2387    /// `Instant` that was output by `poll_timeout`; however spurious extra calls will simply
+2388    /// no-op and therefore are safe.
+2389    pub fn handle_timeout(&mut self, now: Instant) {
+2390        while let Some((timer, _time)) = self.timers.expire_before(now, &self.qlog) {
+2391            let span = match timer {
+2392                Timer::Conn(timer) => trace_span!("timeout", scope = "conn", ?timer),
+2393                Timer::PerPath(path_id, timer) => {
+2394                    trace_span!("timer_fired", scope="path", %path_id, ?timer)
+2395                }
+2396            };
+2397            let _guard = span.enter();
+2398            trace!("timeout");
+2399            match timer {
+2400                Timer::Conn(timer) => match timer {
+2401                    ConnTimer::Close => {
+2402                        let was_draining = self.state.move_to_drained(None);
+2403                        if !was_draining {
+2404                            self.endpoint_events.push_back(EndpointEventInner::Draining);
+2405                        }
+2406                        // move_to_drained checks that we weren't in drained before.
+2407                        // Adding events to endpoint_events is only legal if `Drained` was never queued before.
+2408                        self.endpoint_events.push_back(EndpointEventInner::Drained);
+2409                    }
+2410                    ConnTimer::Idle => {
+2411                        self.kill(ConnectionError::TimedOut);
+2412                    }
+2413                    ConnTimer::KeepAlive => {
+2414                        self.ping();
+2415                    }
+2416                    ConnTimer::KeyDiscard => {
+2417                        self.crypto_state.discard_temporary_keys();
+2418                    }
+2419                    ConnTimer::PushNewCid => {
+2420                        while let Some((path_id, when)) = self.next_cid_retirement() {
+2421                            if when > now {
+2422                                break;
+2423                            }
+2424                            match self.local_cid_state.get_mut(&path_id) {
+2425                                None => error!(%path_id, "No local CID state for path"),
+2426                                Some(cid_state) => {
+2427                                    // Update `retire_prior_to` field in NEW_CONNECTION_ID frame
+2428                                    let num_new_cid = cid_state.on_cid_timeout().into();
+2429                                    if !self.state.is_closed() {
+2430                                        trace!(
+2431                                            "push a new CID to peer RETIRE_PRIOR_TO field {}",
+2432                                            cid_state.retire_prior_to()
+2433                                        );
+2434                                        self.endpoint_events.push_back(
+2435                                            EndpointEventInner::NeedIdentifiers(
+2436                                                path_id,
+2437                                                now,
+2438                                                num_new_cid,
+2439                                            ),
+2440                                        );
+2441                                    }
+2442                                }
+2443                            }
+2444                        }
+2445                    }
+2446                    ConnTimer::NoAvailablePath => {
+2447                        // Grace period expired: all paths were abandoned and no new path
+2448                        // was opened. Close the connection. There are no paths left to
+2449                        // send CONNECTION_CLOSE on, so this is a silent close.
+2450                        // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-21.html#section-3.4-8
+2451                        if self.state.is_closed() || self.state.is_drained() {
+2452                            // Connection already closing/drained (e.g. application called
+2453                            // close() before the grace timer fired). Nothing to do.
+2454                            error!("no viable path timer fired, but connection already closing");
+2455                        } else {
+2456                            trace!("no viable path grace period expired, closing connection");
+2457                            let err = TransportError::NO_VIABLE_PATH(
+2458                                "last path abandoned, no new path opened",
+2459                            );
+2460                            self.close_common();
+2461                            self.set_close_timer(now);
+2462                            self.connection_close_pending = true;
+2463                            self.state.move_to_closed(err);
+2464                        }
+2465                    }
+2466                    ConnTimer::NatTraversalProbeRetry => {
+2467                        self.n0_nat_traversal.queue_retries(self.is_ipv6());
+2468                        if let Some(delay) =
+2469                            self.n0_nat_traversal.retry_delay(self.config.initial_rtt)
+2470                        {
+2471                            self.timers.set(
+2472                                Timer::Conn(ConnTimer::NatTraversalProbeRetry),
+2473                                now + delay,
+2474                                self.qlog.with_time(now),
+2475                            );
+2476                            trace!("re-queued NAT probes");
+2477                        } else {
+2478                            trace!("no more NAT probes remaining");
+2479                        }
+2480                    }
+2481                },
+2482                Timer::PerPath(path_id, timer) => {
+2483                    match timer {
+2484                        PathTimer::PathIdle => {
+2485                            if let Err(err) =
+2486                                self.close_path_inner(now, path_id, PathAbandonReason::TimedOut)
+2487                            {
+2488                                warn!(?err, "failed closing path");
+2489                            }
+2490                        }
+2491
+2492                        PathTimer::PathKeepAlive => {
+2493                            self.ping_path(path_id).ok();
+2494                        }
+2495                        PathTimer::LossDetection => {
+2496                            self.on_loss_detection_timeout(now, path_id);
+2497                            self.qlog.emit_recovery_metrics(
+2498                                path_id,
+2499                                &mut self
+2500                                    .paths
+2501                                    .get_mut(&path_id)
+2502                                    .expect("loss-detection timer fires only on live paths")
+2503                                    .data,
+2504                                now,
+2505                            );
+2506                        }
+2507                        PathTimer::PathValidationFailed => {
+2508                            let Some(path) = self.paths.get_mut(&path_id) else {
+2509                                continue;
+2510                            };
+2511                            self.timers.stop(
+2512                                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
+2513                                self.qlog.with_time(now),
+2514                            );
+2515                            debug!("path migration validation failed");
+2516                            if let Some((_, prev)) = path.prev.take() {
+2517                                path.data = prev;
+2518                            }
+2519                            path.data.reset_on_path_challenges();
+2520                        }
+2521                        PathTimer::PathChallengeLost => {
+2522                            let Some(path) = self.paths.get_mut(&path_id) else {
+2523                                continue;
+2524                            };
+2525                            trace!("path challenge deemed lost");
+2526                            path.data.pending_on_path_challenge = true;
+2527                        }
+2528                        PathTimer::AbandonFromValidation => {
+2529                            let Some(path) = self.paths.get_mut(&path_id) else {
+2530                                continue;
+2531                            };
+2532                            path.data.reset_on_path_challenges();
+2533                            self.timers.stop(
+2534                                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
+2535                                self.qlog.with_time(now),
+2536                            );
+2537                            debug!("new path validation failed");
+2538                            if let Err(err) = self.close_path_inner(
+2539                                now,
+2540                                path_id,
+2541                                PathAbandonReason::ValidationFailed,
+2542                            ) {
+2543                                warn!(?err, "failed closing path");
+2544                            }
+2545                        }
+2546                        PathTimer::Pacing => {}
+2547                        PathTimer::MaxAckDelay => {
+2548                            // This timer is only armed in the Data space
+2549                            self.spaces[SpaceId::Data]
+2550                                .for_path(path_id)
+2551                                .pending_acks
+2552                                .on_max_ack_delay_timeout()
+2553                        }
+2554                        PathTimer::PathDrained => {
+2555                            // The path was abandoned and 3*PTO has expired since.  Clean up all
+2556                            // remaining state and install stateless reset token.
+2557                            self.timers.stop_per_path(path_id, self.qlog.with_time(now));
+2558                            if let Some(local_cid_state) = self.local_cid_state.remove(&path_id) {
+2559                                debug_assert!(!self.state.is_drained()); // requirement for endpoint_events. All timers should be cleared in drained connections.
+2560                                let (min_seq, max_seq) = local_cid_state.active_seq();
+2561                                for seq in min_seq..=max_seq {
+2562                                    self.endpoint_events.push_back(
+2563                                        EndpointEventInner::RetireConnectionId(
+2564                                            now, path_id, seq, false,
+2565                                        ),
+2566                                    );
+2567                                }
+2568                            }
+2569                            self.discard_path(path_id, now);
+2570                        }
+2571                    }
+2572                }
+2573            }
+2574        }
+2575    }
+2576
+2577    /// Close a connection immediately
+2578    ///
+2579    /// This does not ensure delivery of outstanding data. It is the application's responsibility to
+2580    /// call this only when all important communications have been completed, e.g. by calling
+2581    /// [`SendStream::finish`] on outstanding streams and waiting for the corresponding
+2582    /// [`StreamEvent::Finished`] event.
+2583    ///
+2584    /// If [`Streams::send_streams`] returns 0, all outstanding stream data has been
+2585    /// delivered. There may still be data from the peer that has not been received.
+2586    ///
+2587    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+2588    pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes) {
+2589        self.close_inner(
+2590            now,
+2591            Close::Application(frame::ApplicationClose { error_code, reason }),
+2592        )
+2593    }
+2594
+2595    /// Close the connection immediately, initiated by an API call.
+2596    ///
+2597    /// This will not produce a [`ConnectionLost`] event propagated by the
+2598    /// [`Connection::poll`] call, because the API call already propagated the error to the
+2599    /// user.
+2600    ///
+2601    /// Not to be used when entering immediate close due to an internal state change based
+2602    /// on an event. See [`State::move_to_closed_local`] for details.
+2603    ///
+2604    /// This initiates immediate close from
+2605    /// <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2>, moving to the closed
+2606    /// state.
+2607    ///
+2608    /// [`ConnectionLost`]: crate::Event::ConnectionLost
+2609    /// [`Connection::poll`]: super::Connection::poll
+2610    fn close_inner(&mut self, now: Instant, reason: Close) {
+2611        let was_closed = self.state.is_closed();
+2612        if !was_closed {
+2613            self.close_common();
+2614            self.set_close_timer(now);
+2615            self.connection_close_pending = true;
+2616            self.state.move_to_closed_local(reason);
+2617        }
+2618    }
+2619
+2620    /// Control datagrams
+2621    pub fn datagrams(&mut self) -> Datagrams<'_> {
+2622        Datagrams { conn: self }
+2623    }
+2624
+2625    /// Returns connection statistics
+2626    pub fn stats(&mut self) -> ConnectionStats {
+2627        let mut stats = self.partial_stats.clone();
+2628
+2629        for path_stats in self.path_stats.iter_stats() {
+2630            // Self::path_stats() computes the path rtt, cwnd and current_mtu on access
+2631            // because they are not simple counters. When computing the connection stats we
+2632            // can skip that effort since those fields are not used in the `impl
+2633            // Add<PathStats> for ConnectionStats`.
+2634            stats += *path_stats;
+2635        }
+2636
+2637        stats
+2638    }
+2639
+2640    /// Returns path statistics
+2641    pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats> {
+2642        let path = self.paths.get(&path_id)?;
+2643        let stats = self.path_stats.for_path(path_id);
+2644        stats.rtt = path.data.rtt.get();
+2645        stats.cwnd = path.data.congestion.window();
+2646        stats.current_mtu = path.data.mtud.current_mtu();
+2647        Some(*stats)
+2648    }
+2649
+2650    /// Ping the remote endpoint
+2651    ///
+2652    /// Causes an ACK-eliciting packet to be transmitted on the connection.
+2653    pub fn ping(&mut self) {
+2654        // TODO(flub): This is very brute-force: it pings *all* the paths.  Instead it would
+2655        //    be nice if we could only send a single packet for this.
+2656        for path_data in self.spaces[self.highest_space].number_spaces.values_mut() {
+2657            path_data.ping_pending = true;
+2658        }
+2659    }
+2660
+2661    /// Ping the remote endpoint over a specific path
+2662    ///
+2663    /// Causes an ACK-eliciting packet to be transmitted on the path.
+2664    pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath> {
+2665        let path_data = self.spaces[self.highest_space]
+2666            .number_spaces
+2667            .get_mut(&path)
+2668            .ok_or(ClosedPath { _private: () })?;
+2669        path_data.ping_pending = true;
+2670        Ok(())
+2671    }
+2672
+2673    /// Update traffic keys spontaneously
+2674    ///
+2675    /// This can be useful for testing key updates, as they otherwise only happen infrequently.
+2676    pub fn force_key_update(&mut self) {
+2677        if !self.state.is_established() {
+2678            debug!("ignoring forced key update in illegal state");
+2679            return;
+2680        }
+2681        if self.crypto_state.prev_crypto.is_some() {
+2682            // We already just updated, or are currently updating, the keys. Concurrent key updates
+2683            // are illegal.
+2684            debug!("ignoring redundant forced key update");
+2685            return;
+2686        }
+2687        self.crypto_state.update_keys(None, false);
+2688    }
+2689
+2690    /// Get a session reference
+2691    pub fn crypto_session(&self) -> &dyn crypto::Session {
+2692        self.crypto_state.session.as_ref()
+2693    }
+2694
+2695    /// Whether the connection is in the process of being established
+2696    ///
+2697    /// If this returns `false`, the connection may be either established or closed, signaled by the
+2698    /// emission of a [`Connected`](Event::Connected) or [`ConnectionLost`](Event::ConnectionLost)
+2699    /// event respectively. Note that locally-initiated closes via [`close()`](Self::close) do not
+2700    /// emit a `ConnectionLost` event.
+2701    ///
+2702    /// For an established connection this essentially means the handshake is **completed**,
+2703    /// but not necessarily yet confirmed.
+2704    pub fn is_handshaking(&self) -> bool {
+2705        self.state.is_handshake()
+2706    }
+2707
+2708    /// Whether the connection is closed
+2709    ///
+2710    /// Closed connections cannot transport any further data. A connection becomes closed when
+2711    /// either peer application intentionally closes it, or when either transport layer detects an
+2712    /// error such as a time-out or certificate validation failure.
+2713    ///
+2714    /// A [`ConnectionLost`](Event::ConnectionLost) event is emitted with details when the
+2715    /// connection is closed by the peer or due to an error. When the local application closes
+2716    /// the connection via [`close()`](Self::close), no `ConnectionLost` event is emitted;
+2717    /// instead, pending operations fail with [`ConnectionError::LocallyClosed`].
+2718    pub fn is_closed(&self) -> bool {
+2719        self.state.is_closed()
+2720    }
+2721
+2722    /// Whether there is no longer any need to keep the connection around
+2723    ///
+2724    /// Closed connections become drained after a brief timeout to absorb any remaining in-flight
+2725    /// packets from the peer. All drained connections have been closed.
+2726    pub fn is_drained(&self) -> bool {
+2727        self.state.is_drained()
+2728    }
+2729
+2730    /// For clients, if the peer accepted the 0-RTT data packets
+2731    ///
+2732    /// The value is meaningless until after the handshake completes.
+2733    pub fn accepted_0rtt(&self) -> bool {
+2734        self.crypto_state.accepted_0rtt
+2735    }
+2736
+2737    /// Whether 0-RTT is/was possible during the handshake
+2738    pub fn has_0rtt(&self) -> bool {
+2739        self.crypto_state.zero_rtt_enabled
+2740    }
+2741
+2742    /// Whether there are any pending retransmits
+2743    pub fn has_pending_retransmits(&self) -> bool {
+2744        !self.spaces[SpaceId::Data].pending.is_empty(&self.streams)
+2745    }
+2746
+2747    /// Look up whether we're the client or server of this Connection
+2748    pub fn side(&self) -> Side {
+2749        self.side.side()
+2750    }
+2751
+2752    /// Get the address observed by the remote over the given path
+2753    pub fn path_observed_address(&self, path_id: PathId) -> Result<Option<SocketAddr>, ClosedPath> {
+2754        self.path(path_id)
+2755            .map(|path_data| {
+2756                path_data
+2757                    .last_observed_addr_report
+2758                    .as_ref()
+2759                    .map(|observed| observed.socket_addr())
+2760            })
+2761            .ok_or(ClosedPath { _private: () })
+2762    }
+2763
+2764    /// Current best estimate of this connection's latency (round-trip-time)
+2765    pub fn rtt(&self, path_id: PathId) -> Option<Duration> {
+2766        self.path(path_id).map(|d| d.rtt.get())
+2767    }
+2768
+2769    /// Current state of this connection's congestion controller, for debugging purposes
+2770    pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller> {
+2771        self.path(path_id).map(|d| d.congestion.as_ref())
+2772    }
+2773
+2774    /// Modify the number of remotely initiated streams that may be concurrently open
+2775    ///
+2776    /// No streams may be opened by the peer unless fewer than `count` are already open. Large
+2777    /// `count`s increase both minimum and worst-case memory consumption.
+2778    pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt) {
+2779        self.streams.set_max_concurrent(dir, count);
+2780        // If the limit was reduced, then a flow control update previously deemed insignificant may
+2781        // now be significant.
+2782        let pending = &mut self.spaces[SpaceId::Data].pending;
+2783        self.streams.queue_max_stream_id(pending);
+2784    }
+2785
+2786    /// Modify the number of open paths allowed when multipath is enabled
+2787    ///
+2788    /// When reducing the number of concurrent paths this will only affect delaying sending
+2789    /// new MAX_PATH_ID frames until fewer than this number of paths are possible.  To
+2790    /// actively reduce paths they must be closed using [`Connection::close_path`], which
+2791    /// can also be used to close not-yet-opened paths.
+2792    ///
+2793    /// If multipath is not negotiated (see the [`TransportConfig`]) this can not enable
+2794    /// multipath and will fail.
+2795    pub fn set_max_concurrent_paths(
+2796        &mut self,
+2797        now: Instant,
+2798        count: NonZeroU32,
+2799    ) -> Result<(), MultipathNotNegotiated> {
+2800        if !self.is_multipath_negotiated() {
+2801            return Err(MultipathNotNegotiated { _private: () });
+2802        }
+2803        self.max_concurrent_paths = count;
+2804
+2805        let in_use_count = self
+2806            .local_max_path_id
+2807            .next()
+2808            .saturating_sub(self.abandoned_paths.len() as u32)
+2809            .as_u32();
+2810        let extra_needed = count.get().saturating_sub(in_use_count);
+2811        let new_max_path_id = self.local_max_path_id.saturating_add(extra_needed);
+2812
+2813        self.set_max_path_id(now, new_max_path_id);
+2814
+2815        Ok(())
+2816    }
+2817
+2818    /// If needed, issues a new MAX_PATH_ID frame and new CIDs for any newly allowed paths
+2819    fn set_max_path_id(&mut self, now: Instant, max_path_id: PathId) {
+2820        if max_path_id <= self.local_max_path_id {
+2821            return;
+2822        }
+2823
+2824        self.local_max_path_id = max_path_id;
+2825        self.spaces[SpaceId::Data].pending.max_path_id = true;
+2826
+2827        self.issue_first_path_cids(now);
+2828    }
+2829
+2830    /// Current number of remotely initiated streams that may be concurrently open
+2831    ///
+2832    /// If the target for this limit is reduced using [`set_max_concurrent_streams`](Self::set_max_concurrent_streams),
+2833    /// it will not change immediately, even if fewer streams are open. Instead, it will
+2834    /// decrement by one for each time a remotely initiated stream of matching directionality is closed.
+2835    pub fn max_concurrent_streams(&self, dir: Dir) -> u64 {
+2836        self.streams.max_concurrent(dir)
+2837    }
+2838
+2839    /// See [`TransportConfig::send_window()`]
+2840    pub fn set_send_window(&mut self, send_window: u64) {
+2841        self.streams.set_send_window(send_window);
+2842    }
+2843
+2844    /// See [`TransportConfig::receive_window()`]
+2845    pub fn set_receive_window(&mut self, receive_window: VarInt) {
+2846        if self.streams.set_receive_window(receive_window) {
+2847            self.spaces[SpaceId::Data].pending.max_data = true;
+2848        }
+2849    }
+2850
+2851    /// Whether the Multipath for QUIC extension is enabled.
+2852    ///
+2853    /// Multipath is only enabled after the handshake is completed and if it was enabled by both
+2854    /// peers.
+2855    pub fn is_multipath_negotiated(&self) -> bool {
+2856        !self.is_handshaking()
+2857            && self.config.max_concurrent_multipath_paths.is_some()
+2858            && self.peer_params.initial_max_path_id.is_some()
+2859    }
+2860
+2861    fn on_ack_received(
+2862        &mut self,
+2863        now: Instant,
+2864        space: SpaceId,
+2865        ack: frame::Ack,
+2866    ) -> Result<(), TransportError> {
+2867        // All ACKs are referencing path 0
+2868        let path = PathId::ZERO;
+2869        self.inner_on_ack_received(now, space, path, ack)
+2870    }
+2871
+2872    fn on_path_ack_received(
+2873        &mut self,
+2874        now: Instant,
+2875        space: SpaceId,
+2876        path_ack: frame::PathAck,
+2877    ) -> Result<(), TransportError> {
+2878        let (ack, path) = path_ack.into_ack();
+2879        self.inner_on_ack_received(now, space, path, ack)
+2880    }
+2881
+2882    /// Handles an ACK frame acknowledging packets sent on *path*.
+2883    fn inner_on_ack_received(
+2884        &mut self,
+2885        now: Instant,
+2886        space: SpaceId,
+2887        path: PathId,
+2888        ack: frame::Ack,
+2889    ) -> Result<(), TransportError> {
+2890        if !self.spaces[space].number_spaces.contains_key(&path) {
+2891            if self.abandoned_paths.contains(&path) {
+2892                // See also
+2893                // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-21.html#section-3.4.3-3
+2894                // > When an endpoint finally deletes all state associated with the path [...]
+2895                // > PATH_ACK frames received with an abandoned path ID are silently ignored,
+2896                // > as specified in Section 4.
+2897                trace!("silently ignoring PATH_ACK on discarded path");
+2898                return Ok(());
+2899            } else {
+2900                return Err(TransportError::PROTOCOL_VIOLATION(
+2901                    "received PATH_ACK with path ID never used",
+2902                ));
+2903            }
+2904        }
+2905        if ack.largest >= self.spaces[space].for_path(path).next_packet_number {
+2906            return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked"));
+2907        }
+2908        // `Some(pn)` if this ACK raised `largest_acked_packet_pn`.
+2909        let new_largest_pn = {
+2910            let space = &mut self.spaces[space].for_path(path);
+2911            if space
+2912                .largest_acked_packet_pn
+2913                .is_none_or(|pn| ack.largest > pn)
+2914            {
+2915                space.largest_acked_packet_pn = Some(ack.largest);
+2916                if let Some(info) = space.sent_packets.get(ack.largest) {
+2917                    // This should always succeed, but a misbehaving peer might ACK a packet we
+2918                    // haven't sent. At worst, that will result in us spuriously reducing the
+2919                    // congestion window.
+2920                    space.largest_acked_packet_send_time = info.time_sent;
+2921                }
+2922                Some(ack.largest)
+2923            } else {
+2924                None
+2925            }
+2926        };
+2927
+2928        if self.detect_spurious_loss(&ack, space, path) {
+2929            self.path_stats.for_path(path).spurious_congestion_events += 1;
+2930            self.path_data_mut(path)
+2931                .congestion
+2932                .on_spurious_congestion_event();
+2933        }
+2934
+2935        // Avoid DoS from unreasonably huge ack ranges by filtering out just the new acks.
+2936        let mut newly_acked = ArrayRangeSet::new();
+2937        for range in ack.iter() {
+2938            self.spaces[space].for_path(path).check_ack(range.clone())?;
+2939            for (pn, _) in self.spaces[space]
+2940                .for_path(path)
+2941                .sent_packets
+2942                .iter_range(range)
+2943            {
+2944                newly_acked.insert_one(pn);
+2945            }
+2946        }
+2947
+2948        if newly_acked.is_empty() {
+2949            return Ok(());
+2950        }
+2951
+2952        let mut ack_eliciting_acked = false;
+2953        for packet in newly_acked.elts() {
+2954            if let Some(info) = self.spaces[space].for_path(path).take(packet) {
+2955                for (acked_path_id, acked_pn) in info.largest_acked.iter() {
+2956                    // Assume ACKs for all packets below the largest acknowledged in
+2957                    // `packet` have been received. This can cause the peer to spuriously
+2958                    // retransmit if some of our earlier ACKs were lost, but allows for
+2959                    // simpler state tracking. See discussion at
+2960                    // https://www.rfc-editor.org/rfc/rfc9000.html#name-limiting-ranges-by-tracking
+2961                    if let Some(pns) = self.spaces[space].path_space_mut(*acked_path_id) {
+2962                        pns.pending_acks.subtract_below(*acked_pn);
+2963                    }
+2964                }
+2965                ack_eliciting_acked |= info.ack_eliciting;
+2966
+2967                // Notify MTU discovery that a packet was acked, because it might be an MTU probe
+2968                let path_data = self.path_data_mut(path);
+2969                let mtu_updated = path_data.mtud.on_acked(space.kind(), packet, info.size);
+2970                if mtu_updated {
+2971                    path_data
+2972                        .congestion
+2973                        .on_mtu_update(path_data.mtud.current_mtu());
+2974                }
+2975
+2976                // Notify ack frequency that a packet was acked, because it might contain an ACK_FREQUENCY frame
+2977                self.ack_frequency.on_acked(path, packet);
+2978
+2979                self.on_packet_acked(now, path, packet, info);
+2980            }
+2981        }
+2982
+2983        let largest_ackd = self.spaces[space].for_path(path).largest_acked_packet_pn;
+2984        let path_data = self.path_data_mut(path);
+2985        let app_limited = path_data.app_limited;
+2986        let in_flight = path_data.in_flight.bytes;
+2987
+2988        path_data
+2989            .congestion
+2990            .on_end_acks(now, in_flight, app_limited, largest_ackd);
+2991
+2992        if new_largest_pn.is_some() && ack_eliciting_acked {
+2993            let ack_delay = if space != SpaceId::Data {
+2994                Duration::from_micros(0)
+2995            } else {
+2996                cmp::min(
+2997                    self.ack_frequency.peer_max_ack_delay,
+2998                    Duration::from_micros(ack.delay << self.peer_params.ack_delay_exponent.0),
+2999                )
+3000            };
+3001            let rtt = now.saturating_duration_since(
+3002                self.spaces[space]
+3003                    .for_path(path)
+3004                    .largest_acked_packet_send_time,
+3005            );
+3006
+3007            let next_pn = self.spaces[space].for_path(path).next_packet_number;
+3008            let path_data = self.path_data_mut(path);
+3009            // TODO(@divma): should be a method of path, should be contained in a single place
+3010            path_data.rtt.update(ack_delay, rtt);
+3011            if path_data.first_packet_after_rtt_sample.is_none() {
+3012                path_data.first_packet_after_rtt_sample = Some((space.kind(), next_pn));
+3013            }
+3014        }
+3015
+3016        // Must be called before crypto/pto_count are clobbered
+3017        self.detect_lost_packets(now, space, path, true);
+3018
+3019        // If the peer did not complete the handshake address validation the ACK could be
+3020        // spoofed, e.g. in the Initial space. Setting the pto_count back to 0 removes the
+3021        // exponential backoff from the PTO timer and would result in too many tail-loss
+3022        // probes being sent.
+3023        if self.peer_completed_handshake_address_validation() {
+3024            self.path_data_mut(path).pto_count = 0;
+3025        }
+3026
+3027        // Explicit congestion notification
+3028        // TODO(@divma): this code is a good example of logic that should be contained in a single
+3029        // place but it's split between the path data and the packet number space data, we should
+3030        // find a way to make this work without two lookups
+3031        if self.path_data(path).sending_ecn {
+3032            if let Some(ecn) = ack.ecn {
+3033                // We only examine ECN counters from ACKs that we are certain we received in transmit
+3034                // order, allowing us to compute an increase in ECN counts to compare against the number
+3035                // of newly acked packets that remains well-defined in the presence of arbitrary packet
+3036                // reordering.
+3037                if let Some(largest_sent_pn) = new_largest_pn {
+3038                    let sent = self.spaces[space]
+3039                        .for_path(path)
+3040                        .largest_acked_packet_send_time;
+3041                    self.process_ecn(
+3042                        now,
+3043                        space,
+3044                        path,
+3045                        newly_acked.len() as u64,
+3046                        ecn,
+3047                        sent,
+3048                        largest_sent_pn,
+3049                    );
+3050                }
+3051            } else {
+3052                // We always start out sending ECN, so any ack that doesn't acknowledge it disables it.
+3053                debug!("ECN not acknowledged by peer");
+3054                self.path_data_mut(path).sending_ecn = false;
+3055            }
+3056        }
+3057
+3058        self.set_loss_detection_timer(now, path);
+3059        Ok(())
+3060    }
+3061
+3062    fn detect_spurious_loss(&mut self, ack: &frame::Ack, space: SpaceId, path: PathId) -> bool {
+3063        let lost_packets = &mut self.spaces[space].for_path(path).lost_packets;
+3064
+3065        if lost_packets.is_empty() {
+3066            return false;
+3067        }
+3068
+3069        for range in ack.iter() {
+3070            let spurious_losses: Vec<u64> = lost_packets
+3071                .iter_range(range.clone())
+3072                .map(|(pn, _info)| pn)
+3073                .collect();
+3074
+3075            for pn in spurious_losses {
+3076                lost_packets.remove(pn);
+3077            }
+3078        }
+3079
+3080        // If this ACK frame acknowledged all deemed lost packets,
+3081        // then we have raised a spurious congestion event in the past.
+3082        // We cannot conclude when there are remaining packets,
+3083        // but future ACK frames might indicate a spurious loss detection.
+3084        lost_packets.is_empty()
+3085    }
+3086
+3087    /// Drain lost packets that we reasonably think will never arrive
+3088    ///
+3089    /// The current criterion is copied from `msquic`:
+3090    /// discard packets that were sent earlier than 2 probe timeouts ago.
+3091    fn drain_lost_packets(&mut self, now: Instant, space: SpaceId, path: PathId) {
+3092        let two_pto = 2 * self.path_data(path).rtt.pto_base();
+3093
+3094        let lost_packets = &mut self.spaces[space].for_path(path).lost_packets;
+3095        lost_packets.retain(|_pn, info| now.saturating_duration_since(info.time_sent) <= two_pto);
+3096    }
+3097
+3098    /// Process a new ECN block from an in-order ACK
+3099    fn process_ecn(
+3100        &mut self,
+3101        now: Instant,
+3102        space: SpaceId,
+3103        path: PathId,
+3104        newly_acked_pn: u64,
+3105        ecn: frame::EcnCounts,
+3106        largest_sent_time: Instant,
+3107        largest_sent_pn: u64,
+3108    ) {
+3109        match self.spaces[space]
+3110            .for_path(path)
+3111            .detect_ecn(newly_acked_pn, ecn)
+3112        {
+3113            Err(e) => {
+3114                debug!("halting ECN due to verification failure: {}", e);
+3115
+3116                self.path_data_mut(path).sending_ecn = false;
+3117                // Wipe out the existing value because it might be garbage and could interfere with
+3118                // future attempts to use ECN on new paths.
+3119                self.spaces[space].for_path(path).ecn_feedback = frame::EcnCounts::ZERO;
+3120            }
+3121            Ok(false) => {}
+3122            Ok(true) => {
+3123                self.path_stats.for_path(path).congestion_events += 1;
+3124                self.path_data_mut(path).congestion.on_congestion_event(
+3125                    now,
+3126                    largest_sent_time,
+3127                    false,
+3128                    true,
+3129                    0,
+3130                    largest_sent_pn,
+3131                );
+3132            }
+3133        }
+3134    }
+3135
+3136    // Not timing-aware, so it's safe to call this for inferred acks, such as arise from
+3137    // high-latency handshakes
+3138    fn on_packet_acked(&mut self, now: Instant, path_id: PathId, pn: u64, info: SentPacket) {
+3139        let path = self.path_data_mut(path_id);
+3140        let app_limited = path.app_limited;
+3141        path.remove_in_flight(&info);
+3142        if info.ack_eliciting && info.path_generation == path.generation() {
+3143            // Only pass ACKs to the congestion controller if it belongs to this exact
+3144            // generation of the path. Otherwise we might be feeding ACKs from the previous
+3145            // 4-tuple into our congestion controller.
+3146            let rtt = path.rtt;
+3147            path.congestion
+3148                .on_ack(now, info.time_sent, info.size.into(), pn, app_limited, &rtt);
+3149        }
+3150
+3151        // Update state for confirmed delivery of frames
+3152        if let Some(retransmits) = info.retransmits.get() {
+3153            for (id, _) in retransmits.reset_stream.iter() {
+3154                self.streams.reset_acked(*id);
+3155            }
+3156        }
+3157
+3158        for frame in info.stream_frames {
+3159            self.streams.received_ack_of(frame);
+3160        }
+3161    }
+3162
+3163    fn set_key_discard_timer(&mut self, now: Instant, space: SpaceKind) {
+3164        let start = if self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) {
+3165            now
+3166        } else {
+3167            self.crypto_state
+3168                .prev_crypto
+3169                .as_ref()
+3170                .expect("no previous keys")
+3171                .end_packet
+3172                .as_ref()
+3173                .expect("update not acknowledged yet")
+3174                .1
+3175        };
+3176
+3177        // QUIC-MULTIPATH § 2.5 Key Phase Update Process: use largest PTO of all paths.
+3178        self.timers.set(
+3179            Timer::Conn(ConnTimer::KeyDiscard),
+3180            start + self.max_pto_for_space(space) * 3,
+3181            self.qlog.with_time(now),
+3182        );
+3183    }
+3184
+3185    /// Handle a [`PathTimer::LossDetection`] timeout.
+3186    ///
+3187    /// This timer expires for two reasons:
+3188    /// - An ACK-eliciting packet we sent should be considered lost.
+3189    /// - The PTO may have expired and a tail-loss probe needs to be scheduled.
+3190    ///
+3191    /// The former needs us to schedule re-transmission of the lost data.
+3192    ///
+3193    /// The latter means we have not received an ACK for an ack-eliciting packet we sent
+3194    /// within the PTO time-window. We need to schedule a tail-loss probe, an ack-eliciting
+3195    /// packet, to try and elicit new acknowledgements. These new acknowledgements will
+3196    /// indicate whether the previously sent packets were lost or not.
+3197    fn on_loss_detection_timeout(&mut self, now: Instant, path_id: PathId) {
+3198        if let Some((_, pn_space)) = self.loss_time_and_space(path_id) {
+3199            // Time threshold loss Detection
+3200            self.detect_lost_packets(now, pn_space, path_id, false);
+3201            self.set_loss_detection_timer(now, path_id);
+3202            return;
+3203        }
+3204
+3205        let Some((_, space)) = self.pto_time_and_space(now, path_id) else {
+3206            error!(%path_id, "PTO expired while unset");
+3207            return;
+3208        };
+3209        trace!(
+3210            in_flight = self.path_data(path_id).in_flight.bytes,
+3211            count = self.path_data(path_id).pto_count,
+3212            ?space,
+3213            %path_id,
+3214            "PTO fired"
+3215        );
+3216
+3217        let count = match self.path_data(path_id).in_flight.ack_eliciting {
+3218            // A PTO when we're not expecting any ACKs must be due to handshake
+3219            // anti-amplification deadlock prevention.
+3220            0 => {
+3221                debug_assert!(!self.peer_completed_handshake_address_validation());
+3222                1
+3223            }
+3224            // Conventional loss probe
+3225            _ => 2,
+3226        };
+3227        let pns = self.spaces[space].for_path(path_id);
+3228        pns.loss_probes = pns.loss_probes.saturating_add(count);
+3229        let path_data = self.path_data_mut(path_id);
+3230        path_data.pto_count = path_data.pto_count.saturating_add(1);
+3231        self.set_loss_detection_timer(now, path_id);
+3232    }
+3233
+3234    /// Detect any lost packets
+3235    ///
+3236    /// There are two cases in which we detects lost packets:
+3237    ///
+3238    /// - We received an ACK packet.
+3239    /// - The [`PathTimer::LossDetection`] timer expired. So there is an un-acknowledged packet
+3240    ///   that was followed by an acknowledged packet. The loss timer for this
+3241    ///   un-acknowledged packet expired and we need to detect that packet as lost.
+3242    ///
+3243    /// Packets are lost if they are both (See RFC9002 §6.1):
+3244    ///
+3245    /// - Unacknowledged, in flight and sent prior to an acknowledged packet.
+3246    /// - Old enough by either:
+3247    ///   - Having a packet number [`TransportConfig::packet_threshold`] lower then the last
+3248    ///     acknowledged packet.
+3249    ///   - Being sent [`TransportConfig::time_threshold`] * RTT in the past.
+3250    fn detect_lost_packets(
+3251        &mut self,
+3252        now: Instant,
+3253        pn_space: SpaceId,
+3254        path_id: PathId,
+3255        due_to_ack: bool,
+3256    ) {
+3257        let mut lost_packets = Vec::<u64>::new();
+3258        let mut lost_mtu_probe = None;
+3259        let mut in_persistent_congestion = false;
+3260        let mut size_of_lost_packets = 0u64;
+3261        self.spaces[pn_space].for_path(path_id).loss_time = None;
+3262
+3263        // Find all the lost packets, populating all variables initialised above.
+3264
+3265        let path = self.path_data(path_id);
+3266        let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe();
+3267        let loss_delay = path
+3268            .rtt
+3269            .conservative()
+3270            .mul_f32(self.config.time_threshold)
+3271            .max(TIMER_GRANULARITY);
+3272        let first_packet_after_rtt_sample = path.first_packet_after_rtt_sample;
+3273
+3274        let largest_acked_packet_pn = self.spaces[pn_space]
+3275            .for_path(path_id)
+3276            .largest_acked_packet_pn
+3277            .expect("detect_lost_packets only to be called if path received at least one ACK");
+3278        let packet_threshold = self.config.packet_threshold as u64;
+3279
+3280        // InPersistentCongestion: Determine if all packets in the time period before the newest
+3281        // lost packet, including the edges, are marked lost. PTO computation must always
+3282        // include max ACK delay, i.e. operate as if in Data space (see RFC9001 §7.6.1).
+3283        let congestion_period = self
+3284            .pto(SpaceKind::Data, path_id)
+3285            .saturating_mul(self.config.persistent_congestion_threshold);
+3286        let mut persistent_congestion_start: Option<Instant> = None;
+3287        let mut prev_packet = None;
+3288        let space = self.spaces[pn_space].for_path(path_id);
+3289
+3290        for (packet, info) in space.sent_packets.iter_range(0..largest_acked_packet_pn) {
+3291            if prev_packet != Some(packet.wrapping_sub(1)) {
+3292                // An intervening packet was acknowledged
+3293                persistent_congestion_start = None;
+3294            }
+3295
+3296            // Packets sent before now - loss_delay are deemed lost.
+3297            // However, we avoid subtraction as it can panic and there's no
+3298            // saturating equivalent of this subtraction operation with a Duration.
+3299            let packet_too_old = now.saturating_duration_since(info.time_sent) >= loss_delay;
+3300            if packet_too_old || largest_acked_packet_pn >= packet + packet_threshold {
+3301                // The packet should be declared lost.
+3302                if Some(packet) == in_flight_mtu_probe {
+3303                    // Lost MTU probes are not included in `lost_packets`, because they
+3304                    // should not trigger a congestion control response
+3305                    lost_mtu_probe = in_flight_mtu_probe;
+3306                } else {
+3307                    lost_packets.push(packet);
+3308                    size_of_lost_packets += info.size as u64;
+3309                    if info.ack_eliciting && due_to_ack {
+3310                        match persistent_congestion_start {
+3311                            // Two ACK-eliciting packets lost more than
+3312                            // congestion_period apart, with no ACKed packets in between
+3313                            Some(start) if info.time_sent - start > congestion_period => {
+3314                                in_persistent_congestion = true;
+3315                            }
+3316                            // Persistent congestion must start after the first RTT sample
+3317                            None if first_packet_after_rtt_sample
+3318                                .is_some_and(|x| x < (pn_space.kind(), packet)) =>
+3319                            {
+3320                                persistent_congestion_start = Some(info.time_sent);
+3321                            }
+3322                            _ => {}
+3323                        }
+3324                    }
+3325                }
+3326            } else {
+3327                // The packet should not yet be declared lost.
+3328                if space.loss_time.is_none() {
+3329                    // Since we iterate in order the lowest packet number's loss time will
+3330                    // always be the earliest.
+3331                    space.loss_time = Some(info.time_sent + loss_delay);
+3332                }
+3333                persistent_congestion_start = None;
+3334            }
+3335
+3336            prev_packet = Some(packet);
+3337        }
+3338
+3339        self.handle_lost_packets(
+3340            pn_space,
+3341            path_id,
+3342            now,
+3343            lost_packets,
+3344            lost_mtu_probe,
+3345            loss_delay,
+3346            in_persistent_congestion,
+3347            size_of_lost_packets,
+3348        );
+3349    }
+3350
+3351    /// Drops the path state, declaring any remaining in-flight packets as lost
+3352    fn discard_path(&mut self, path_id: PathId, now: Instant) {
+3353        trace!(%path_id, "dropping path state");
+3354        let path = self.path_data(path_id);
+3355        let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe();
+3356
+3357        let mut size_of_lost_packets = 0u64; // add to path_stats.lost_bytes;
+3358        let lost_pns: Vec<_> = self.spaces[SpaceId::Data]
+3359            .for_path(path_id)
+3360            .sent_packets
+3361            .iter()
+3362            .filter(|(pn, _info)| Some(*pn) != in_flight_mtu_probe)
+3363            .map(|(pn, info)| {
+3364                size_of_lost_packets += info.size as u64;
+3365                pn
+3366            })
+3367            .collect();
+3368
+3369        if !lost_pns.is_empty() {
+3370            trace!(
+3371                %path_id,
+3372                count = lost_pns.len(),
+3373                lost_bytes = size_of_lost_packets,
+3374                "packets lost on path abandon"
+3375            );
+3376            self.handle_lost_packets(
+3377                SpaceId::Data,
+3378                path_id,
+3379                now,
+3380                lost_pns,
+3381                in_flight_mtu_probe,
+3382                Duration::ZERO,
+3383                false,
+3384                size_of_lost_packets,
+3385            );
+3386        }
+3387        // Before removing the path, we fetch the final path stats via `Self::path_stats`.
+3388        // This updates some values for the last time.
+3389        let path_stats = self.path_stats.discard(&path_id);
+3390        self.partial_stats += path_stats;
+3391        self.paths.remove(&path_id);
+3392        self.spaces[SpaceId::Data].number_spaces.remove(&path_id);
+3393
+3394        self.events.push_back(
+3395            PathEvent::Discarded {
+3396                id: path_id,
+3397                path_stats: Box::new(path_stats),
+3398            }
+3399            .into(),
+3400        );
+3401    }
+3402
+3403    fn handle_lost_packets(
+3404        &mut self,
+3405        pn_space: SpaceId,
+3406        path_id: PathId,
+3407        now: Instant,
+3408        lost_packets: Vec<u64>,
+3409        lost_mtu_probe: Option<u64>,
+3410        loss_delay: Duration,
+3411        in_persistent_congestion: bool,
+3412        size_of_lost_packets: u64,
+3413    ) {
+3414        debug_assert!(lost_packets.is_sorted(), "lost_packets must be sorted");
+3415
+3416        self.drain_lost_packets(now, pn_space, path_id);
+3417
+3418        // OnPacketsLost
+3419        if let Some(largest_lost) = lost_packets.last().cloned() {
+3420            let old_bytes_in_flight = self.path_data_mut(path_id).in_flight.bytes;
+3421            let largest_lost_sent = self.spaces[pn_space]
+3422                .for_path(path_id)
+3423                .sent_packets
+3424                .get(largest_lost)
+3425                .unwrap()
+3426                .time_sent;
+3427            let path_stats = self.path_stats.for_path(path_id);
+3428            path_stats.lost_packets += lost_packets.len() as u64;
+3429            path_stats.lost_bytes += size_of_lost_packets;
+3430            trace!(
+3431                %path_id,
+3432                count = lost_packets.len(),
+3433                lost_bytes = size_of_lost_packets,
+3434                "packets lost",
+3435            );
+3436
+3437            for &packet in &lost_packets {
+3438                let Some(info) = self.spaces[pn_space].for_path(path_id).take(packet) else {
+3439                    continue;
+3440                };
+3441                self.qlog
+3442                    .emit_packet_lost(packet, &info, loss_delay, pn_space.kind(), now);
+3443                self.paths
+3444                    .get_mut(&path_id)
+3445                    .unwrap()
+3446                    .remove_in_flight(&info);
+3447
+3448                for frame in info.stream_frames {
+3449                    self.streams.retransmit(frame);
+3450                }
+3451                self.spaces[pn_space].pending |= info.retransmits;
+3452                let path = self.path_data_mut(path_id);
+3453                path.mtud.on_non_probe_lost(packet, info.size);
+3454                path.congestion.on_packet_lost(info.size, packet, now);
+3455
+3456                self.spaces[pn_space].for_path(path_id).lost_packets.insert(
+3457                    packet,
+3458                    LostPacket {
+3459                        time_sent: info.time_sent,
+3460                    },
+3461                );
+3462            }
+3463
+3464            let path = self.path_data_mut(path_id);
+3465            if path.mtud.black_hole_detected(now) {
+3466                path.congestion.on_mtu_update(path.mtud.current_mtu());
+3467                if let Some(max_datagram_size) = self.datagrams().max_size()
+3468                    && self.datagrams.drop_oversized(max_datagram_size)
+3469                    && self.datagrams.send_blocked
+3470                {
+3471                    self.datagrams.send_blocked = false;
+3472                    self.events.push_back(Event::DatagramsUnblocked);
+3473                }
+3474                self.path_stats.for_path(path_id).black_holes_detected += 1;
+3475            }
+3476
+3477            // Don't apply congestion penalty for lost ack-only packets
+3478            let lost_ack_eliciting =
+3479                old_bytes_in_flight != self.path_data_mut(path_id).in_flight.bytes;
+3480
+3481            if lost_ack_eliciting {
+3482                self.path_stats.for_path(path_id).congestion_events += 1;
+3483                self.path_data_mut(path_id).congestion.on_congestion_event(
+3484                    now,
+3485                    largest_lost_sent,
+3486                    in_persistent_congestion,
+3487                    false,
+3488                    size_of_lost_packets,
+3489                    largest_lost,
+3490                );
+3491            }
+3492        }
+3493
+3494        // Handle a lost MTU probe
+3495        if let Some(packet) = lost_mtu_probe {
+3496            let info = self.spaces[SpaceId::Data]
+3497                .for_path(path_id)
+3498                .take(packet)
+3499                .unwrap(); // safe: lost_mtu_probe is omitted from lost_packets, and
+3500            // therefore must not have been removed yet
+3501            self.paths
+3502                .get_mut(&path_id)
+3503                .unwrap()
+3504                .remove_in_flight(&info);
+3505            self.path_data_mut(path_id).mtud.on_probe_lost();
+3506            self.path_stats.for_path(path_id).lost_plpmtud_probes += 1;
+3507        }
+3508    }
+3509
+3510    /// Returns the earliest time packets should be declared lost for all spaces on a path.
+3511    ///
+3512    /// If a path has an acknowledged packet with any prior un-acknowledged packets, the
+3513    /// earliest un-acknowledged packet can be declared lost after a timeout has elapsed.
+3514    /// The time returned is when this packet should be declared lost.
+3515    fn loss_time_and_space(&self, path_id: PathId) -> Option<(Instant, SpaceId)> {
+3516        SpaceId::iter()
+3517            .filter_map(|id| {
+3518                self.spaces[id]
+3519                    .number_spaces
+3520                    .get(&path_id)
+3521                    .and_then(|pns| pns.loss_time)
+3522                    .map(|time| (time, id))
+3523            })
+3524            .min_by_key(|&(time, _)| time)
+3525    }
+3526
+3527    /// Returns the earliest next PTO should fire for all spaces on a path.
+3528    ///
+3529    /// This needs to be fully deterministic because it is also used to determine the PTO
+3530    /// that fired, not just to set the next timer. So if it fired in the past it needs to
+3531    /// return the time from the past at which it fired.
+3532    ///
+3533    /// This is the next time a tail-loss probe should be sent.
+3534    fn pto_time_and_space(&mut self, now: Instant, path_id: PathId) -> Option<(Instant, SpaceId)> {
+3535        let path = self.path(path_id)?;
+3536        let pto_count = path.pto_count;
+3537
+3538        // Cap the maximum interval between two tail-loss probes.
+3539        let max_interval = if path.rtt.get() > SLOW_RTT_THRESHOLD {
+3540            // For slow links we want to increase the interval beyond 2s.
+3541            (path.rtt.get() * 3) / 2
+3542        } else if let Some(idle) = path.idle_timeout.or(self.idle_timeout)
+3543            && idle <= MIN_IDLE_FOR_FAST_PTO
+3544        {
+3545            // If the idle timeout is relatively low, cap at 1s so we get plenty of retries
+3546            // before the idle timeout fires.
+3547            MAX_PTO_FAST_INTERVAL
+3548        } else {
+3549            // Otherwise cap to 2s.
+3550            MAX_PTO_INTERVAL
+3551        };
+3552
+3553        if path_id == PathId::ZERO
+3554            && path.in_flight.ack_eliciting == 0
+3555            && !self.peer_completed_handshake_address_validation()
+3556        {
+3557            // Address Validation during Connection Establishment:
+3558            // https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1. To prevent a
+3559            // deadlock if an Initial or Handshake packet from the server is lost and the
+3560            // server can not send more due to its anti-amplification limit the client must
+3561            // send another packet on PTO.
+3562            let space = match self.highest_space {
+3563                SpaceKind::Handshake => SpaceId::Handshake,
+3564                _ => SpaceId::Initial,
+3565            };
+3566
+3567            let backoff = 2u32.pow(path.pto_count.min(MAX_BACKOFF_EXPONENT));
+3568            let duration = path.rtt.pto_base() * backoff;
+3569            let duration = duration.min(max_interval);
+3570            return Some((now + duration, space));
+3571        }
+3572
+3573        let mut result = None;
+3574        for space in SpaceId::iter() {
+3575            let Some(pns) = self.spaces[space].number_spaces.get(&path_id) else {
+3576                continue;
+3577            };
+3578
+3579            if space == SpaceId::Data && !self.is_handshake_confirmed() {
+3580                // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1-7:
+3581                // An endpoint MUST NOT set its PTO timer for the Application Data packet
+3582                // number space until the handshake is confirmed.
+3583                continue;
+3584            }
+3585
+3586            if !pns.has_in_flight() {
+3587                continue;
+3588            }
+3589
+3590            // Compute the PTO duration for this space, we want to cap the maximum interval
+3591            // between two tail-loss probes so to not do a simple exponential backoff but
+3592            // rather iterate through the probes to compute the capped increment for an
+3593            // exponential backoff at each step.
+3594            let duration = {
+3595                let max_ack_delay = if space == SpaceId::Data {
+3596                    self.ack_frequency.max_ack_delay_for_pto()
+3597                } else {
+3598                    Duration::ZERO
+3599                };
+3600                let pto_base = path.rtt.pto_base() + max_ack_delay;
+3601                let mut duration = pto_base;
+3602                for i in 1..=pto_count {
+3603                    let exponential_duration = pto_base * 2u32.pow(i.min(MAX_BACKOFF_EXPONENT));
+3604                    let max_duration = duration + max_interval;
+3605                    duration = exponential_duration.min(max_duration);
+3606                }
+3607                duration
+3608            };
+3609
+3610            let Some(last_ack_eliciting) = pns.time_of_last_ack_eliciting_packet else {
+3611                continue;
+3612            };
+3613            // Base the deadline on when the last probe was sent, so the PTO
+3614            // doesn't fire before the response has had time to arrive.
+3615            let pto = last_ack_eliciting + duration;
+3616            if result.is_none_or(|(earliest_pto, _)| pto < earliest_pto) {
+3617                if path.anti_amplification_blocked(1) {
+3618                    // Nothing would be able to be sent.
+3619                    continue;
+3620                }
+3621                if path.in_flight.ack_eliciting == 0 {
+3622                    // Nothing ack-eliciting, no PTO to arm/fire.
+3623                    continue;
+3624                }
+3625                result = Some((pto, space));
+3626            }
+3627        }
+3628        result
+3629    }
+3630
+3631    /// Whether the peer validated our address in the connection handshake.
+3632    fn peer_completed_handshake_address_validation(&self) -> bool {
+3633        if self.side.is_server() || self.state.is_closed() {
+3634            return true;
+3635        }
+3636        // The server is guaranteed to have validated our address if any of our handshake or
+3637        // 1-RTT packets are acknowledged or we've seen HANDSHAKE_DONE and discarded
+3638        // handshake keys.
+3639        self.spaces[SpaceId::Handshake]
+3640            .path_space(PathId::ZERO)
+3641            .and_then(|pns| pns.largest_acked_packet_pn)
+3642            .is_some()
+3643            || self.spaces[SpaceId::Data]
+3644                .path_space(PathId::ZERO)
+3645                .and_then(|pns| pns.largest_acked_packet_pn)
+3646                .is_some()
+3647            || (self.crypto_state.has_keys(EncryptionLevel::OneRtt)
+3648                && !self.crypto_state.has_keys(EncryptionLevel::Handshake))
+3649    }
+3650
+3651    /// Resets the the [`PathTimer::LossDetection`] timer to the next instant it may be needed
+3652    ///
+3653    /// The timer must fire if either:
+3654    /// - An ack-eliciting packet we sent needs to be declared lost.
+3655    /// - A tail-loss probe needs to be sent.
+3656    ///
+3657    /// See [`Connection::on_loss_detection_timeout`] for details.
+3658    fn set_loss_detection_timer(&mut self, now: Instant, path_id: PathId) {
+3659        if self.state.is_closed() {
+3660            // No loss detection takes place on closed connections, and `close_common` already
+3661            // stopped time timer. Ensure we don't restart it inadvertently, e.g. in response to a
+3662            // reordered packet being handled by state-insensitive code.
+3663            return;
+3664        }
+3665
+3666        if let Some((loss_time, _)) = self.loss_time_and_space(path_id) {
+3667            // Time threshold loss detection.
+3668            self.timers.set(
+3669                Timer::PerPath(path_id, PathTimer::LossDetection),
+3670                loss_time,
+3671                self.qlog.with_time(now),
+3672            );
+3673            return;
+3674        }
+3675
+3676        // Determine which PN space to arm PTO for.
+3677        // We can only send tail-loss probes on paths that aren't abandoned yet.
+3678        if !self.abandoned_paths.contains(&path_id)
+3679            && let Some((timeout, _)) = self.pto_time_and_space(now, path_id)
+3680        {
+3681            self.timers.set(
+3682                Timer::PerPath(path_id, PathTimer::LossDetection),
+3683                timeout,
+3684                self.qlog.with_time(now),
+3685            );
+3686        } else {
+3687            self.timers.stop(
+3688                Timer::PerPath(path_id, PathTimer::LossDetection),
+3689                self.qlog.with_time(now),
+3690            );
+3691        }
+3692    }
+3693
+3694    /// The maximum probe timeout across all paths
+3695    ///
+3696    /// See [`Connection::pto`]
+3697    fn max_pto_for_space(&self, space: SpaceKind) -> Duration {
+3698        self.paths
+3699            .keys()
+3700            .map(|path_id| self.pto(space, *path_id))
+3701            .max()
+3702            .unwrap_or_else(|| {
+3703                // No paths remain (e.g. last path was abandoned and the NoAvailablePath grace timer
+3704                // fired before any new path was opened). Fall back to a PTO derived from the
+3705                // configured initial RTT, matching RFC 9002 §6.2.2 initial values.
+3706                let rtt = self.config.initial_rtt;
+3707                let max_ack_delay = match space {
+3708                    SpaceKind::Initial | SpaceKind::Handshake => Duration::ZERO,
+3709                    SpaceKind::Data => self.ack_frequency.max_ack_delay_for_pto(),
+3710                };
+3711                rtt + cmp::max(4 * (rtt / 2), TIMER_GRANULARITY) + max_ack_delay
+3712            })
+3713    }
+3714
+3715    /// Probe Timeout
+3716    ///
+3717    /// The PTO is logically the time in which you'd expect to receive an acknowledgement
+3718    /// for a packet. So approximately RTT + max_ack_delay.
+3719    fn pto(&self, space: SpaceKind, path_id: PathId) -> Duration {
+3720        let max_ack_delay = match space {
+3721            SpaceKind::Initial | SpaceKind::Handshake => Duration::ZERO,
+3722            SpaceKind::Data => self.ack_frequency.max_ack_delay_for_pto(),
+3723        };
+3724        self.path_data(path_id).rtt.pto_base() + max_ack_delay
+3725    }
+3726
+3727    fn on_packet_authenticated(
+3728        &mut self,
+3729        now: Instant,
+3730        space_id: SpaceKind,
+3731        path_id: PathId,
+3732        ecn: Option<EcnCodepoint>,
+3733        packet_number: Option<u64>,
+3734        spin: bool,
+3735        is_1rtt: bool,
+3736        remote: &FourTuple,
+3737    ) {
+3738        // During the handshake we already have discarded packets that do not match the path
+3739        // remote. So any off-path packet here is either a probing packet or a
+3740        // migration. Handling probing packets here means that the path's idle timeout will
+3741        // be reset and will delay detecting the path as idle. However tail-loss probes
+3742        // would still not get acknowledged if the path was broken so eventually the path
+3743        // would still become idle.
+3744        let is_on_path = self
+3745            .path_data(path_id)
+3746            .network_path
+3747            .is_probably_same_path(remote);
+3748
+3749        self.total_authed_packets += 1;
+3750        self.reset_keep_alive(path_id, now);
+3751        self.reset_idle_timeout(now, space_id, path_id);
+3752        self.path_data_mut(path_id).permit_idle_reset = true;
+3753
+3754        // Do not process ECN for off-path packets. If this is a migration we'll get ECN
+3755        // back once we've migrated.
+3756        if is_on_path {
+3757            self.receiving_ecn |= ecn.is_some();
+3758            if let Some(x) = ecn {
+3759                let space = &mut self.spaces[space_id];
+3760                space.for_path(path_id).ecn_counters += x;
+3761
+3762                if x.is_ce() {
+3763                    space
+3764                        .for_path(path_id)
+3765                        .pending_acks
+3766                        .set_immediate_ack_required();
+3767                }
+3768            }
+3769        }
+3770
+3771        let Some(packet_number) = packet_number else {
+3772            return;
+3773        };
+3774        match &self.side {
+3775            ConnectionSide::Client { .. } => {
+3776                // If we received a handshake packet that authenticated, then we're talking to
+3777                // the real server.  From now on we should no longer allow the server to migrate
+3778                // its address.
+3779                if space_id == SpaceKind::Handshake
+3780                    && let Some(hs) = self.state.as_handshake_mut()
+3781                {
+3782                    hs.allow_server_migration = false;
+3783                }
+3784            }
+3785            ConnectionSide::Server { .. } => {
+3786                if self.crypto_state.has_keys(EncryptionLevel::Initial)
+3787                    && space_id == SpaceKind::Handshake
+3788                {
+3789                    // A server stops sending and processing Initial packets when it receives its first Handshake packet.
+3790                    self.discard_space(now, SpaceKind::Initial);
+3791                }
+3792                if self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) && is_1rtt {
+3793                    // Discard 0-RTT keys soon after receiving a 1-RTT packet
+3794                    self.set_key_discard_timer(now, space_id)
+3795                }
+3796            }
+3797        }
+3798        let space = self.spaces[space_id].for_path(path_id);
+3799
+3800        space.pending_acks.insert_one(packet_number, now);
+3801        if packet_number >= space.largest_received_packet_number.unwrap_or_default() {
+3802            space.largest_received_packet_number = Some(packet_number);
+3803
+3804            // Update outgoing spin bit for on-path packets, inverting iff we're the client
+3805            if is_on_path {
+3806                self.spin = self.side.is_client() ^ spin;
+3807            }
+3808        }
+3809    }
+3810
+3811    /// Resets the idle timeout timers
+3812    ///
+3813    /// Without multipath there is only the connection-wide idle timeout. When multipath is
+3814    /// enabled there is an additional per-path idle timeout.
+3815    fn reset_idle_timeout(&mut self, now: Instant, space: SpaceKind, path_id: PathId) {
+3816        // First reset the global idle timeout.
+3817        if let Some(timeout) = self.idle_timeout {
+3818            if self.state.is_closed() {
+3819                self.timers
+3820                    .stop(Timer::Conn(ConnTimer::Idle), self.qlog.with_time(now));
+3821            } else {
+3822                let dt = cmp::max(timeout, 3 * self.max_pto_for_space(space));
+3823                self.timers.set(
+3824                    Timer::Conn(ConnTimer::Idle),
+3825                    now + dt,
+3826                    self.qlog.with_time(now),
+3827                );
+3828            }
+3829        }
+3830
+3831        // Now handle the per-path state
+3832        if let Some(timeout) = self.path_data(path_id).idle_timeout {
+3833            if self.state.is_closed() {
+3834                self.timers.stop(
+3835                    Timer::PerPath(path_id, PathTimer::PathIdle),
+3836                    self.qlog.with_time(now),
+3837                );
+3838            } else {
+3839                let dt = cmp::max(timeout, 3 * self.pto(space, path_id));
+3840                self.timers.set(
+3841                    Timer::PerPath(path_id, PathTimer::PathIdle),
+3842                    now + dt,
+3843                    self.qlog.with_time(now),
+3844                );
+3845            }
+3846        }
+3847    }
+3848
+3849    /// Resets both the [`ConnTimer::KeepAlive`] and [`PathTimer::PathKeepAlive`] timers
+3850    fn reset_keep_alive(&mut self, path_id: PathId, now: Instant) {
+3851        if !self.state.is_established() {
+3852            return;
+3853        }
+3854
+3855        if let Some(interval) = self.config.keep_alive_interval {
+3856            self.timers.set(
+3857                Timer::Conn(ConnTimer::KeepAlive),
+3858                now + interval,
+3859                self.qlog.with_time(now),
+3860            );
+3861        }
+3862
+3863        if let Some(interval) = self.path_data(path_id).keep_alive {
+3864            self.timers.set(
+3865                Timer::PerPath(path_id, PathTimer::PathKeepAlive),
+3866                now + interval,
+3867                self.qlog.with_time(now),
+3868            );
+3869        }
+3870    }
+3871
+3872    /// Sets the timer for when a previously issued CID should be retired next
+3873    fn reset_cid_retirement(&mut self, now: Instant) {
+3874        if let Some((_path, t)) = self.next_cid_retirement() {
+3875            self.timers.set(
+3876                Timer::Conn(ConnTimer::PushNewCid),
+3877                t,
+3878                self.qlog.with_time(now),
+3879            );
+3880        }
+3881    }
+3882
+3883    /// The next time when a previously issued CID should be retired
+3884    fn next_cid_retirement(&self) -> Option<(PathId, Instant)> {
+3885        self.local_cid_state
+3886            .iter()
+3887            .filter_map(|(path_id, cid_state)| cid_state.next_timeout().map(|t| (*path_id, t)))
+3888            .min_by_key(|(_path_id, timeout)| *timeout)
+3889    }
+3890
+3891    /// Handle the already-decrypted first packet from the client
+3892    ///
+3893    /// Decrypting the first packet in the `Endpoint` allows stateless packet handling to be more
+3894    /// efficient.
+3895    pub(crate) fn handle_first_packet(
+3896        &mut self,
+3897        now: Instant,
+3898        network_path: FourTuple,
+3899        ecn: Option<EcnCodepoint>,
+3900        packet_number: u64,
+3901        packet: InitialPacket,
+3902        remaining: Option<BytesMut>,
+3903    ) -> Result<(), ConnectionError> {
+3904        let span = trace_span!("first recv");
+3905        let _guard = span.enter();
+3906        debug_assert!(self.side.is_server());
+3907        let len = packet.header_data.len() + packet.payload.len();
+3908        let path_id = PathId::ZERO;
+3909        self.path_data_mut(path_id).total_recvd = len as u64;
+3910
+3911        if let Some(hs) = self.state.as_handshake_mut() {
+3912            hs.expected_token = packet.header.token.clone();
+3913        } else {
+3914            unreachable!("first packet must be delivered in Handshake state");
+3915        }
+3916
+3917        // The first packet is always on PathId::ZERO
+3918        self.on_packet_authenticated(
+3919            now,
+3920            SpaceKind::Initial,
+3921            path_id,
+3922            ecn,
+3923            Some(packet_number),
+3924            false,
+3925            false,
+3926            &network_path,
+3927        );
+3928
+3929        let packet: Packet = packet.into();
+3930
+3931        let mut qlog = QlogRecvPacket::new(len);
+3932        qlog.header(&packet.header, Some(packet_number), path_id);
+3933
+3934        self.process_decrypted_packet(
+3935            now,
+3936            network_path,
+3937            path_id,
+3938            Some(packet_number),
+3939            packet,
+3940            &mut qlog,
+3941        )?;
+3942        self.qlog.emit_packet_received(qlog, now);
+3943        if let Some(data) = remaining {
+3944            self.handle_coalesced(now, network_path, path_id, ecn, data);
+3945        }
+3946
+3947        self.qlog.emit_recovery_metrics(
+3948            path_id,
+3949            &mut self
+3950                .paths
+3951                .get_mut(&path_id)
+3952                .expect("path_id was supplied by the caller for an active path")
+3953                .data,
+3954            now,
+3955        );
+3956
+3957        Ok(())
+3958    }
+3959
+3960    fn init_0rtt(&mut self, now: Instant) {
+3961        let Some((header, packet)) = self.crypto_state.session.early_crypto() else {
+3962            return;
+3963        };
+3964        if self.side.is_client() {
+3965            match self.crypto_state.session.transport_parameters() {
+3966                Ok(params) => {
+3967                    let params = params
+3968                        .expect("crypto layer didn't supply transport parameters with ticket");
+3969                    // Certain values must not be cached
+3970                    let params = TransportParameters {
+3971                        initial_src_cid: None,
+3972                        original_dst_cid: None,
+3973                        preferred_address: None,
+3974                        retry_src_cid: None,
+3975                        stateless_reset_token: None,
+3976                        min_ack_delay: None,
+3977                        ack_delay_exponent: TransportParameters::default().ack_delay_exponent,
+3978                        max_ack_delay: TransportParameters::default().max_ack_delay,
+3979                        initial_max_path_id: None,
+3980                        ..params
+3981                    };
+3982                    self.set_peer_params(params);
+3983                    self.qlog.emit_peer_transport_params_restored(self, now);
+3984                }
+3985                Err(e) => {
+3986                    error!("session ticket has malformed transport parameters: {}", e);
+3987                    return;
+3988                }
+3989            }
+3990        }
+3991        trace!("0-RTT enabled");
+3992        self.crypto_state.enable_zero_rtt(header, packet);
+3993    }
+3994
+3995    fn read_crypto(
+3996        &mut self,
+3997        space: SpaceId,
+3998        crypto: &frame::Crypto,
+3999        payload_len: usize,
+4000    ) -> Result<(), TransportError> {
+4001        let expected = if !self.state.is_handshake() {
+4002            SpaceId::Data
+4003        } else if self.highest_space == SpaceKind::Initial {
+4004            SpaceId::Initial
+4005        } else {
+4006            // On the server, self.highest_space can be Data after receiving the client's first
+4007            // flight, but we expect Handshake CRYPTO until the handshake is complete.
+4008            SpaceId::Handshake
+4009        };
+4010        // We can't decrypt Handshake packets when highest_space is Initial, CRYPTO frames in 0-RTT
+4011        // packets are illegal, and we don't process 1-RTT packets until the handshake is
+4012        // complete. Therefore, we will never see CRYPTO data from a later-than-expected space.
+4013        debug_assert!(space <= expected, "received out-of-order CRYPTO data");
+4014
+4015        let end = crypto.offset + crypto.data.len() as u64;
+4016        if space < expected
+4017            && end
+4018                > self.crypto_state.spaces[space.kind()]
+4019                    .crypto_stream
+4020                    .bytes_read()
+4021        {
+4022            warn!(
+4023                "received new {:?} CRYPTO data when expecting {:?}",
+4024                space, expected
+4025            );
+4026            return Err(TransportError::PROTOCOL_VIOLATION(
+4027                "new data at unexpected encryption level",
+4028            ));
+4029        }
+4030
+4031        let crypto_space = &mut self.crypto_state.spaces[space.kind()];
+4032        let max = end.saturating_sub(crypto_space.crypto_stream.bytes_read());
+4033        if max > self.config.crypto_buffer_size as u64 {
+4034            return Err(TransportError::CRYPTO_BUFFER_EXCEEDED(""));
+4035        }
+4036
+4037        crypto_space
+4038            .crypto_stream
+4039            .insert(crypto.offset, crypto.data.clone(), payload_len);
+4040        while let Some(chunk) = crypto_space.crypto_stream.read(usize::MAX, true) {
+4041            trace!("consumed {} CRYPTO bytes", chunk.bytes.len());
+4042            if self.crypto_state.session.read_handshake(&chunk.bytes)? {
+4043                self.events.push_back(Event::HandshakeDataReady);
+4044            }
+4045        }
+4046
+4047        Ok(())
+4048    }
+4049
+4050    fn write_crypto(&mut self) {
+4051        loop {
+4052            let space = self.highest_space;
+4053            let mut outgoing = Vec::new();
+4054            if let Some(crypto) = self.crypto_state.session.write_handshake(&mut outgoing) {
+4055                match space {
+4056                    SpaceKind::Initial => {
+4057                        self.upgrade_crypto(SpaceKind::Handshake, crypto);
+4058                    }
+4059                    SpaceKind::Handshake => {
+4060                        self.upgrade_crypto(SpaceKind::Data, crypto);
+4061                    }
+4062                    SpaceKind::Data => unreachable!("got updated secrets during 1-RTT"),
+4063                }
+4064            }
+4065            if outgoing.is_empty() {
+4066                if space == self.highest_space {
+4067                    break;
+4068                } else {
+4069                    // Keys updated, check for more data to send
+4070                    continue;
+4071                }
+4072            }
+4073            let offset = self.crypto_state.spaces[space].crypto_offset;
+4074            let outgoing = Bytes::from(outgoing);
+4075            if let Some(hs) = self.state.as_handshake_mut()
+4076                && space == SpaceKind::Initial
+4077                && offset == 0
+4078                && self.side.is_client()
+4079            {
+4080                hs.client_hello = Some(outgoing.clone());
+4081            }
+4082            self.crypto_state.spaces[space].crypto_offset += outgoing.len() as u64;
+4083            trace!("wrote {} {:?} CRYPTO bytes", outgoing.len(), space);
+4084            self.spaces[space].pending.crypto.push_back(frame::Crypto {
+4085                offset,
+4086                data: outgoing,
+4087            });
+4088        }
+4089    }
+4090
+4091    /// Switch to stronger cryptography during handshake
+4092    fn upgrade_crypto(&mut self, space: SpaceKind, crypto: Keys) {
+4093        debug_assert!(
+4094            !self.crypto_state.has_keys(space.encryption_level()),
+4095            "already reached packet space {space:?}"
+4096        );
+4097        trace!("{:?} keys ready", space);
+4098        if space == SpaceKind::Data {
+4099            // Precompute the first key update
+4100            self.crypto_state.next_crypto = Some(
+4101                self.crypto_state
+4102                    .session
+4103                    .next_1rtt_keys()
+4104                    .expect("handshake should be complete"),
+4105            );
+4106        }
+4107
+4108        self.crypto_state.spaces[space].keys = Some(crypto);
+4109        debug_assert!(space > self.highest_space);
+4110        self.highest_space = space;
+4111        if space == SpaceKind::Data && self.side.is_client() {
+4112            // Discard 0-RTT keys because 1-RTT keys are available.
+4113            self.crypto_state.discard_zero_rtt();
+4114        }
+4115    }
+4116
+4117    fn discard_space(&mut self, now: Instant, space: SpaceKind) {
+4118        debug_assert!(space != SpaceKind::Data);
+4119        trace!("discarding {:?} keys", space);
+4120        if space == SpaceKind::Initial {
+4121            // No longer needed
+4122            if let ConnectionSide::Client { token, .. } = &mut self.side {
+4123                *token = Bytes::new();
+4124            }
+4125        }
+4126        self.crypto_state.spaces[space].keys = None;
+4127        let space = &mut self.spaces[space];
+4128        let pns = space.for_path(PathId::ZERO);
+4129        pns.time_of_last_ack_eliciting_packet = None;
+4130        pns.loss_time = None;
+4131        pns.loss_probes = 0;
+4132        let sent_packets = mem::take(&mut pns.sent_packets);
+4133        let path = self
+4134            .paths
+4135            .get_mut(&PathId::ZERO)
+4136            .expect("PathId::ZERO is alive while Initial/Handshake spaces exist");
+4137        for (_, packet) in sent_packets.into_iter() {
+4138            path.data.remove_in_flight(&packet);
+4139        }
+4140
+4141        self.set_loss_detection_timer(now, PathId::ZERO)
+4142    }
+4143
+4144    fn handle_coalesced(
+4145        &mut self,
+4146        now: Instant,
+4147        network_path: FourTuple,
+4148        path_id: PathId,
+4149        ecn: Option<EcnCodepoint>,
+4150        data: BytesMut,
+4151    ) {
+4152        self.path_data_mut(path_id)
+4153            .inc_total_recvd(data.len() as u64);
+4154        let mut remaining = Some(data);
+4155        let cid_len = self
+4156            .local_cid_state
+4157            .values()
+4158            .map(|cid_state| cid_state.cid_len())
+4159            .next()
+4160            .expect("one cid_state must exist");
+4161        while let Some(data) = remaining {
+4162            match PartialDecode::new(
+4163                data,
+4164                &FixedLengthConnectionIdParser::new(cid_len),
+4165                &[self.version],
+4166                self.endpoint_config.grease_quic_bit,
+4167            ) {
+4168                Ok((partial_decode, rest)) => {
+4169                    remaining = rest;
+4170                    self.handle_decode(now, network_path, path_id, ecn, partial_decode);
+4171                }
+4172                Err(e) => {
+4173                    trace!("malformed header: {}", e);
+4174                    return;
+4175                }
+4176            }
+4177        }
+4178    }
+4179
+4180    /// Decrypts the packet and processes the payload.
+4181    ///
+4182    /// Processes the entire packet, starting with removing header protection, then handling
+4183    /// a stateless reset if needed, and decrypting and processing the frames in the payload
+4184    /// if not a stateless reset.
+4185    fn handle_decode(
+4186        &mut self,
+4187        now: Instant,
+4188        network_path: FourTuple,
+4189        path_id: PathId,
+4190        ecn: Option<EcnCodepoint>,
+4191        partial_decode: PartialDecode,
+4192    ) {
+4193        let qlog = QlogRecvPacket::new(partial_decode.len());
+4194        if let Some(decoded) = self
+4195            .crypto_state
+4196            .unprotect_header(partial_decode, self.peer_params.stateless_reset_token)
+4197        {
+4198            self.handle_packet(
+4199                now,
+4200                network_path,
+4201                path_id,
+4202                ecn,
+4203                decoded.packet,
+4204                decoded.stateless_reset,
+4205                qlog,
+4206            );
+4207        }
+4208    }
+4209
+4210    /// Handles a packet with header protection removed.
+4211    ///
+4212    /// The packet body is still encrypted at this point.
+4213    ///
+4214    /// If the datagram was a stateless reset we may have failed to remove header protection
+4215    /// and thus `packet` may be `None`.
+4216    fn handle_packet(
+4217        &mut self,
+4218        now: Instant,
+4219        network_path: FourTuple,
+4220        path_id: PathId,
+4221        ecn: Option<EcnCodepoint>,
+4222        packet: Option<Packet>,
+4223        stateless_reset: bool,
+4224        mut qlog: QlogRecvPacket,
+4225    ) {
+4226        self.path_stats.for_path(path_id).udp_rx.ios += 1;
+4227
+4228        if let Some(ref packet) = packet {
+4229            trace!(
+4230                "got {:?} packet ({} bytes) from {} using id {}",
+4231                packet.header.space(),
+4232                packet.payload.len() + packet.header_data.len(),
+4233                network_path,
+4234                packet.header.dst_cid(),
+4235            );
+4236        }
+4237
+4238        let was_closed = self.state.is_closed();
+4239        let was_drained = self.state.is_drained();
+4240
+4241        // Now decrypt the packet payload in-place.
+4242        let decrypted = match packet {
+4243            None => Err(None),
+4244            Some(mut packet) => self
+4245                .decrypt_packet(now, path_id, &mut packet)
+4246                .map(move |number| (packet, number)),
+4247        };
+4248        let result = match decrypted {
+4249            _ if stateless_reset => {
+4250                debug!("got stateless reset");
+4251                Err(ConnectionError::Reset)
+4252            }
+4253            Err(Some(e)) => {
+4254                warn!("illegal packet: {}", e);
+4255                Err(e.into())
+4256            }
+4257            Err(None) => {
+4258                debug!("failed to authenticate packet");
+4259                self.authentication_failures += 1;
+4260                let integrity_limit = self
+4261                    .crypto_state
+4262                    .integrity_limit(self.highest_space)
+4263                    .unwrap();
+4264                if self.authentication_failures > integrity_limit {
+4265                    Err(TransportError::AEAD_LIMIT_REACHED("integrity limit violated").into())
+4266                } else {
+4267                    return;
+4268                }
+4269            }
+4270            Ok((packet, pn)) => {
+4271                // We received an authenticated packet and decrypted it.
+4272                qlog.header(&packet.header, pn, path_id);
+4273                let span = match pn {
+4274                    Some(pn) => trace_span!("recv", space = ?packet.header.space(), pn),
+4275                    None => trace_span!("recv", space = ?packet.header.space()),
+4276                };
+4277                let _guard = span.enter();
+4278
+4279                // Now the packet is authenticated we do the migration during the handshake,
+4280                // see Handshake::allow_server_migration for details.  Be careful here to
+4281                // not yet rely on the path existing however, new paths are accepted and
+4282                // created later.
+4283                // Note that we can't do any other migrations yet, for those we need to know
+4284                // whether this was a probing packet or not. See the end of
+4285                // Self::process_packet for that.
+4286                if self.is_handshaking()
+4287                    && self
+4288                        .path(path_id)
+4289                        .map(|path_data| {
+4290                            !path_data.network_path.is_probably_same_path(&network_path)
+4291                        })
+4292                        .unwrap_or(false)
+4293                {
+4294                    if let Some(hs) = self.state.as_handshake()
+4295                        && hs.allow_server_migration
+4296                    {
+4297                        trace!(
+4298                            %network_path,
+4299                            prev = %self.path_data(path_id).network_path,
+4300                            "server migrated to new remote",
+4301                        );
+4302                        self.path_data_mut(path_id).network_path = network_path;
+4303                        self.qlog.emit_tuple_assigned(path_id, network_path, now);
+4304                    } else {
+4305                        debug!(
+4306                            recv_path = %network_path,
+4307                            expected_path = %self.path_data_mut(path_id).network_path,
+4308                            "discarding packet with unexpected remote during handshake",
+4309                        );
+4310                        return;
+4311                    }
+4312                }
+4313
+4314                let dedup = self.spaces[packet.header.space()]
+4315                    .path_space_mut(path_id)
+4316                    .map(|pns| &mut pns.dedup);
+4317                if pn.zip(dedup).is_some_and(|(n, d)| d.insert(n)) {
+4318                    debug!("discarding possible duplicate packet");
+4319                    self.qlog.emit_packet_received(qlog, now);
+4320                    return;
+4321                } else if self.state.is_handshake() && packet.header.is_short() {
+4322                    // TODO: SHOULD buffer these to improve reordering tolerance.
+4323                    trace!("dropping short packet during handshake");
+4324                    self.qlog.emit_packet_received(qlog, now);
+4325                    return;
+4326                } else {
+4327                    if let Header::Initial(InitialHeader { ref token, .. }) = packet.header
+4328                        && let Some(hs) = self.state.as_handshake()
+4329                        && self.side.is_server()
+4330                        && token != &hs.expected_token
+4331                    {
+4332                        // Clients must send the same retry token in every Initial. Initial
+4333                        // packets can be spoofed, so we discard rather than killing the
+4334                        // connection.
+4335                        warn!("discarding Initial with invalid retry token");
+4336                        self.qlog.emit_packet_received(qlog, now);
+4337                        return;
+4338                    }
+4339
+4340                    if !self.state.is_closed() {
+4341                        let spin = match packet.header {
+4342                            Header::Short { spin, .. } => spin,
+4343                            _ => false,
+4344                        };
+4345
+4346                        if self.side().is_server() && !self.abandoned_paths.contains(&path_id) {
+4347                            // Only the client is allowed to open paths
+4348                            self.ensure_path(path_id, network_path, now, pn);
+4349                        }
+4350                        if self.paths.contains_key(&path_id) {
+4351                            self.on_packet_authenticated(
+4352                                now,
+4353                                packet.header.space(),
+4354                                path_id,
+4355                                ecn,
+4356                                pn,
+4357                                spin,
+4358                                packet.header.is_1rtt(),
+4359                                &network_path,
+4360                            );
+4361                        }
+4362                    }
+4363
+4364                    let res = self.process_decrypted_packet(
+4365                        now,
+4366                        network_path,
+4367                        path_id,
+4368                        pn,
+4369                        packet,
+4370                        &mut qlog,
+4371                    );
+4372
+4373                    self.qlog.emit_packet_received(qlog, now);
+4374                    res
+4375                }
+4376            }
+4377        };
+4378
+4379        // State transitions for error cases
+4380        if let Err(conn_err) = result {
+4381            match conn_err {
+4382                ConnectionError::ApplicationClosed(reason) => self.state.move_to_closed(reason),
+4383                ConnectionError::ConnectionClosed(reason) => self.state.move_to_closed(reason),
+4384                ConnectionError::Reset
+4385                | ConnectionError::TransportError(TransportError {
+4386                    code: TransportErrorCode::AEAD_LIMIT_REACHED,
+4387                    ..
+4388                }) => {
+4389                    let was_draining = self.state.move_to_drained(Some(conn_err));
+4390                    if !was_draining {
+4391                        self.endpoint_events.push_back(EndpointEventInner::Draining);
+4392                    }
+4393                }
+4394                ConnectionError::TimedOut => {
+4395                    unreachable!("timeouts aren't generated by packet processing");
+4396                }
+4397                ConnectionError::TransportError(err) => {
+4398                    debug!("closing connection due to transport error: {}", err);
+4399                    self.state.move_to_closed(err);
+4400                }
+4401                ConnectionError::VersionMismatch => {
+4402                    self.state.move_to_draining(Some(conn_err));
+4403                    self.endpoint_events.push_back(EndpointEventInner::Draining);
+4404                }
+4405                ConnectionError::LocallyClosed => {
+4406                    unreachable!("LocallyClosed isn't generated by packet processing");
+4407                }
+4408                ConnectionError::CidsExhausted => {
+4409                    unreachable!("CidsExhausted isn't generated by packet processing");
+4410                }
+4411            };
+4412        }
+4413
+4414        if !was_closed && self.state.is_closed() {
+4415            self.close_common();
+4416            if !self.state.is_drained() {
+4417                self.set_close_timer(now);
+4418            }
+4419        }
+4420        if !was_drained && self.state.is_drained() {
+4421            self.endpoint_events.push_back(EndpointEventInner::Drained);
+4422            // Close timer may have been started previously, e.g. if we sent a close and got a
+4423            // stateless reset in response
+4424            self.timers
+4425                .stop(Timer::Conn(ConnTimer::Close), self.qlog.with_time(now));
+4426        }
+4427
+4428        // Transmit CONNECTION_CLOSE if necessary.
+4429        //
+4430        // If we received a valid packet and we are in the closed state we should respond
+4431        // with a CONNECTION_CLOSE frame.
+4432        // TODO: This SHOULD be rate-limited according to §10.2.1 of QUIC-TRANSPORT, but
+4433        //    that does not yet happen. This is triggered by each received packet.
+4434        if matches!(self.state.as_type(), StateType::Closed) {
+4435            // From https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1-7
+4436            //
+4437            // While in the closing state we must either:
+4438            // - discard packets coming from an un-validated remote OR
+4439            // - ensure we do not send more than 3 times the received data
+4440            //
+4441            // Doing the 2nd would mean we would be able to send CONNECTION_CLOSE to a peer
+4442            // who was (involuntary) migrated just at the time we initiated immediate
+4443            // close. It is a lot more work though. So while we would like to do this for
+4444            // now we only do 1.
+4445            //
+4446            // Another shortcoming of the current implementation is that when we have a
+4447            // previous PathData which is validated and the remote matches that path, we
+4448            // should schedule CONNECTION_CLOSE on that path. However currently we can not
+4449            // schedule such a packet. We should also fix this some day. This makes us
+4450            // vulnerable to an attacker faking a migration at the right time and then we'd
+4451            // be unable to send the CONNECTION_CLOSE to the real remote.
+4452            if self
+4453                .paths
+4454                .get(&path_id)
+4455                .map(|p| p.data.validated && p.data.network_path == network_path)
+4456                .unwrap_or(false)
+4457            {
+4458                self.connection_close_pending = true;
+4459            }
+4460        }
+4461    }
+4462
+4463    fn process_decrypted_packet(
+4464        &mut self,
+4465        now: Instant,
+4466        network_path: FourTuple,
+4467        path_id: PathId,
+4468        number: Option<u64>,
+4469        packet: Packet,
+4470        qlog: &mut QlogRecvPacket,
+4471    ) -> Result<(), ConnectionError> {
+4472        if !self.paths.contains_key(&path_id) {
+4473            // There is a chance this is a server side, first (for this path) packet, which would
+4474            // be a protocol violation. It's more likely, however, that this is a packet of a
+4475            // pruned path
+4476            trace!(%path_id, ?number, "discarding packet for unknown path");
+4477            return Ok(());
+4478        }
+4479        let state = match self.state.as_type() {
+4480            StateType::Established => {
+4481                match packet.header.space() {
+4482                    SpaceKind::Data => self.process_payload(
+4483                        now,
+4484                        network_path,
+4485                        path_id,
+4486                        number.unwrap(),
+4487                        packet,
+4488                        qlog,
+4489                    )?,
+4490                    _ if packet.header.has_frames() => {
+4491                        self.process_early_payload(now, path_id, packet, qlog)?
+4492                    }
+4493                    _ => {
+4494                        trace!("discarding unexpected pre-handshake packet");
+4495                    }
+4496                }
+4497                return Ok(());
+4498            }
+4499            StateType::Closed => {
+4500                for result in frame::Iter::new(packet.payload.freeze())? {
+4501                    let frame = match result {
+4502                        Ok(frame) => frame,
+4503                        Err(err) => {
+4504                            debug!("frame decoding error: {err:?}");
+4505                            continue;
+4506                        }
+4507                    };
+4508                    qlog.frame(&frame);
+4509
+4510                    if let Frame::Padding = frame {
+4511                        continue;
+4512                    };
+4513
+4514                    trace!(?frame, "processing frame in closed state");
+4515
+4516                    self.path_stats
+4517                        .for_path(path_id)
+4518                        .frame_rx
+4519                        .record(frame.ty());
+4520
+4521                    if let Frame::Close(_error) = frame {
+4522                        self.state.move_to_draining(None);
+4523                        self.endpoint_events.push_back(EndpointEventInner::Draining);
+4524                        break;
+4525                    }
+4526                }
+4527                return Ok(());
+4528            }
+4529            StateType::Draining | StateType::Drained => return Ok(()),
+4530            StateType::Handshake => self.state.as_handshake_mut().expect("checked"),
+4531        };
+4532
+4533        match packet.header {
+4534            Header::Retry {
+4535                src_cid: remote_cid,
+4536                ..
+4537            } => {
+4538                debug_assert_eq!(path_id, PathId::ZERO);
+4539                if self.side.is_server() {
+4540                    return Err(TransportError::PROTOCOL_VIOLATION("client sent Retry").into());
+4541                }
+4542
+4543                let is_valid_retry = self
+4544                    .remote_cids
+4545                    .get(&path_id)
+4546                    .map(|cids| cids.active())
+4547                    .map(|orig_dst_cid| {
+4548                        self.crypto_state.session.is_valid_retry(
+4549                            orig_dst_cid,
+4550                            &packet.header_data,
+4551                            &packet.payload,
+4552                        )
+4553                    })
+4554                    .unwrap_or_default();
+4555                if self.total_authed_packets > 1
+4556                            || packet.payload.len() <= 16 // token + 16 byte tag
+4557                            || !is_valid_retry
+4558                {
+4559                    trace!("discarding invalid Retry");
+4560                    // - After the client has received and processed an Initial or Retry
+4561                    //   packet from the server, it MUST discard any subsequent Retry
+4562                    //   packets that it receives.
+4563                    // - A client MUST discard a Retry packet with a zero-length Retry Token
+4564                    //   field.
+4565                    // - Clients MUST discard Retry packets that have a Retry Integrity Tag
+4566                    //   that cannot be validated
+4567                    return Ok(());
+4568                }
+4569
+4570                trace!("retrying with CID {}", remote_cid);
+4571                let client_hello = state.client_hello.take().unwrap();
+4572                self.retry_src_cid = Some(remote_cid);
+4573                self.remote_cids
+4574                    .get_mut(&path_id)
+4575                    .expect("PathId::ZERO not yet abandoned, is_valid_retry would have been false")
+4576                    .update_initial_cid(remote_cid);
+4577                self.remote_handshake_cid = remote_cid;
+4578
+4579                let space = &mut self.spaces[SpaceId::Initial];
+4580                if let Some(info) = space.for_path(PathId::ZERO).take(0) {
+4581                    self.on_packet_acked(now, PathId::ZERO, 0, info);
+4582                };
+4583
+4584                self.discard_space(now, SpaceKind::Initial); // Make sure we clean up after
+4585                // any retransmitted Initials
+4586                let crypto_space = &mut self.crypto_state.spaces[SpaceKind::Initial];
+4587                crypto_space.keys = Some(
+4588                    self.crypto_state
+4589                        .session
+4590                        .initial_keys(remote_cid, self.side.side()),
+4591                );
+4592                crypto_space.crypto_offset = client_hello.len() as u64;
+4593
+4594                let next_pn = self.spaces[SpaceId::Initial]
+4595                    .for_path(path_id)
+4596                    .next_packet_number;
+4597                self.spaces[SpaceId::Initial] = {
+4598                    let mut space = PacketSpace::new(now, SpaceId::Initial, &mut self.rng);
+4599                    space.for_path(path_id).next_packet_number = next_pn;
+4600                    space.pending.crypto.push_back(frame::Crypto {
+4601                        offset: 0,
+4602                        data: client_hello,
+4603                    });
+4604                    space
+4605                };
+4606
+4607                // Retransmit all 0-RTT data
+4608                let zero_rtt = mem::take(
+4609                    &mut self.spaces[SpaceId::Data]
+4610                        .for_path(PathId::ZERO)
+4611                        .sent_packets,
+4612                );
+4613                for (_, info) in zero_rtt.into_iter() {
+4614                    self.paths
+4615                        .get_mut(&PathId::ZERO)
+4616                        .unwrap()
+4617                        .remove_in_flight(&info);
+4618                    self.spaces[SpaceId::Data].pending |= info.retransmits;
+4619                }
+4620                self.streams.retransmit_all_for_0rtt();
+4621
+4622                let token_len = packet.payload.len() - 16;
+4623                let ConnectionSide::Client { ref mut token, .. } = self.side else {
+4624                    unreachable!("we already short-circuited if we're server");
+4625                };
+4626                *token = packet.payload.freeze().split_to(token_len);
+4627
+4628                self.state = State::handshake(state::Handshake {
+4629                    expected_token: Bytes::new(),
+4630                    remote_cid_set: false,
+4631                    client_hello: None,
+4632                    allow_server_migration: true,
+4633                });
+4634                Ok(())
+4635            }
+4636            Header::Long {
+4637                ty: LongType::Handshake,
+4638                src_cid: remote_cid,
+4639                dst_cid: local_cid,
+4640                ..
+4641            } => {
+4642                debug_assert_eq!(path_id, PathId::ZERO);
+4643                if remote_cid != self.remote_handshake_cid {
+4644                    debug!(
+4645                        "discarding packet with mismatched remote CID: {} != {}",
+4646                        self.remote_handshake_cid, remote_cid
+4647                    );
+4648                    return Ok(());
+4649                }
+4650                self.on_path_validated(path_id);
+4651
+4652                self.process_early_payload(now, path_id, packet, qlog)?;
+4653                if self.state.is_closed() {
+4654                    return Ok(());
+4655                }
+4656
+4657                if self.crypto_state.session.is_handshaking() {
+4658                    trace!("handshake ongoing");
+4659                    return Ok(());
+4660                }
+4661
+4662                if self.side.is_client() {
+4663                    // Client-only because server params were set from the client's Initial
+4664                    let params = self
+4665                        .crypto_state
+4666                        .session
+4667                        .transport_parameters()?
+4668                        .ok_or_else(|| {
+4669                            TransportError::new(
+4670                                TransportErrorCode::crypto(0x6d),
+4671                                "transport parameters missing".to_owned(),
+4672                            )
+4673                        })?;
+4674
+4675                    if self.has_0rtt() {
+4676                        if !self.crypto_state.session.early_data_accepted().unwrap() {
+4677                            debug_assert!(self.side.is_client());
+4678                            debug!("0-RTT rejected");
+4679                            self.crypto_state.accepted_0rtt = false;
+4680                            self.streams.zero_rtt_rejected();
+4681
+4682                            // Discard already-queued frames
+4683                            self.spaces[SpaceId::Data].pending = Retransmits::default();
+4684
+4685                            // Discard 0-RTT packets
+4686                            let sent_packets = mem::take(
+4687                                &mut self.spaces[SpaceId::Data].for_path(path_id).sent_packets,
+4688                            );
+4689                            for (_, packet) in sent_packets.into_iter() {
+4690                                self.paths
+4691                                    .get_mut(&path_id)
+4692                                    .unwrap()
+4693                                    .remove_in_flight(&packet);
+4694                            }
+4695                        } else {
+4696                            self.crypto_state.accepted_0rtt = true;
+4697                            params.validate_resumption_from(&self.peer_params)?;
+4698                        }
+4699                    }
+4700                    if let Some(token) = params.stateless_reset_token {
+4701                        let remote = self.path_data(path_id).network_path.remote;
+4702                        debug_assert!(!self.state.is_drained()); // requirement for endpoint events, checked above
+4703                        self.endpoint_events
+4704                            .push_back(EndpointEventInner::ResetToken(path_id, remote, token));
+4705                    }
+4706                    self.handle_peer_params(params, local_cid, remote_cid, now)?;
+4707                    self.issue_first_cids(now);
+4708                } else {
+4709                    // Server-only
+4710                    self.spaces[SpaceId::Data].pending.handshake_done = true;
+4711                    self.discard_space(now, SpaceKind::Handshake);
+4712                    self.events.push_back(Event::HandshakeConfirmed);
+4713                    trace!("handshake confirmed");
+4714                }
+4715
+4716                self.events.push_back(Event::Connected);
+4717                self.state.move_to_established();
+4718                trace!("established");
+4719
+4720                // Multipath can only be enabled after the state has reached Established.
+4721                // So this can not happen any earlier.
+4722                self.issue_first_path_cids(now);
+4723                Ok(())
+4724            }
+4725            Header::Initial(InitialHeader {
+4726                src_cid: remote_cid,
+4727                dst_cid: local_cid,
+4728                ..
+4729            }) => {
+4730                debug_assert_eq!(path_id, PathId::ZERO);
+4731                if !state.remote_cid_set {
+4732                    trace!("switching remote CID to {}", remote_cid);
+4733                    let mut state = state.clone();
+4734                    self.remote_cids
+4735                        .get_mut(&path_id)
+4736                        .expect("PathId::ZERO not yet abandoned")
+4737                        .update_initial_cid(remote_cid);
+4738                    self.remote_handshake_cid = remote_cid;
+4739                    self.original_remote_cid = remote_cid;
+4740                    state.remote_cid_set = true;
+4741                    self.state.move_to_handshake(state);
+4742                } else if remote_cid != self.remote_handshake_cid {
+4743                    debug!(
+4744                        "discarding packet with mismatched remote CID: {} != {}",
+4745                        self.remote_handshake_cid, remote_cid
+4746                    );
+4747                    return Ok(());
+4748                }
+4749
+4750                let starting_space = self.highest_space;
+4751                self.process_early_payload(now, path_id, packet, qlog)?;
+4752
+4753                if self.side.is_server()
+4754                    && starting_space == SpaceKind::Initial
+4755                    && self.highest_space != SpaceKind::Initial
+4756                {
+4757                    let params = self
+4758                        .crypto_state
+4759                        .session
+4760                        .transport_parameters()?
+4761                        .ok_or_else(|| {
+4762                            TransportError::new(
+4763                                TransportErrorCode::crypto(0x6d),
+4764                                "transport parameters missing".to_owned(),
+4765                            )
+4766                        })?;
+4767                    self.handle_peer_params(params, local_cid, remote_cid, now)?;
+4768                    self.issue_first_cids(now);
+4769                    self.init_0rtt(now);
+4770                }
+4771                Ok(())
+4772            }
+4773            Header::Long {
+4774                ty: LongType::ZeroRtt,
+4775                ..
+4776            } => {
+4777                self.process_payload(now, network_path, path_id, number.unwrap(), packet, qlog)?;
+4778                Ok(())
+4779            }
+4780            Header::VersionNegotiate { .. } => {
+4781                if self.total_authed_packets > 1 {
+4782                    return Ok(());
+4783                }
+4784                let supported = packet
+4785                    .payload
+4786                    .chunks(4)
+4787                    .any(|x| match <[u8; 4]>::try_from(x) {
+4788                        Ok(version) => self.version == u32::from_be_bytes(version),
+4789                        Err(_) => false,
+4790                    });
+4791                if supported {
+4792                    return Ok(());
+4793                }
+4794                debug!("remote doesn't support our version");
+4795                Err(ConnectionError::VersionMismatch)
+4796            }
+4797            Header::Short { .. } => unreachable!(
+4798                "short packets received during handshake are discarded in handle_packet"
+4799            ),
+4800        }
+4801    }
+4802
+4803    /// Process an Initial or Handshake packet payload
+4804    fn process_early_payload(
+4805        &mut self,
+4806        now: Instant,
+4807        path_id: PathId,
+4808        packet: Packet,
+4809        #[allow(unused)] qlog: &mut QlogRecvPacket,
+4810    ) -> Result<(), TransportError> {
+4811        debug_assert_ne!(packet.header.space(), SpaceKind::Data);
+4812        debug_assert_eq!(path_id, PathId::ZERO);
+4813        let payload_len = packet.payload.len();
+4814        let mut ack_eliciting = false;
+4815        for result in frame::Iter::new(packet.payload.freeze())? {
+4816            let frame = result?;
+4817            qlog.frame(&frame);
+4818            let span = match frame {
+4819                Frame::Padding => continue,
+4820                _ => Some(trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty)),
+4821            };
+4822
+4823            self.path_stats
+4824                .for_path(path_id)
+4825                .frame_rx
+4826                .record(frame.ty());
+4827
+4828            let _guard = span.as_ref().map(|x| x.enter());
+4829            ack_eliciting |= frame.is_ack_eliciting();
+4830
+4831            // Process frames
+4832            if frame.is_1rtt() && packet.header.space() != SpaceKind::Data {
+4833                return Err(TransportError::PROTOCOL_VIOLATION(
+4834                    "illegal frame type in handshake",
+4835                ));
+4836            }
+4837
+4838            match frame {
+4839                Frame::Padding | Frame::Ping => {}
+4840                Frame::Crypto(frame) => {
+4841                    self.read_crypto(packet.header.space().into(), &frame, payload_len)?;
+4842                }
+4843                Frame::Ack(ack) => {
+4844                    self.on_ack_received(now, packet.header.space().into(), ack)?;
+4845                }
+4846                Frame::PathAck(ack) => {
+4847                    span.as_ref()
+4848                        .map(|span| span.record("path", tracing::field::display(&ack.path_id)));
+4849                    self.on_path_ack_received(now, packet.header.space().into(), ack)?;
+4850                }
+4851                Frame::Close(reason) => {
+4852                    self.state.move_to_draining(Some(reason.into()));
+4853                    self.endpoint_events.push_back(EndpointEventInner::Draining);
+4854                    return Ok(());
+4855                }
+4856                _ => {
+4857                    let mut err =
+4858                        TransportError::PROTOCOL_VIOLATION("illegal frame type in handshake");
+4859                    err.frame = frame::MaybeFrame::Known(frame.ty());
+4860                    return Err(err);
+4861                }
+4862            }
+4863        }
+4864
+4865        if ack_eliciting {
+4866            // In the initial and handshake spaces, ACKs must be sent immediately
+4867            self.spaces[packet.header.space()]
+4868                .for_path(path_id)
+4869                .pending_acks
+4870                .set_immediate_ack_required();
+4871        }
+4872
+4873        self.write_crypto();
+4874        Ok(())
+4875    }
+4876
+4877    /// Processes the decrypted packet payload, always in the data space.
+4878    fn process_payload(
+4879        &mut self,
+4880        now: Instant,
+4881        network_path: FourTuple,
+4882        path_id: PathId,
+4883        number: u64,
+4884        packet: Packet,
+4885        #[allow(unused)] qlog: &mut QlogRecvPacket,
+4886    ) -> Result<(), TransportError> {
+4887        let is_multipath_negotiated = self.is_multipath_negotiated();
+4888        let payload = packet.payload.freeze();
+4889        let mut is_probing_packet = true;
+4890        let mut close = None;
+4891        let payload_len = payload.len();
+4892        let mut ack_eliciting = false;
+4893        // if this packet triggers a path migration and includes a observed address frame, it's
+4894        // stored here
+4895        let mut migration_observed_addr = None;
+4896        for result in frame::Iter::new(payload)? {
+4897            let frame = result?;
+4898            qlog.frame(&frame);
+4899            let span = match frame {
+4900                Frame::Padding => continue,
+4901                _ => trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty),
+4902            };
+4903
+4904            self.path_stats
+4905                .for_path(path_id)
+4906                .frame_rx
+4907                .record(frame.ty());
+4908            // Crypto, Stream and Datagram frames are special cased in order no pollute
+4909            // the log with payload data
+4910            match &frame {
+4911                Frame::Crypto(f) => {
+4912                    trace!(offset = f.offset, len = f.data.len(), "got frame CRYPTO");
+4913                }
+4914                Frame::Stream(f) => {
+4915                    trace!(id = %f.id, offset = f.offset, len = f.data.len(), fin = f.fin, "got frame STREAM");
+4916                }
+4917                Frame::Datagram(f) => {
+4918                    trace!(len = f.data.len(), "got frame DATAGRAM");
+4919                }
+4920                f => {
+4921                    trace!("got frame {f}");
+4922                }
+4923            }
+4924
+4925            let _guard = span.enter();
+4926            if packet.header.is_0rtt() {
+4927                match frame {
+4928                    Frame::Crypto(_) | Frame::Close(Close::Application(_)) => {
+4929                        return Err(TransportError::PROTOCOL_VIOLATION(
+4930                            "illegal frame type in 0-RTT",
+4931                        ));
+4932                    }
+4933                    _ => {
+4934                        if frame.is_1rtt() {
+4935                            return Err(TransportError::PROTOCOL_VIOLATION(
+4936                                "illegal frame type in 0-RTT",
+4937                            ));
+4938                        }
+4939                    }
+4940                }
+4941            }
+4942            ack_eliciting |= frame.is_ack_eliciting();
+4943
+4944            // Check whether this could be a probing packet
+4945            match frame {
+4946                Frame::Padding
+4947                | Frame::PathChallenge(_)
+4948                | Frame::PathResponse(_)
+4949                | Frame::NewConnectionId(_)
+4950                | Frame::ObservedAddr(_) => {}
+4951                _ => {
+4952                    is_probing_packet = false;
+4953                }
+4954            }
+4955
+4956            match frame {
+4957                Frame::Crypto(frame) => {
+4958                    self.read_crypto(SpaceId::Data, &frame, payload_len)?;
+4959                }
+4960                Frame::Stream(frame) => {
+4961                    if self.streams.received(frame, payload_len)?.should_transmit() {
+4962                        self.spaces[SpaceId::Data].pending.max_data = true;
+4963                    }
+4964                }
+4965                Frame::Ack(ack) => {
+4966                    self.on_ack_received(now, SpaceId::Data, ack)?;
+4967                }
+4968                Frame::PathAck(ack) => {
+4969                    if !self.is_multipath_negotiated() {
+4970                        return Err(TransportError::PROTOCOL_VIOLATION(
+4971                            "received PATH_ACK frame when multipath was not negotiated",
+4972                        ));
+4973                    }
+4974                    span.record("path", tracing::field::display(&ack.path_id));
+4975                    self.on_path_ack_received(now, SpaceId::Data, ack)?;
+4976                }
+4977                Frame::Padding | Frame::Ping => {}
+4978                Frame::Close(reason) => {
+4979                    close = Some(reason);
+4980                }
+4981                Frame::PathChallenge(challenge) => {
+4982                    let path = &mut self
+4983                        .path_mut(path_id)
+4984                        .expect("payload is processed only after the path becomes known");
+4985                    path.path_responses.push(number, challenge.0, network_path);
+4986                    // If we were passively migrated (e.g. NAT rebinding), our local_ip will
+4987                    // not match. Once we processed a non-probing packet the local_ip will
+4988                    // finally be updated.
+4989                    if network_path.remote == path.network_path.remote {
+4990                        // PATH_CHALLENGE on active path, possible off-path packet
+4991                        // forwarding attack. Send a non-probing packet to recover the
+4992                        // active path. See
+4993                        // https://www.rfc-editor.org/rfc/rfc9000.html#section-9.3.3-3. In
+4994                        // rare cases NAT probes might also appear on-path and would also
+4995                        // get a non-probing packet as response. There is little harm in
+4996                        // this.
+4997                        match self.peer_supports_ack_frequency() {
+4998                            true => self.immediate_ack(path_id),
+4999                            false => {
+5000                                self.ping_path(path_id).ok();
+5001                            }
+5002                        }
+5003                    }
+5004                }
+5005                Frame::PathResponse(response) => {
+5006                    // First try to see if this is a NAT probe response.
+5007                    if self
+5008                        .n0_nat_traversal
+5009                        .handle_path_response(network_path, response.0)
+5010                    {
+5011                        self.open_nat_traversed_paths(now);
+5012                    } else {
+5013                        // Try to see if this is a response to an on-path PATH_CHALLENGE.
+5014
+5015                        let path = self
+5016                            .paths
+5017                            .get_mut(&path_id)
+5018                            .expect("payload is processed only after the path becomes known");
+5019
+5020                        use PathTimer::*;
+5021                        use paths::OnPathResponseReceived::*;
+5022                        match path
+5023                            .data
+5024                            .on_path_response_received(now, response.0, network_path)
+5025                        {
+5026                            OnPath { was_open } => {
+5027                                let qlog = self.qlog.with_time(now);
+5028
+5029                                self.timers.stop(
+5030                                    Timer::PerPath(path_id, PathValidationFailed),
+5031                                    qlog.clone(),
+5032                                );
+5033                                self.timers.stop(
+5034                                    Timer::PerPath(path_id, AbandonFromValidation),
+5035                                    qlog.clone(),
+5036                                );
+5037
+5038                                let next_challenge = path
+5039                                    .data
+5040                                    .earliest_on_path_expiring_challenge()
+5041                                    .map(|time| time + self.ack_frequency.max_ack_delay_for_pto());
+5042                                self.timers.set_or_stop(
+5043                                    Timer::PerPath(path_id, PathChallengeLost),
+5044                                    next_challenge,
+5045                                    qlog,
+5046                                );
+5047
+5048                                if !was_open {
+5049                                    if is_multipath_negotiated {
+5050                                        self.events.push_back(Event::Path(
+5051                                            PathEvent::Established { id: path_id },
+5052                                        ));
+5053                                    }
+5054                                    if let Some(observed) =
+5055                                        path.data.last_observed_addr_report.as_ref()
+5056                                    {
+5057                                        self.events.push_back(Event::Path(
+5058                                            PathEvent::ObservedAddr {
+5059                                                id: path_id,
+5060                                                addr: observed.socket_addr(),
+5061                                            },
+5062                                        ));
+5063                                    }
+5064                                }
+5065                                if let Some((_, ref mut prev)) = path.prev {
+5066                                    // If an on-path response was received while there is a
+5067                                    // previous path from a migration, then the new path is
+5068                                    // validated and we can stop sending challenges that try to
+5069                                    // re-validate the previous path.
+5070                                    prev.reset_on_path_challenges();
+5071                                }
+5072                            }
+5073                            Ignored {
+5074                                sent_on,
+5075                                current_path,
+5076                            } => {
+5077                                debug!(%sent_on, %current_path, %response, "ignoring valid PATH_RESPONSE")
+5078                            }
+5079                            Unknown => debug!(%response, "ignoring invalid PATH_RESPONSE"),
+5080                        }
+5081                    }
+5082                }
+5083                Frame::MaxData(frame::MaxData(bytes)) => {
+5084                    self.streams.received_max_data(bytes);
+5085                }
+5086                Frame::MaxStreamData(frame::MaxStreamData { id, offset }) => {
+5087                    self.streams.received_max_stream_data(id, offset)?;
+5088                }
+5089                Frame::MaxStreams(frame::MaxStreams { dir, count }) => {
+5090                    self.streams.received_max_streams(dir, count)?;
+5091                }
+5092                Frame::ResetStream(frame) => {
+5093                    if self.streams.received_reset(frame)?.should_transmit() {
+5094                        self.spaces[SpaceId::Data].pending.max_data = true;
+5095                    }
+5096                }
+5097                Frame::DataBlocked(DataBlocked(offset)) => {
+5098                    debug!(offset, "peer claims to be blocked at connection level");
+5099                }
+5100                Frame::StreamDataBlocked(StreamDataBlocked { id, offset }) => {
+5101                    if id.initiator() == self.side.side() && id.dir() == Dir::Uni {
+5102                        debug!("got STREAM_DATA_BLOCKED on send-only {}", id);
+5103                        return Err(TransportError::STREAM_STATE_ERROR(
+5104                            "STREAM_DATA_BLOCKED on send-only stream",
+5105                        ));
+5106                    }
+5107                    debug!(
+5108                        stream = %id,
+5109                        offset, "peer claims to be blocked at stream level"
+5110                    );
+5111                }
+5112                Frame::StreamsBlocked(StreamsBlocked { dir, limit }) => {
+5113                    if limit > MAX_STREAM_COUNT {
+5114                        return Err(TransportError::FRAME_ENCODING_ERROR(
+5115                            "unrepresentable stream limit",
+5116                        ));
+5117                    }
+5118                    debug!(
+5119                        "peer claims to be blocked opening more than {} {} streams",
+5120                        limit, dir
+5121                    );
+5122                }
+5123                Frame::StopSending(frame::StopSending { id, error_code }) => {
+5124                    if id.initiator() != self.side.side() {
+5125                        if id.dir() == Dir::Uni {
+5126                            debug!("got STOP_SENDING on recv-only {}", id);
+5127                            return Err(TransportError::STREAM_STATE_ERROR(
+5128                                "STOP_SENDING on recv-only stream",
+5129                            ));
+5130                        }
+5131                    } else if self.streams.is_local_unopened(id) {
+5132                        return Err(TransportError::STREAM_STATE_ERROR(
+5133                            "STOP_SENDING on unopened stream",
+5134                        ));
+5135                    }
+5136                    self.streams.received_stop_sending(id, error_code);
+5137                }
+5138                Frame::RetireConnectionId(frame::RetireConnectionId { path_id, sequence }) => {
+5139                    if let Some(ref path_id) = path_id {
+5140                        span.record("path", tracing::field::display(&path_id));
+5141                    }
+5142                    let path_id = path_id.unwrap_or_default();
+5143                    match self.local_cid_state.get_mut(&path_id) {
+5144                        None => debug!(?path_id, "RETIRE_CONNECTION_ID for unknown path"),
+5145                        Some(cid_state) => {
+5146                            let allow_more_cids = cid_state
+5147                                .on_cid_retirement(sequence, self.peer_params.issue_cids_limit())?;
+5148
+5149                            // If the path has closed, we do not issue more CIDs for this path
+5150                            // For details see  https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.2.2
+5151                            // > an endpoint SHOULD provide new connection IDs for that path, if still open, using PATH_NEW_CONNECTION_ID frames.
+5152                            let has_path = !self.abandoned_paths.contains(&path_id);
+5153                            let allow_more_cids = allow_more_cids && has_path;
+5154
+5155                            debug_assert!(!self.state.is_drained()); // required for adding endpoint events, process_payload is never called for drained connections
+5156                            self.endpoint_events
+5157                                .push_back(EndpointEventInner::RetireConnectionId(
+5158                                    now,
+5159                                    path_id,
+5160                                    sequence,
+5161                                    allow_more_cids,
+5162                                ));
+5163                        }
+5164                    }
+5165                }
+5166                Frame::NewConnectionId(frame) => {
+5167                    let path_id = if let Some(path_id) = frame.path_id {
+5168                        if !self.is_multipath_negotiated() {
+5169                            return Err(TransportError::PROTOCOL_VIOLATION(
+5170                                "received PATH_NEW_CONNECTION_ID frame when multipath was not negotiated",
+5171                            ));
+5172                        }
+5173                        if path_id > self.local_max_path_id {
+5174                            return Err(TransportError::PROTOCOL_VIOLATION(
+5175                                "PATH_NEW_CONNECTION_ID contains path_id exceeding current max",
+5176                            ));
+5177                        }
+5178                        path_id
+5179                    } else {
+5180                        PathId::ZERO
+5181                    };
+5182
+5183                    if let Some(ref path_id) = frame.path_id {
+5184                        span.record("path", tracing::field::display(&path_id));
+5185                    }
+5186
+5187                    if self.abandoned_paths.contains(&path_id) {
+5188                        trace!("ignoring issued CID for abandoned path");
+5189                        continue;
+5190                    }
+5191                    let remote_cids = self
+5192                        .remote_cids
+5193                        .entry(path_id)
+5194                        .or_insert_with(|| CidQueue::new(frame.id));
+5195                    if remote_cids.active().is_empty() {
+5196                        return Err(TransportError::PROTOCOL_VIOLATION(
+5197                            "NEW_CONNECTION_ID when CIDs aren't in use",
+5198                        ));
+5199                    }
+5200                    if frame.retire_prior_to > frame.sequence {
+5201                        return Err(TransportError::PROTOCOL_VIOLATION(
+5202                            "NEW_CONNECTION_ID retiring unissued CIDs",
+5203                        ));
+5204                    }
+5205
+5206                    use crate::cid_queue::InsertError;
+5207                    match remote_cids.insert(frame) {
+5208                        Ok(None) => {
+5209                            self.open_nat_traversed_paths(now);
+5210                        }
+5211                        Ok(Some((retired, reset_token))) => {
+5212                            let pending_retired =
+5213                                &mut self.spaces[SpaceId::Data].pending.retire_cids;
+5214                            /// Ensure `pending_retired` cannot grow without bound. Limit is
+5215                            /// somewhat arbitrary but very permissive.
+5216                            const MAX_PENDING_RETIRED_CIDS: u64 = CidQueue::LEN as u64 * 10;
+5217                            // We don't bother counting in-flight frames because those are bounded
+5218                            // by congestion control.
+5219                            if (pending_retired.len() as u64)
+5220                                .saturating_add(retired.end.saturating_sub(retired.start))
+5221                                > MAX_PENDING_RETIRED_CIDS
+5222                            {
+5223                                return Err(TransportError::CONNECTION_ID_LIMIT_ERROR(
+5224                                    "queued too many retired CIDs",
+5225                                ));
+5226                            }
+5227                            pending_retired.extend(retired.map(|seq| (path_id, seq)));
+5228                            self.set_reset_token(path_id, network_path.remote, reset_token);
+5229                            self.open_nat_traversed_paths(now);
+5230                        }
+5231                        Err(InsertError::ExceedsLimit) => {
+5232                            return Err(TransportError::CONNECTION_ID_LIMIT_ERROR(""));
+5233                        }
+5234                        Err(InsertError::Retired) => {
+5235                            trace!("discarding already-retired");
+5236                            // RETIRE_CONNECTION_ID might not have been previously sent if e.g. a
+5237                            // range of connection IDs larger than the active connection ID limit
+5238                            // was retired all at once via retire_prior_to.
+5239                            self.spaces[SpaceId::Data]
+5240                                .pending
+5241                                .retire_cids
+5242                                .push((path_id, frame.sequence));
+5243                            continue;
+5244                        }
+5245                    };
+5246
+5247                    if self.side.is_server()
+5248                        && path_id == PathId::ZERO
+5249                        && self
+5250                            .remote_cids
+5251                            .get(&PathId::ZERO)
+5252                            .map(|cids| cids.active_seq() == 0)
+5253                            .unwrap_or_default()
+5254                    {
+5255                        // We're a server still using the initial remote CID for the client, so
+5256                        // let's switch immediately to enable clientside stateless resets.
+5257                        self.update_remote_cid(PathId::ZERO);
+5258                    }
+5259                }
+5260                Frame::NewToken(NewToken { token }) => {
+5261                    let ConnectionSide::Client {
+5262                        token_store,
+5263                        server_name,
+5264                        ..
+5265                    } = &self.side
+5266                    else {
+5267                        return Err(TransportError::PROTOCOL_VIOLATION("client sent NEW_TOKEN"));
+5268                    };
+5269                    if token.is_empty() {
+5270                        return Err(TransportError::FRAME_ENCODING_ERROR("empty token"));
+5271                    }
+5272                    trace!("got new token");
+5273                    token_store.insert(server_name, token);
+5274                }
+5275                Frame::Datagram(datagram) => {
+5276                    if self
+5277                        .datagrams
+5278                        .received(datagram, &self.config.datagram_receive_buffer_size)?
+5279                    {
+5280                        self.events.push_back(Event::DatagramReceived);
+5281                    }
+5282                }
+5283                Frame::AckFrequency(ack_frequency) => {
+5284                    // This frame can only be sent in the Data space
+5285
+5286                    if !self.ack_frequency.ack_frequency_received(&ack_frequency)? {
+5287                        // The AckFrequency frame is stale (we have already received a more
+5288                        // recent one)
+5289                        continue;
+5290                    }
+5291
+5292                    // Update the params for all of our paths
+5293                    for (path_id, space) in self.spaces[SpaceId::Data].number_spaces.iter_mut() {
+5294                        space.pending_acks.set_ack_frequency_params(&ack_frequency);
+5295
+5296                        // Our `max_ack_delay` has been updated, so we may need to adjust
+5297                        // its associated timeout.
+5298                        // Packets received on abandoned paths are always acknowledged immediately.
+5299                        if !self.abandoned_paths.contains(path_id)
+5300                            && let Some(timeout) = space
+5301                                .pending_acks
+5302                                .max_ack_delay_timeout(self.ack_frequency.max_ack_delay)
+5303                        {
+5304                            self.timers.set(
+5305                                Timer::PerPath(*path_id, PathTimer::MaxAckDelay),
+5306                                timeout,
+5307                                self.qlog.with_time(now),
+5308                            );
+5309                        }
+5310                    }
+5311                }
+5312                Frame::ImmediateAck => {
+5313                    // This frame can only be sent in the Data space
+5314                    for pns in self.spaces[SpaceId::Data].iter_paths_mut() {
+5315                        pns.pending_acks.set_immediate_ack_required();
+5316                    }
+5317                }
+5318                Frame::HandshakeDone => {
+5319                    if self.side.is_server() {
+5320                        return Err(TransportError::PROTOCOL_VIOLATION(
+5321                            "client sent HANDSHAKE_DONE",
+5322                        ));
+5323                    }
+5324                    if self.crypto_state.has_keys(EncryptionLevel::Handshake) {
+5325                        self.discard_space(now, SpaceKind::Handshake);
+5326                        self.events.push_back(Event::HandshakeConfirmed);
+5327                        trace!("handshake confirmed");
+5328                    }
+5329                }
+5330                Frame::ObservedAddr(observed) => {
+5331                    // check if params allows the peer to send report and this node to receive it
+5332                    trace!(seq_no = %observed.seq_no, ip = %observed.ip, port = observed.port);
+5333                    if !self
+5334                        .peer_params
+5335                        .address_discovery_role
+5336                        .should_report(&self.config.address_discovery_role)
+5337                    {
+5338                        return Err(TransportError::PROTOCOL_VIOLATION(
+5339                            "received OBSERVED_ADDRESS frame when not negotiated",
+5340                        ));
+5341                    }
+5342                    // must only be sent in data space
+5343                    if packet.header.space() != SpaceKind::Data {
+5344                        return Err(TransportError::PROTOCOL_VIOLATION(
+5345                            "OBSERVED_ADDRESS frame outside data space",
+5346                        ));
+5347                    }
+5348
+5349                    let path = self.path_data_mut(path_id);
+5350                    if path.network_path.remote == network_path.remote {
+5351                        if let Some(updated) = path.update_observed_addr_report(observed)
+5352                            && path.open_status == paths::OpenStatus::Informed
+5353                        {
+5354                            self.events.push_back(Event::Path(PathEvent::ObservedAddr {
+5355                                id: path_id,
+5356                                addr: updated,
+5357                            }));
+5358                            // otherwise the event is reported when the path is deemed open
+5359                        }
+5360                    } else {
+5361                        // include in migration
+5362                        migration_observed_addr = Some(observed)
+5363                    }
+5364                }
+5365                Frame::PathAbandon(frame::PathAbandon {
+5366                    path_id,
+5367                    error_code,
+5368                }) => {
+5369                    span.record("path", tracing::field::display(&path_id));
+5370                    match self.close_path_inner(
+5371                        now,
+5372                        path_id,
+5373                        PathAbandonReason::RemoteAbandoned {
+5374                            error_code: error_code.into(),
+5375                        },
+5376                    ) {
+5377                        Ok(()) => {
+5378                            trace!("peer abandoned path");
+5379                        }
+5380                        Err(ClosePathError::ClosedPath) => {
+5381                            trace!("peer abandoned already closed path");
+5382                        }
+5383                        Err(ClosePathError::MultipathNotNegotiated) => {
+5384                            return Err(TransportError::PROTOCOL_VIOLATION(
+5385                                "received PATH_ABANDON frame when multipath was not negotiated",
+5386                            ));
+5387                        }
+5388                        Err(ClosePathError::LastOpenPath) => {
+5389                            // Not reachable: close_path_inner allows remote abandons
+5390                            // for the last path. But handle gracefully just in case.
+5391                            error!(
+5392                                "peer abandoned last path but close_path_inner returned LastOpenPath"
+5393                            );
+5394                        }
+5395                    };
+5396
+5397                    // Start draining the path if it still exists and hasn't started draining yet.
+5398                    if let Some(path) = self.paths.get_mut(&path_id)
+5399                        && !mem::replace(&mut path.data.draining, true)
+5400                    {
+5401                        let ack_delay = self.ack_frequency.max_ack_delay_for_pto();
+5402                        let pto = path.data.rtt.pto_base() + ack_delay;
+5403                        self.timers.set(
+5404                            Timer::PerPath(path_id, PathTimer::PathDrained),
+5405                            now + 3 * pto,
+5406                            self.qlog.with_time(now),
+5407                        );
+5408
+5409                        self.set_max_path_id(now, self.local_max_path_id.saturating_add(1u8));
+5410                    }
+5411                }
+5412                Frame::PathStatusAvailable(info) => {
+5413                    span.record("path", tracing::field::display(&info.path_id));
+5414                    if self.is_multipath_negotiated() {
+5415                        self.on_path_status(
+5416                            info.path_id,
+5417                            PathStatus::Available,
+5418                            info.status_seq_no,
+5419                        );
+5420                    } else {
+5421                        return Err(TransportError::PROTOCOL_VIOLATION(
+5422                            "received PATH_STATUS_AVAILABLE frame when multipath was not negotiated",
+5423                        ));
+5424                    }
+5425                }
+5426                Frame::PathStatusBackup(info) => {
+5427                    span.record("path", tracing::field::display(&info.path_id));
+5428                    if self.is_multipath_negotiated() {
+5429                        self.on_path_status(info.path_id, PathStatus::Backup, info.status_seq_no);
+5430                    } else {
+5431                        return Err(TransportError::PROTOCOL_VIOLATION(
+5432                            "received PATH_STATUS_BACKUP frame when multipath was not negotiated",
+5433                        ));
+5434                    }
+5435                }
+5436                Frame::MaxPathId(frame::MaxPathId(path_id)) => {
+5437                    span.record("path", tracing::field::display(&path_id));
+5438                    if !self.is_multipath_negotiated() {
+5439                        return Err(TransportError::PROTOCOL_VIOLATION(
+5440                            "received MAX_PATH_ID frame when multipath was not negotiated",
+5441                        ));
+5442                    }
+5443                    // frames that do not increase the path id are ignored
+5444                    if path_id > self.remote_max_path_id {
+5445                        self.remote_max_path_id = path_id;
+5446                        self.issue_first_path_cids(now);
+5447                        self.open_nat_traversed_paths(now);
+5448                    }
+5449                }
+5450                Frame::PathsBlocked(frame::PathsBlocked(max_path_id)) => {
+5451                    // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST
+5452                    // be treated as a connection error of type PROTOCOL_VIOLATION.
+5453                    // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids>
+5454                    if self.is_multipath_negotiated() {
+5455                        if max_path_id > self.local_max_path_id {
+5456                            return Err(TransportError::PROTOCOL_VIOLATION(
+5457                                "PATHS_BLOCKED maximum path identifier was larger than local maximum",
+5458                            ));
+5459                        }
+5460                        debug!("received PATHS_BLOCKED({:?})", max_path_id);
+5461                        // TODO(@divma): ensure max concurrent paths
+5462                    } else {
+5463                        return Err(TransportError::PROTOCOL_VIOLATION(
+5464                            "received PATHS_BLOCKED frame when not multipath was not negotiated",
+5465                        ));
+5466                    }
+5467                }
+5468                Frame::PathCidsBlocked(frame::PathCidsBlocked { path_id, next_seq }) => {
+5469                    // Nothing to do.  This is recorded in the frame stats, but otherwise we
+5470                    // always issue all CIDs we're allowed to issue, so either this is an
+5471                    // impatient peer or a bug on our side.
+5472
+5473                    // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST
+5474                    // be treated as a connection error of type PROTOCOL_VIOLATION.
+5475                    // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids>
+5476                    if self.is_multipath_negotiated() {
+5477                        if path_id > self.local_max_path_id {
+5478                            return Err(TransportError::PROTOCOL_VIOLATION(
+5479                                "PATH_CIDS_BLOCKED path identifier was larger than local maximum",
+5480                            ));
+5481                        }
+5482                        if next_seq.0
+5483                            > self
+5484                                .local_cid_state
+5485                                .get(&path_id)
+5486                                .map(|cid_state| cid_state.active_seq().1 + 1)
+5487                                .unwrap_or_default()
+5488                        {
+5489                            return Err(TransportError::PROTOCOL_VIOLATION(
+5490                                "PATH_CIDS_BLOCKED next sequence number larger than in local state",
+5491                            ));
+5492                        }
+5493                        debug!(%path_id, %next_seq, "received PATH_CIDS_BLOCKED");
+5494                    } else {
+5495                        return Err(TransportError::PROTOCOL_VIOLATION(
+5496                            "received PATH_CIDS_BLOCKED frame when not multipath was not negotiated",
+5497                        ));
+5498                    }
+5499                }
+5500                Frame::AddAddress(addr) => {
+5501                    let client_state = match self.n0_nat_traversal.client_side_mut() {
+5502                        Ok(state) => state,
+5503                        Err(err) => {
+5504                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
+5505                                "Nat traversal(ADD_ADDRESS): {err}"
+5506                            )));
+5507                        }
+5508                    };
+5509
+5510                    if !client_state.check_remote_address(&addr) {
+5511                        // if the address is not valid we flag it, but update anyway
+5512                        warn!(?addr, "server sent illegal ADD_ADDRESS frame");
+5513                    }
+5514
+5515                    match client_state.add_remote_address(addr) {
+5516                        Ok(maybe_added) => {
+5517                            if let Some(added) = maybe_added {
+5518                                self.events.push_back(Event::NatTraversal(
+5519                                    n0_nat_traversal::Event::AddressAdded(added),
+5520                                ));
+5521                            }
+5522                        }
+5523                        Err(e) => {
+5524                            warn!(%e, "failed to add remote address")
+5525                        }
+5526                    }
+5527                }
+5528                Frame::RemoveAddress(addr) => {
+5529                    let client_state = match self.n0_nat_traversal.client_side_mut() {
+5530                        Ok(state) => state,
+5531                        Err(err) => {
+5532                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
+5533                                "Nat traversal(REMOVE_ADDRESS): {err}"
+5534                            )));
+5535                        }
+5536                    };
+5537                    if let Some(removed_addr) = client_state.remove_remote_address(addr) {
+5538                        self.events.push_back(Event::NatTraversal(
+5539                            n0_nat_traversal::Event::AddressRemoved(removed_addr),
+5540                        ));
+5541                    }
+5542                }
+5543                Frame::ReachOut(reach_out) => {
+5544                    let ipv6 = self.is_ipv6();
+5545                    let server_state = match self.n0_nat_traversal.server_side_mut() {
+5546                        Ok(state) => state,
+5547                        Err(err) => {
+5548                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
+5549                                "Nat traversal(REACH_OUT): {err}"
+5550                            )));
+5551                        }
+5552                    };
+5553
+5554                    let round_before = server_state.current_round();
+5555
+5556                    if let Err(err) = server_state.handle_reach_out(reach_out, ipv6) {
+5557                        return Err(TransportError::PROTOCOL_VIOLATION(format!(
+5558                            "Nat traversal(REACH_OUT): {err}"
+5559                        )));
+5560                    }
+5561
+5562                    if server_state.current_round() > round_before {
+5563                        // A new round was started, reset the NAT probe retry timer.
+5564                        if let Some(delay) =
+5565                            self.n0_nat_traversal.retry_delay(self.config.initial_rtt)
+5566                        {
+5567                            self.timers.set(
+5568                                Timer::Conn(ConnTimer::NatTraversalProbeRetry),
+5569                                now + delay,
+5570                                self.qlog.with_time(now),
+5571                            );
+5572                        }
+5573                    }
+5574                }
+5575            }
+5576        }
+5577
+5578        let space = self.spaces[SpaceId::Data].for_path(path_id);
+5579        if space
+5580            .pending_acks
+5581            .packet_received(now, number, ack_eliciting, &space.dedup)
+5582        {
+5583            if self.abandoned_paths.contains(&path_id) {
+5584                // § 3.4.3 QUIC-MULTIPATH: promptly send ACKs for packets received from
+5585                // abandoned paths.
+5586                space.pending_acks.set_immediate_ack_required();
+5587            } else {
+5588                self.timers.set(
+5589                    Timer::PerPath(path_id, PathTimer::MaxAckDelay),
+5590                    now + self.ack_frequency.max_ack_delay,
+5591                    self.qlog.with_time(now),
+5592                );
+5593            }
+5594        }
+5595
+5596        // Issue stream ID credit due to ACKs of outgoing finish/resets and incoming finish/resets
+5597        // on stopped streams. Incoming finishes/resets on open streams are not handled here as they
+5598        // are only freed, and hence only issue credit, once the application has been notified
+5599        // during a read on the stream.
+5600        let pending = &mut self.spaces[SpaceId::Data].pending;
+5601        self.streams.queue_max_stream_id(pending);
+5602
+5603        if let Some(reason) = close {
+5604            self.state.move_to_draining(Some(reason.into()));
+5605            self.endpoint_events.push_back(EndpointEventInner::Draining);
+5606            self.connection_close_pending = true;
+5607        }
+5608
+5609        // For Multipath any packet triggers migration. For RFC9000 or QNT (+ Multipath)
+5610        // only non-probing packets trigger migration.
+5611        let migrate_on_any_packet =
+5612            self.is_multipath_negotiated() && !self.n0_nat_traversal.is_negotiated();
+5613
+5614        // Only migrate if this is the largest packet number seen.
+5615        let is_largest_received_pn = Some(number)
+5616            == self.spaces[SpaceId::Data]
+5617                .for_path(path_id)
+5618                .largest_received_packet_number;
+5619
+5620        // If we receive a non-probing packet on a new local IP that means we had a NAT
+5621        // rebinding-like migration. We update our local address but do not otherwise
+5622        // validate the new path, we only need to validate the path if the peer migrates per
+5623        // RFC9000 §9: https://www.rfc-editor.org/rfc/rfc9000.html#section-9-4
+5624        if (migrate_on_any_packet || !is_probing_packet)
+5625            && is_largest_received_pn
+5626            && self.local_ip_may_migrate()
+5627            && let Some(new_local_ip) = network_path.local_ip
+5628        {
+5629            let path_data = self.path_data_mut(path_id);
+5630            if path_data
+5631                .network_path
+5632                .local_ip
+5633                .is_some_and(|ip| ip != new_local_ip)
+5634            {
+5635                debug!(
+5636                    %path_id,
+5637                    new_4tuple = %network_path,
+5638                    prev_4tuple = %path_data.network_path,
+5639                    "local address passive migration"
+5640                );
+5641            }
+5642            path_data.network_path.local_ip = Some(new_local_ip)
+5643        }
+5644
+5645        // If the peer migrated to a new address, trigger migration.
+5646        if self.peer_may_migrate()
+5647            && (migrate_on_any_packet || !is_probing_packet)
+5648            && is_largest_received_pn
+5649            && network_path.remote != self.path_data(path_id).network_path.remote
+5650        {
+5651            self.migrate(path_id, now, network_path, migration_observed_addr);
+5652            // Break linkability, if possible
+5653            self.update_remote_cid(path_id);
+5654            self.spin = false;
+5655        }
+5656
+5657        Ok(())
+5658    }
+5659
+5660    /// Opens any paths that have been successfully NAT traversed.
+5661    fn open_nat_traversed_paths(&mut self, now: Instant) {
+5662        while let Some(network_path) = self
+5663            .n0_nat_traversal
+5664            .client_side_mut()
+5665            .ok()
+5666            .and_then(|s| s.pop_pending_path_open())
+5667        {
+5668            match self.open_path_ensure(network_path, PathStatus::Backup, now) {
+5669                Ok((path_id, already_existed)) => {
+5670                    debug!(
+5671                        %path_id,
+5672                        ?network_path,
+5673                        new_path = !already_existed,
+5674                        "Opened NAT traversal path",
+5675                    );
+5676                }
+5677                Err(err) => match err {
+5678                    PathError::MultipathNotNegotiated
+5679                    | PathError::ServerSideNotAllowed
+5680                    | PathError::ValidationFailed
+5681                    | PathError::InvalidRemoteAddress(_) => {
+5682                        error!(
+5683                            ?err,
+5684                            ?network_path,
+5685                            "Failed to open path for successful NAT traversal"
+5686                        );
+5687                    }
+5688                    PathError::MaxPathIdReached | PathError::RemoteCidsExhausted => {
+5689                        // Temporary error, put back.
+5690                        self.n0_nat_traversal
+5691                            .client_side_mut()
+5692                            .map(|s| s.push_pending_path_open(network_path))
+5693                            .ok();
+5694                        debug!(
+5695                            ?err,
+5696                            ?network_path,
+5697                            "Blocked opening NAT traversal path, enqueued"
+5698                        );
+5699                        return;
+5700                    }
+5701                },
+5702            }
+5703        }
+5704    }
+5705
+5706    /// Migrates the 4-tuple of the path.
+5707    ///
+5708    /// This creates a new [`PathData`] for the migrated path and stores the previous
+5709    /// [`PathData`] in [`PathState::prev`].
+5710    fn migrate(
+5711        &mut self,
+5712        path_id: PathId,
+5713        now: Instant,
+5714        network_path: FourTuple,
+5715        observed_addr: Option<ObservedAddr>,
+5716    ) {
+5717        trace!(
+5718            new_4tuple = %network_path,
+5719            prev_4tuple = %self.path_data(path_id).network_path,
+5720            %path_id,
+5721            "migration initiated",
+5722        );
+5723        self.path_generation_counter = self.path_generation_counter.wrapping_add(1);
+5724        // TODO(@divma): conditions for path migration in multipath are very specific, check them
+5725        // again to prevent path migrations that should actually create a new path
+5726
+5727        // Reset rtt/congestion state for new path unless it looks like a NAT rebinding.
+5728        // Note that the congestion window will not grow until validation terminates. Helps mitigate
+5729        // amplification attacks performed by spoofing source addresses.
+5730        let prev_pto = self.pto(SpaceKind::Data, path_id);
+5731        let path = self.paths.get_mut(&path_id).expect("known path");
+5732        let mut new_path_data = if network_path.remote.is_ipv4()
+5733            && network_path.remote.ip() == path.data.network_path.remote.ip()
+5734        {
+5735            PathData::from_previous(network_path, &path.data, self.path_generation_counter, now)
+5736        } else {
+5737            let peer_max_udp_payload_size =
+5738                u16::try_from(self.peer_params.max_udp_payload_size.into_inner())
+5739                    .unwrap_or(u16::MAX);
+5740            PathData::new(
+5741                network_path,
+5742                self.allow_mtud,
+5743                Some(peer_max_udp_payload_size),
+5744                self.path_generation_counter,
+5745                now,
+5746                &self.config,
+5747            )
+5748        };
+5749        new_path_data.last_observed_addr_report = path.data.last_observed_addr_report.clone();
+5750        if let Some(report) = observed_addr
+5751            && let Some(updated) = new_path_data.update_observed_addr_report(report)
+5752        {
+5753            tracing::info!("adding observed addr event from migration");
+5754            self.events.push_back(Event::Path(PathEvent::ObservedAddr {
+5755                id: path_id,
+5756                addr: updated,
+5757            }));
+5758        }
+5759        new_path_data.pending_on_path_challenge = true;
+5760
+5761        let mut prev_path_data = mem::replace(&mut path.data, new_path_data);
+5762
+5763        // Only store this as previous path if it was validated. For all we know there could
+5764        // already be a previous path stored which might have been validated in the past,
+5765        // which is more valuable than one that's not yet validated.
+5766        //
+5767        // With multipath it is possible that there are no remote CIDs for the path ID
+5768        // yet. In this case we would never have sent on this path yet and would not be able
+5769        // to send a PATH_CHALLENGE either, which is currently a fire-and-forget affair
+5770        // anyway. So don't store such a path either.
+5771        if !prev_path_data.validated
+5772            && let Some(cid) = self.remote_cids.get(&path_id).map(CidQueue::active)
+5773        {
+5774            prev_path_data.pending_on_path_challenge = true;
+5775            // We haven't updated the remote CID yet, this captures the remote CID we were using on
+5776            // the previous path.
+5777            path.prev = Some((cid, prev_path_data));
+5778        }
+5779
+5780        // We need to re-assign the correct remote to this path in qlog
+5781        self.qlog.emit_tuple_assigned(path_id, network_path, now);
+5782
+5783        self.timers.set(
+5784            Timer::PerPath(path_id, PathTimer::PathValidationFailed),
+5785            now + 3 * cmp::max(self.pto(SpaceKind::Data, path_id), prev_pto),
+5786            self.qlog.with_time(now),
+5787        );
+5788    }
+5789
+5790    /// Handle a change in the local address, i.e. an active migration
+5791    ///
+5792    /// In the general (non-multipath) case, paths will perform a RFC9000 migration and be pinged
+5793    /// for a liveness check. This is the behaviour of a path assumed to be recoverable, even if
+5794    /// this is not the case.
+5795    ///
+5796    /// Clients in a connection in which multipath has been negotiated should migrate paths to new
+5797    /// [`PathId`]s. For paths that are known to be non-recoverable can be migrated to a new
+5798    /// [`PathId`] by closing the current path, and opening a new one to the same remote. Treating
+5799    /// paths as non recoverable when necessary accelerates connectivity re-establishment, or might
+5800    /// allow it altogether.
+5801    ///
+5802    /// The optional `hint` allows callers to indicate when paths are non-recoverable and should be
+5803    /// migrated to new a [`PathId`].
+5804    // NOTE: only clients are allowed to migrate, but generally dealing with RFC9000 migrations is
+5805    // lacking <https://github.com/n0-computer/noq/issues/364>
+5806    pub fn handle_network_change(&mut self, hint: Option<&dyn NetworkChangeHint>, now: Instant) {
+5807        debug!("network changed");
+5808        if self.state.is_drained() {
+5809            return;
+5810        }
+5811        if self.highest_space < SpaceKind::Data {
+5812            for path in self.paths.values_mut() {
+5813                // Clear the local address for it to be obtained from the socket again.
+5814                path.data.network_path.local_ip = None;
+5815            }
+5816
+5817            self.update_remote_cid(PathId::ZERO);
+5818            self.ping();
+5819
+5820            return;
+5821        }
+5822
+5823        // Paths that can't recover so a new path should be open instead. If multipath is not
+5824        // negotiated, this will be empty.
+5825        let mut non_recoverable_paths = Vec::default();
+5826        let mut recoverable_paths = Vec::default();
+5827        let mut open_paths = 0;
+5828
+5829        let is_multipath_negotiated = self.is_multipath_negotiated();
+5830        let is_client = self.side().is_client();
+5831        let immediate_ack_allowed = self.peer_supports_ack_frequency();
+5832
+5833        for (path_id, path) in self.paths.iter_mut() {
+5834            if self.abandoned_paths.contains(path_id) {
+5835                continue;
+5836            }
+5837            open_paths += 1;
+5838
+5839            // Read the network path BEFORE clearing local_ip, so the hint can
+5840            // check which interface the path was using.
+5841            let network_path = path.data.network_path;
+5842
+5843            // Clear the local address for it to be obtained from the socket again. This applies to
+5844            // all paths, regardless of being considered recoverable or not
+5845            path.data.network_path.local_ip = None;
+5846            let remote = network_path.remote;
+5847
+5848            // Without multipath, the connection tries to recover the single path, whereas with
+5849            // multipath, even in a single-path scenario, we attempt to migrate the path to a new
+5850            // PathId.
+5851            let attempt_to_recover = if is_multipath_negotiated {
+5852                // Use the hint to determine if the path can recover. When no hint is
+5853                // provided, clients default to non-recoverable (abandon and re-open)
+5854                // while servers default to recoverable (attempt in-place recovery).
+5855                hint.map(|h| h.is_path_recoverable(*path_id, network_path))
+5856                    .unwrap_or(!is_client)
+5857            } else {
+5858                // In the non multipath case, we try to recover the single active path
+5859                true
+5860            };
+5861
+5862            if attempt_to_recover {
+5863                recoverable_paths.push((*path_id, remote));
+5864            } else {
+5865                non_recoverable_paths.push((*path_id, remote, path.data.local_status()))
+5866            }
+5867        }
+5868
+5869        /* NON RECOVERABLE PATHS */
+5870        // This are handled first, so that in case the treatment intended for these fails, we can
+5871        // go the recoverable route instead.
+5872
+5873        // Decide if we need to close first or open first in the multipath case.
+5874        // - Opening first has a higher risk of getting limited by the negotiated MAX_PATH_ID.
+5875        // - Closing first risks this being the only open path.
+5876        // We prefer closing paths first unless we identify this is the last open path.
+5877        let open_first = open_paths == non_recoverable_paths.len();
+5878
+5879        for (path_id, remote, status) in non_recoverable_paths.into_iter() {
+5880            let network_path = FourTuple {
+5881                remote,
+5882                local_ip: None, /* allow the local ip to be discovered */
+5883            };
+5884
+5885            if open_first && let Err(e) = self.open_path(network_path, status, now) {
+5886                if self.side().is_client() {
+5887                    debug!(%e, "Failed to open new path for network change");
+5888                }
+5889                // if this fails, let the path try to recover itself
+5890                recoverable_paths.push((path_id, remote));
+5891                continue;
+5892            }
+5893
+5894            if let Err(e) =
+5895                self.close_path_inner(now, path_id, PathAbandonReason::UnusableAfterNetworkChange)
+5896            {
+5897                debug!(%e,"Failed to close unrecoverable path after network change");
+5898                recoverable_paths.push((path_id, remote));
+5899                continue;
+5900            }
+5901
+5902            if !open_first && let Err(e) = self.open_path(network_path, status, now) {
+5903                // Path has already been closed if we got here. Since the path was not recoverable,
+5904                // this might be desirable in any case, because other paths exist (!open_first) and
+5905                // this was is considered non recoverable
+5906                debug!(%e,"Failed to open new path for network change");
+5907            }
+5908        }
+5909
+5910        /* RECOVERABLE PATHS */
+5911
+5912        for (path_id, remote) in recoverable_paths.into_iter() {
+5913            // Schedule a Ping for a liveness check.
+5914            if let Some(path_space) = self.spaces[SpaceId::Data].number_spaces.get_mut(&path_id) {
+5915                path_space.ping_pending = true;
+5916
+5917                if immediate_ack_allowed {
+5918                    path_space.immediate_ack_pending = true;
+5919                }
+5920            }
+5921
+5922            // Reset PTO backoff so retransmits resume promptly. Congestion controller and
+5923            // RTT are intentionally preserved for recoverable paths. We explicitly allow
+5924            // this reset also during the handshake, so do not check
+5925            // Self::peer_competed_handshake_address_validation.
+5926            if let Some(path) = self.paths.get_mut(&path_id) {
+5927                path.data.pto_count = 0;
+5928            }
+5929            self.set_loss_detection_timer(now, path_id);
+5930
+5931            let Some((reset_token, retired)) =
+5932                self.remote_cids.get_mut(&path_id).and_then(CidQueue::next)
+5933            else {
+5934                continue;
+5935            };
+5936
+5937            // Retire the current remote CID and any CIDs we had to skip.
+5938            self.spaces[SpaceId::Data]
+5939                .pending
+5940                .retire_cids
+5941                .extend(retired.map(|seq| (path_id, seq)));
+5942
+5943            debug_assert!(!self.state.is_drained()); // required for endpoint_events, checked above
+5944            self.endpoint_events
+5945                .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token));
+5946        }
+5947    }
+5948
+5949    /// Switch to a previously unused remote connection ID, if possible
+5950    fn update_remote_cid(&mut self, path_id: PathId) {
+5951        let Some((reset_token, retired)) = self
+5952            .remote_cids
+5953            .get_mut(&path_id)
+5954            .and_then(|cids| cids.next())
+5955        else {
+5956            return;
+5957        };
+5958
+5959        // Retire the current remote CID and any CIDs we had to skip.
+5960        self.spaces[SpaceId::Data]
+5961            .pending
+5962            .retire_cids
+5963            .extend(retired.map(|seq| (path_id, seq)));
+5964        let remote = self.path_data(path_id).network_path.remote;
+5965        self.set_reset_token(path_id, remote, reset_token);
+5966    }
+5967
+5968    /// Sends this reset token to the endpoint
+5969    ///
+5970    /// The endpoint needs to know the reset tokens issued by the peer, so that if the peer
+5971    /// sends a reset token it knows to route it to this connection. See RFC 9000 section
+5972    /// 10.3. Stateless Reset.
+5973    ///
+5974    /// Reset tokens are different for each path, the endpoint identifies paths by peer
+5975    /// socket address however, not by path ID.
+5976    fn set_reset_token(&mut self, path_id: PathId, remote: SocketAddr, reset_token: ResetToken) {
+5977        debug_assert!(!self.state.is_drained()); // required for endpoint events, set_reset_token is never called for drained connections
+5978        self.endpoint_events
+5979            .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token));
+5980
+5981        // During the handshake the server sends a reset token in the transport
+5982        // parameters. When we are the client and we receive the reset token during the
+5983        // handshake we want this to affect our peer transport parameters.
+5984        // TODO(flub): Pretty sure this is pointless, the entire params is overwritten
+5985        //    shortly after this was called.  And then the params don't have this anymore.
+5986        if path_id == PathId::ZERO {
+5987            self.peer_params.stateless_reset_token = Some(reset_token);
+5988        }
+5989    }
+5990
+5991    /// Issue an initial set of connection IDs to the peer upon connection
+5992    fn issue_first_cids(&mut self, now: Instant) {
+5993        if self
+5994            .local_cid_state
+5995            .get(&PathId::ZERO)
+5996            .expect("PathId::ZERO exists when the connection is created")
+5997            .cid_len()
+5998            == 0
+5999        {
+6000            return;
+6001        }
+6002
+6003        // Subtract 1 to account for the CID we supplied while handshaking
+6004        let mut n = self.peer_params.issue_cids_limit() - 1;
+6005        if let ConnectionSide::Server { server_config } = &self.side
+6006            && server_config.has_preferred_address()
+6007        {
+6008            // We also sent a CID in the transport parameters
+6009            n -= 1;
+6010        }
+6011        debug_assert!(!self.state.is_drained()); // requirement for endpoint_events
+6012        self.endpoint_events
+6013            .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n));
+6014    }
+6015
+6016    /// Issues an initial set of CIDs for paths that have not yet had any CIDs issued
+6017    ///
+6018    /// Later CIDs are issued when CIDs expire or are retired by the peer.
+6019    fn issue_first_path_cids(&mut self, now: Instant) {
+6020        if let Some(max_path_id) = self.max_path_id() {
+6021            let mut path_id = self.max_path_id_with_cids.next();
+6022            while path_id <= max_path_id {
+6023                self.endpoint_events
+6024                    .push_back(EndpointEventInner::NeedIdentifiers(
+6025                        path_id,
+6026                        now,
+6027                        self.peer_params.issue_cids_limit(),
+6028                    ));
+6029                path_id = path_id.next();
+6030            }
+6031            self.max_path_id_with_cids = max_path_id;
+6032        }
+6033    }
+6034
+6035    /// Populates a packet with frames
+6036    ///
+6037    /// This tries to fit as many frames as possible into the packet.
+6038    ///
+6039    /// *path_exclusive_only* means to only build frames which can only be sent on this
+6040    /// *path.  This is used in multipath for backup paths while there is still an active
+6041    /// *path.
+6042    fn populate_packet<'a, 'b>(
+6043        &mut self,
+6044        now: Instant,
+6045        space_id: SpaceId,
+6046        path_id: PathId,
+6047        scheduling_info: &PathSchedulingInfo,
+6048        builder: &mut PacketBuilder<'a, 'b>,
+6049    ) {
+6050        let is_multipath_negotiated = self.is_multipath_negotiated();
+6051        let space_has_keys = self.crypto_state.has_keys(space_id.encryption_level());
+6052        let is_0rtt = space_id == SpaceId::Data && !space_has_keys;
+6053        let stats = &mut self.path_stats.for_path(path_id).frame_tx;
+6054        let space = &mut self.spaces[space_id];
+6055        let path = &mut self.paths.get_mut(&path_id).expect("known path").data;
+6056        space
+6057            .for_path(path_id)
+6058            .pending_acks
+6059            .maybe_ack_non_eliciting();
+6060
+6061        // HANDSHAKE_DONE
+6062        if !is_0rtt
+6063            && !scheduling_info.is_abandoned
+6064            && scheduling_info.may_send_data
+6065            && mem::replace(&mut space.pending.handshake_done, false)
+6066        {
+6067            builder.write_frame(frame::HandshakeDone, stats);
+6068        }
+6069
+6070        // PING
+6071        if !scheduling_info.is_abandoned
+6072            && mem::replace(&mut space.for_path(path_id).ping_pending, false)
+6073        {
+6074            builder.write_frame(frame::Ping, stats);
+6075        }
+6076
+6077        // IMMEDIATE_ACK
+6078        if !scheduling_info.is_abandoned
+6079            && mem::replace(&mut space.for_path(path_id).immediate_ack_pending, false)
+6080        {
+6081            debug_assert_eq!(
+6082                space_id,
+6083                SpaceId::Data,
+6084                "immediate acks must be sent in the data space"
+6085            );
+6086            builder.write_frame(frame::ImmediateAck, stats);
+6087        }
+6088
+6089        // ACK
+6090        if !scheduling_info.is_abandoned && scheduling_info.may_send_data {
+6091            for path_id in space
+6092                .number_spaces
+6093                .iter_mut()
+6094                .filter(|(_, pns)| pns.pending_acks.can_send())
+6095                .map(|(&path_id, _)| path_id)
+6096                .collect::<Vec<_>>()
+6097            {
+6098                Self::populate_acks(
+6099                    now,
+6100                    self.receiving_ecn,
+6101                    path_id,
+6102                    space_id,
+6103                    space,
+6104                    is_multipath_negotiated,
+6105                    builder,
+6106                    stats,
+6107                    space_has_keys,
+6108                );
+6109            }
+6110        }
+6111
+6112        // ACK_FREQUENCY
+6113        if !scheduling_info.is_abandoned
+6114            && scheduling_info.may_send_data
+6115            && mem::replace(&mut space.pending.ack_frequency, false)
+6116        {
+6117            let sequence_number = self.ack_frequency.next_sequence_number();
+6118
+6119            // Safe to unwrap because this is always provided when ACK frequency is enabled
+6120            let config = self.config.ack_frequency_config.as_ref().unwrap();
+6121
+6122            // Ensure the delay is within bounds to avoid a PROTOCOL_VIOLATION error
+6123            let max_ack_delay = self.ack_frequency.candidate_max_ack_delay(
+6124                path.rtt.get(),
+6125                config,
+6126                &self.peer_params,
+6127            );
+6128
+6129            let frame = frame::AckFrequency {
+6130                sequence: sequence_number,
+6131                ack_eliciting_threshold: config.ack_eliciting_threshold,
+6132                request_max_ack_delay: max_ack_delay.as_micros().try_into().unwrap_or(VarInt::MAX),
+6133                reordering_threshold: config.reordering_threshold,
+6134            };
+6135            builder.write_frame(frame, stats);
+6136
+6137            self.ack_frequency
+6138                .ack_frequency_sent(path_id, builder.packet_number, max_ack_delay);
+6139        }
+6140
+6141        // PATH_CHALLENGE
+6142        if !scheduling_info.is_abandoned
+6143            && space_id == SpaceId::Data
+6144            && path.pending_on_path_challenge
+6145            && !self.state.is_closed()
+6146            && builder.frame_space_remaining() > frame::PathChallenge::SIZE_BOUND
+6147        // we don't want to send new challenges if we are already closing
+6148        {
+6149            path.pending_on_path_challenge = false;
+6150
+6151            let token = self.rng.random();
+6152            path.record_path_challenge_sent(now, token, path.network_path);
+6153            // Generate a new challenge every time we send a new PATH_CHALLENGE
+6154            let challenge = frame::PathChallenge(token);
+6155            builder.write_frame(challenge, stats);
+6156            builder.require_padding();
+6157            let pto = self.ack_frequency.max_ack_delay_for_pto() + path.rtt.pto_base();
+6158            match path.open_status {
+6159                paths::OpenStatus::Sent | paths::OpenStatus::Informed => {}
+6160                paths::OpenStatus::Pending => {
+6161                    path.open_status = paths::OpenStatus::Sent;
+6162                    self.timers.set(
+6163                        Timer::PerPath(path_id, PathTimer::AbandonFromValidation),
+6164                        now + 3 * pto,
+6165                        self.qlog.with_time(now),
+6166                    );
+6167                }
+6168            }
+6169
+6170            self.timers.set(
+6171                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
+6172                now + pto,
+6173                self.qlog.with_time(now),
+6174            );
+6175
+6176            if is_multipath_negotiated && !path.validated && path.pending_on_path_challenge {
+6177                // queue informing the path status along with the challenge
+6178                space.pending.path_status.insert(path_id);
+6179            }
+6180
+6181            // Always include an OBSERVED_ADDR frame with a PATH_CHALLENGE, regardless
+6182            // of whether one has already been sent on this path.
+6183            if space_id == SpaceId::Data
+6184                && self
+6185                    .config
+6186                    .address_discovery_role
+6187                    .should_report(&self.peer_params.address_discovery_role)
+6188            {
+6189                let frame = frame::ObservedAddr::new(
+6190                    path.network_path.remote,
+6191                    self.next_observed_addr_seq_no,
+6192                );
+6193                if builder.frame_space_remaining() > frame.size() {
+6194                    builder.write_frame(frame, stats);
+6195
+6196                    self.next_observed_addr_seq_no =
+6197                        self.next_observed_addr_seq_no.saturating_add(1u8);
+6198                    path.observed_addr_sent = true;
+6199
+6200                    space.pending.observed_addr = false;
+6201                }
+6202            }
+6203        }
+6204
+6205        // PATH_RESPONSE
+6206        if !scheduling_info.is_abandoned
+6207            && space_id == SpaceId::Data
+6208            && builder.frame_space_remaining() > frame::PathResponse::SIZE_BOUND
+6209            && let Some(token) = path.path_responses.pop_on_path(path.network_path)
+6210        {
+6211            let response = frame::PathResponse(token);
+6212            builder.write_frame(response, stats);
+6213            builder.require_padding();
+6214
+6215            // NOTE: this is technically not required but might be useful to ride the
+6216            // request/response nature of path challenges to refresh an observation
+6217            // Since PATH_RESPONSE is a probing frame, this is allowed by the spec.
+6218            if space_id == SpaceId::Data
+6219                && self
+6220                    .config
+6221                    .address_discovery_role
+6222                    .should_report(&self.peer_params.address_discovery_role)
+6223            {
+6224                let frame = frame::ObservedAddr::new(
+6225                    path.network_path.remote,
+6226                    self.next_observed_addr_seq_no,
+6227                );
+6228                if builder.frame_space_remaining() > frame.size() {
+6229                    builder.write_frame(frame, stats);
+6230
+6231                    self.next_observed_addr_seq_no =
+6232                        self.next_observed_addr_seq_no.saturating_add(1u8);
+6233                    path.observed_addr_sent = true;
+6234
+6235                    space.pending.observed_addr = false;
+6236                }
+6237            }
+6238        }
+6239
+6240        // REACH_OUT
+6241        while !scheduling_info.is_abandoned
+6242            && scheduling_info.may_send_data
+6243            && let Some(reach_out) = space
+6244                .pending
+6245                .reach_out
+6246                .pop_if(|frame| builder.frame_space_remaining() >= frame.size())
+6247        {
+6248            builder.write_frame(reach_out, stats);
+6249        }
+6250
+6251        // PATH_ABANDON
+6252        if space_id == SpaceId::Data
+6253            && scheduling_info.is_abandoned
+6254            && scheduling_info.may_self_abandon
+6255            && frame::PathAbandon::SIZE_BOUND <= builder.frame_space_remaining()
+6256            && let Some(error_code) = space.pending.path_abandon.remove(&path_id)
+6257        {
+6258            let frame = frame::PathAbandon {
+6259                path_id,
+6260                error_code,
+6261            };
+6262            builder.write_frame(frame, stats);
+6263
+6264            // Consider remotely issued CIDs as retired now that we have sent this frame at
+6265            // least once.
+6266            self.remote_cids.remove(&path_id);
+6267        }
+6268        while space_id == SpaceId::Data
+6269            && scheduling_info.may_send_data
+6270            && frame::PathAbandon::SIZE_BOUND <= builder.frame_space_remaining()
+6271            && let Some((abandoned_path_id, error_code)) = space.pending.path_abandon.pop_first()
+6272        {
+6273            let frame = frame::PathAbandon {
+6274                path_id: abandoned_path_id,
+6275                error_code,
+6276            };
+6277            builder.write_frame(frame, stats);
+6278
+6279            // Consider remotely issued CIDs as retired now that we have sent this frame at
+6280            // least once.
+6281            self.remote_cids.remove(&abandoned_path_id);
+6282        }
+6283
+6284        // OBSERVED_ADDR
+6285        if !scheduling_info.is_abandoned
+6286            && scheduling_info.may_send_data
+6287            && space_id == SpaceId::Data
+6288            && self
+6289                .config
+6290                .address_discovery_role
+6291                .should_report(&self.peer_params.address_discovery_role)
+6292            && (!path.observed_addr_sent || space.pending.observed_addr)
+6293        {
+6294            let frame =
+6295                frame::ObservedAddr::new(path.network_path.remote, self.next_observed_addr_seq_no);
+6296            if builder.frame_space_remaining() > frame.size() {
+6297                builder.write_frame(frame, stats);
+6298
+6299                self.next_observed_addr_seq_no = self.next_observed_addr_seq_no.saturating_add(1u8);
+6300                path.observed_addr_sent = true;
+6301
+6302                space.pending.observed_addr = false;
+6303            }
+6304        }
+6305
+6306        // CRYPTO
+6307        while !is_0rtt
+6308            && !scheduling_info.is_abandoned
+6309            && scheduling_info.may_send_data
+6310            && builder.frame_space_remaining() > frame::Crypto::SIZE_BOUND
+6311        {
+6312            let Some(mut frame) = space.pending.crypto.pop_front() else {
+6313                break;
+6314            };
+6315
+6316            // Calculate the maximum amount of crypto data we can store in the buffer.
+6317            // Since the offset is known, we can reserve the exact size required to encode it.
+6318            // For length we reserve 2bytes which allows to encode up to 2^14,
+6319            // which is more than what fits into normally sized QUIC frames.
+6320            let max_crypto_data_size = builder.frame_space_remaining()
+6321                - 1 // Frame Type
+6322                - VarInt::size(unsafe { VarInt::from_u64_unchecked(frame.offset) })
+6323                - 2; // Maximum encoded length for frame size, given we send less than 2^14 bytes
+6324
+6325            let len = frame
+6326                .data
+6327                .len()
+6328                .min(2usize.pow(14) - 1)
+6329                .min(max_crypto_data_size);
+6330
+6331            let data = frame.data.split_to(len);
+6332            let offset = frame.offset;
+6333            let truncated = frame::Crypto { offset, data };
+6334            builder.write_frame(truncated, stats);
+6335
+6336            if !frame.data.is_empty() {
+6337                frame.offset += len as u64;
+6338                space.pending.crypto.push_front(frame);
+6339            }
+6340        }
+6341
+6342        // PATH_STATUS_AVAILABLE & PATH_STATUS_BACKUP
+6343        while space_id == SpaceId::Data
+6344            && !scheduling_info.is_abandoned
+6345            && scheduling_info.may_send_data
+6346            && frame::PathStatusAvailable::SIZE_BOUND <= builder.frame_space_remaining()
+6347        {
+6348            let Some(path_id) = space.pending.path_status.pop_first() else {
+6349                break;
+6350            };
+6351            let Some(path) = self.paths.get(&path_id).map(|path_state| &path_state.data) else {
+6352                trace!(%path_id, "discarding queued path status for unknown path");
+6353                continue;
+6354            };
+6355
+6356            let seq = path.status.seq();
+6357            match path.local_status() {
+6358                PathStatus::Available => {
+6359                    let frame = frame::PathStatusAvailable {
+6360                        path_id,
+6361                        status_seq_no: seq,
+6362                    };
+6363                    builder.write_frame(frame, stats);
+6364                }
+6365                PathStatus::Backup => {
+6366                    let frame = frame::PathStatusBackup {
+6367                        path_id,
+6368                        status_seq_no: seq,
+6369                    };
+6370                    builder.write_frame(frame, stats);
+6371                }
+6372            }
+6373        }
+6374
+6375        // MAX_PATH_ID
+6376        if space_id == SpaceId::Data
+6377            && !scheduling_info.is_abandoned
+6378            && scheduling_info.may_send_data
+6379            && space.pending.max_path_id
+6380            && frame::MaxPathId::SIZE_BOUND <= builder.frame_space_remaining()
+6381        {
+6382            let frame = frame::MaxPathId(self.local_max_path_id);
+6383            builder.write_frame(frame, stats);
+6384            space.pending.max_path_id = false;
+6385        }
+6386
+6387        // PATHS_BLOCKED
+6388        if space_id == SpaceId::Data
+6389            && !scheduling_info.is_abandoned
+6390            && scheduling_info.may_send_data
+6391            && space.pending.paths_blocked
+6392            && frame::PathsBlocked::SIZE_BOUND <= builder.frame_space_remaining()
+6393        {
+6394            let frame = frame::PathsBlocked(self.remote_max_path_id);
+6395            builder.write_frame(frame, stats);
+6396            space.pending.paths_blocked = false;
+6397        }
+6398
+6399        // PATH_CIDS_BLOCKED
+6400        while space_id == SpaceId::Data
+6401            && !scheduling_info.is_abandoned
+6402            && scheduling_info.may_send_data
+6403            && frame::PathCidsBlocked::SIZE_BOUND <= builder.frame_space_remaining()
+6404        {
+6405            let Some(path_id) = space.pending.path_cids_blocked.pop_first() else {
+6406                break;
+6407            };
+6408            let next_seq = match self.remote_cids.get(&path_id) {
+6409                Some(cid_queue) => VarInt(cid_queue.active_seq() + 1),
+6410                None => VarInt(0),
+6411            };
+6412            let frame = frame::PathCidsBlocked { path_id, next_seq };
+6413            builder.write_frame(frame, stats);
+6414        }
+6415
+6416        // RESET_STREAM, STOP_SENDING, MAX_DATA, MAX_STREAM_DATA, MAX_STREAMS
+6417        if space_id == SpaceId::Data
+6418            && !scheduling_info.is_abandoned
+6419            && scheduling_info.may_send_data
+6420        {
+6421            self.streams
+6422                .write_control_frames(builder, &mut space.pending, stats);
+6423        }
+6424
+6425        // NEW_CONNECTION_ID
+6426        let cid_len = self
+6427            .local_cid_state
+6428            .values()
+6429            .map(|cid_state| cid_state.cid_len())
+6430            .max()
+6431            .expect("some local CID state must exist");
+6432        let new_cid_size_bound =
+6433            frame::NewConnectionId::size_bound(is_multipath_negotiated, cid_len);
+6434        while !scheduling_info.is_abandoned
+6435            && scheduling_info.may_send_data
+6436            && builder.frame_space_remaining() > new_cid_size_bound
+6437        {
+6438            let Some(issued) = space.pending.new_cids.pop() else {
+6439                break;
+6440            };
+6441            // Path was discarded after this CID was queued, drop.
+6442            let Some(cid_state) = self.local_cid_state.get(&issued.path_id) else {
+6443                debug!(
+6444                    path = %issued.path_id, seq = issued.sequence,
+6445                    "dropping queued NEW_CONNECTION_ID for discarded path",
+6446                );
+6447                continue;
+6448            };
+6449            let retire_prior_to = cid_state.retire_prior_to();
+6450
+6451            let cid_path_id = match is_multipath_negotiated {
+6452                true => Some(issued.path_id),
+6453                false => {
+6454                    debug_assert_eq!(issued.path_id, PathId::ZERO);
+6455                    None
+6456                }
+6457            };
+6458            let frame = frame::NewConnectionId {
+6459                path_id: cid_path_id,
+6460                sequence: issued.sequence,
+6461                retire_prior_to,
+6462                id: issued.id,
+6463                reset_token: issued.reset_token,
+6464            };
+6465            builder.write_frame(frame, stats);
+6466        }
+6467
+6468        // RETIRE_CONNECTION_ID
+6469        let retire_cid_bound = frame::RetireConnectionId::size_bound(is_multipath_negotiated);
+6470        while !scheduling_info.is_abandoned
+6471            && scheduling_info.may_send_data
+6472            && builder.frame_space_remaining() > retire_cid_bound
+6473        {
+6474            let (path_id, sequence) = match space.pending.retire_cids.pop() {
+6475                Some((PathId::ZERO, seq)) if !is_multipath_negotiated => (None, seq),
+6476                Some((path_id, seq)) => (Some(path_id), seq),
+6477                None => break,
+6478            };
+6479            let frame = frame::RetireConnectionId { path_id, sequence };
+6480            builder.write_frame(frame, stats);
+6481        }
+6482
+6483        // DATAGRAM
+6484        let mut sent_datagrams = false;
+6485        while !scheduling_info.is_abandoned
+6486            && scheduling_info.may_send_data
+6487            && builder.frame_space_remaining() > Datagram::SIZE_BOUND
+6488            && space_id == SpaceId::Data
+6489        {
+6490            match self.datagrams.write(builder, stats) {
+6491                true => {
+6492                    sent_datagrams = true;
+6493                }
+6494                false => break,
+6495            }
+6496        }
+6497        if self.datagrams.send_blocked && sent_datagrams {
+6498            self.events.push_back(Event::DatagramsUnblocked);
+6499            self.datagrams.send_blocked = false;
+6500        }
+6501
+6502        let path = &mut self.paths.get_mut(&path_id).expect("known path").data;
+6503
+6504        // NEW_TOKEN
+6505        if !scheduling_info.is_abandoned && scheduling_info.may_send_data {
+6506            while let Some(network_path) = space.pending.new_tokens.pop() {
+6507                debug_assert_eq!(space_id, SpaceId::Data);
+6508                let ConnectionSide::Server { server_config } = &self.side else {
+6509                    panic!("NEW_TOKEN frames should not be enqueued by clients");
+6510                };
+6511
+6512                if !network_path.is_probably_same_path(&path.network_path) {
+6513                    // NEW_TOKEN frames contain tokens bound to a client's IP address, and are only
+6514                    // useful if used from the same IP address.  Thus, we abandon enqueued NEW_TOKEN
+6515                    // frames upon an path change. Instead, when the new path becomes validated,
+6516                    // NEW_TOKEN frames may be enqueued for the new path instead.
+6517                    continue;
+6518                }
+6519
+6520                let token = Token::new(
+6521                    TokenPayload::Validation {
+6522                        ip: network_path.remote.ip(),
+6523                        issued: server_config.time_source.now(),
+6524                    },
+6525                    &mut self.rng,
+6526                );
+6527                let new_token = NewToken {
+6528                    token: token.encode(&*server_config.token_key).into(),
+6529                };
+6530
+6531                if builder.frame_space_remaining() < new_token.size() {
+6532                    space.pending.new_tokens.push(network_path);
+6533                    break;
+6534                }
+6535
+6536                builder.write_frame(new_token, stats);
+6537                builder.retransmits_mut().new_tokens.push(network_path);
+6538            }
+6539        }
+6540
+6541        // ADD_ADDRESS
+6542        while space_id == SpaceId::Data
+6543            && !scheduling_info.is_abandoned
+6544            && scheduling_info.may_send_data
+6545            && frame::AddAddress::SIZE_BOUND <= builder.frame_space_remaining()
+6546        {
+6547            if let Some(added_address) = space.pending.add_address.pop_last() {
+6548                builder.write_frame(added_address, stats);
+6549            } else {
+6550                break;
+6551            }
+6552        }
+6553
+6554        // REMOVE_ADDRESS
+6555        while space_id == SpaceId::Data
+6556            && !scheduling_info.is_abandoned
+6557            && scheduling_info.may_send_data
+6558            && frame::RemoveAddress::SIZE_BOUND <= builder.frame_space_remaining()
+6559        {
+6560            if let Some(removed_address) = space.pending.remove_address.pop_last() {
+6561                builder.write_frame(removed_address, stats);
+6562            } else {
+6563                break;
+6564            }
+6565        }
+6566
+6567        // STREAM
+6568        if !scheduling_info.is_abandoned
+6569            && scheduling_info.may_send_data
+6570            && space_id == SpaceId::Data
+6571        {
+6572            self.streams
+6573                .write_stream_frames(builder, self.config.send_fairness, stats);
+6574        }
+6575    }
+6576
+6577    /// Write pending ACKs into a buffer
+6578    fn populate_acks<'a, 'b>(
+6579        now: Instant,
+6580        receiving_ecn: bool,
+6581        path_id: PathId,
+6582        space_id: SpaceId,
+6583        space: &mut PacketSpace,
+6584        is_multipath_negotiated: bool,
+6585        builder: &mut PacketBuilder<'a, 'b>,
+6586        stats: &mut FrameStats,
+6587        space_has_keys: bool,
+6588    ) {
+6589        // 0-RTT packets must never carry acks (which would have to be of handshake packets)
+6590        debug_assert!(space_has_keys, "tried to send ACK in 0-RTT");
+6591
+6592        debug_assert!(
+6593            is_multipath_negotiated || path_id == PathId::ZERO,
+6594            "Only PathId::ZERO allowed without multipath (have {path_id:?})"
+6595        );
+6596        if is_multipath_negotiated {
+6597            debug_assert!(
+6598                space_id == SpaceId::Data || path_id == PathId::ZERO,
+6599                "path acks must be sent in 1RTT space (have {space_id:?})"
+6600            );
+6601        }
+6602
+6603        let pns = space.for_path(path_id);
+6604        let ranges = pns.pending_acks.ranges();
+6605        debug_assert!(!ranges.is_empty(), "can not send empty ACK range");
+6606        let ecn = if receiving_ecn {
+6607            Some(&pns.ecn_counters)
+6608        } else {
+6609            None
+6610        };
+6611
+6612        let delay_micros = pns.pending_acks.ack_delay(now).as_micros() as u64;
+6613        // TODO: This should come from `TransportConfig` if that gets configurable.
+6614        let ack_delay_exp = TransportParameters::default().ack_delay_exponent;
+6615        let delay = delay_micros >> ack_delay_exp.into_inner();
+6616
+6617        if is_multipath_negotiated && space_id == SpaceId::Data {
+6618            if !ranges.is_empty() {
+6619                let frame = frame::PathAck::encoder(path_id, delay, ranges, ecn);
+6620                builder.write_frame(frame, stats);
+6621            }
+6622        } else {
+6623            builder.write_frame(frame::Ack::encoder(delay, ranges, ecn), stats);
+6624        }
+6625    }
+6626
+6627    fn close_common(&mut self) {
+6628        trace!("connection closed");
+6629        self.timers.reset();
+6630    }
+6631
+6632    fn set_close_timer(&mut self, now: Instant) {
+6633        // QUIC-MULTIPATH § 2.6 Connection Closure: draining for 3*PTO using the max PTO of
+6634        // all paths.
+6635        let pto_max = self.max_pto_for_space(self.highest_space);
+6636        self.timers.set(
+6637            Timer::Conn(ConnTimer::Close),
+6638            now + 3 * pto_max,
+6639            self.qlog.with_time(now),
+6640        );
+6641    }
+6642
+6643    /// Handle transport parameters received from the peer
+6644    ///
+6645    /// *remote_cid* and *local_cid* are the source and destination CIDs respectively of the
+6646    /// *packet into which the transport parameters arrived.
+6647    fn handle_peer_params(
+6648        &mut self,
+6649        params: TransportParameters,
+6650        local_cid: ConnectionId,
+6651        remote_cid: ConnectionId,
+6652        now: Instant,
+6653    ) -> Result<(), TransportError> {
+6654        if Some(self.original_remote_cid) != params.initial_src_cid
+6655            || (self.side.is_client()
+6656                && (Some(self.initial_dst_cid) != params.original_dst_cid
+6657                    || self.retry_src_cid != params.retry_src_cid))
+6658        {
+6659            return Err(TransportError::TRANSPORT_PARAMETER_ERROR(
+6660                "CID authentication failure",
+6661            ));
+6662        }
+6663        if params.initial_max_path_id.is_some() && (local_cid.is_empty() || remote_cid.is_empty()) {
+6664            return Err(TransportError::PROTOCOL_VIOLATION(
+6665                "multipath must not use zero-length CIDs",
+6666            ));
+6667        }
+6668
+6669        self.set_peer_params(params);
+6670        self.qlog.emit_peer_transport_params_received(self, now);
+6671
+6672        Ok(())
+6673    }
+6674
+6675    fn set_peer_params(&mut self, params: TransportParameters) {
+6676        self.streams.set_params(&params);
+6677        self.idle_timeout =
+6678            negotiate_max_idle_timeout(self.config.max_idle_timeout, Some(params.max_idle_timeout));
+6679        trace!("negotiated max idle timeout {:?}", self.idle_timeout);
+6680
+6681        if let Some(ref info) = params.preferred_address {
+6682            // During the handshake PathId::ZERO exists.
+6683            self.remote_cids.get_mut(&PathId::ZERO).expect("not yet abandoned").insert(frame::NewConnectionId {
+6684                path_id: None,
+6685                sequence: 1,
+6686                id: info.connection_id,
+6687                reset_token: info.stateless_reset_token,
+6688                retire_prior_to: 0,
+6689            })
+6690            .expect(
+6691                "preferred address CID is the first received, and hence is guaranteed to be legal",
+6692            );
+6693            let remote = self.path_data(PathId::ZERO).network_path.remote;
+6694            self.set_reset_token(PathId::ZERO, remote, info.stateless_reset_token);
+6695        }
+6696        self.ack_frequency.peer_max_ack_delay = get_max_ack_delay(&params);
+6697
+6698        let mut multipath_enabled = false;
+6699        if let (Some(local_max_path_id), Some(remote_max_path_id)) = (
+6700            self.config.get_initial_max_path_id(),
+6701            params.initial_max_path_id,
+6702        ) {
+6703            // multipath is enabled, register the local and remote maximums
+6704            self.local_max_path_id = local_max_path_id;
+6705            self.remote_max_path_id = remote_max_path_id;
+6706            let initial_max_path_id = local_max_path_id.min(remote_max_path_id);
+6707            debug!(%initial_max_path_id, "multipath negotiated");
+6708            multipath_enabled = true;
+6709        }
+6710
+6711        if let Some((max_locally_allowed_remote_addresses, max_remotely_allowed_remote_addresses)) =
+6712            self.config
+6713                .max_remote_nat_traversal_addresses
+6714                .zip(params.max_remote_nat_traversal_addresses)
+6715        {
+6716            if multipath_enabled {
+6717                let max_local_addresses = max_remotely_allowed_remote_addresses.get();
+6718                let max_remote_addresses = max_locally_allowed_remote_addresses.get();
+6719                self.n0_nat_traversal = n0_nat_traversal::State::new(
+6720                    max_remote_addresses,
+6721                    max_local_addresses,
+6722                    self.side(),
+6723                );
+6724                debug!(
+6725                    %max_remote_addresses, %max_local_addresses,
+6726                    "n0's nat traversal negotiated"
+6727                );
+6728            } else {
+6729                debug!("n0 nat traversal enabled for both endpoints, but multipath is missing")
+6730            }
+6731        }
+6732
+6733        self.peer_params = params;
+6734        let peer_max_udp_payload_size =
+6735            u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX);
+6736        self.path_data_mut(PathId::ZERO)
+6737            .mtud
+6738            .on_peer_max_udp_payload_size_received(peer_max_udp_payload_size);
+6739    }
+6740
+6741    /// Decrypts a packet, returning the packet number on success
+6742    fn decrypt_packet(
+6743        &mut self,
+6744        now: Instant,
+6745        path_id: PathId,
+6746        packet: &mut Packet,
+6747    ) -> Result<Option<u64>, Option<TransportError>> {
+6748        let result = self
+6749            .crypto_state
+6750            .decrypt_packet_body(packet, path_id, &self.spaces)?;
+6751
+6752        let Some(result) = result else {
+6753            return Ok(None);
+6754        };
+6755
+6756        if result.outgoing_key_update_acked
+6757            && let Some(prev) = self.crypto_state.prev_crypto.as_mut()
+6758        {
+6759            prev.end_packet = Some((result.packet_number, now));
+6760            self.set_key_discard_timer(now, packet.header.space());
+6761        }
+6762
+6763        if result.incoming_key_update {
+6764            trace!("key update authenticated");
+6765            self.crypto_state
+6766                .update_keys(Some((result.packet_number, now)), true);
+6767            self.set_key_discard_timer(now, packet.header.space());
+6768        }
+6769
+6770        Ok(Some(result.packet_number))
+6771    }
+6772
+6773    fn peer_supports_ack_frequency(&self) -> bool {
+6774        self.peer_params.min_ack_delay.is_some()
+6775    }
+6776
+6777    /// Send an IMMEDIATE_ACK frame to the remote endpoint
+6778    ///
+6779    /// According to the spec, this will result in an error if the remote endpoint does not support
+6780    /// the Acknowledgement Frequency extension
+6781    pub(crate) fn immediate_ack(&mut self, path_id: PathId) {
+6782        debug_assert_eq!(
+6783            self.highest_space,
+6784            SpaceKind::Data,
+6785            "immediate ack must be written in the data space"
+6786        );
+6787        self.spaces[SpaceId::Data]
+6788            .for_path(path_id)
+6789            .immediate_ack_pending = true;
+6790    }
+6791
+6792    /// Decodes a packet, returning its decrypted payload, so it can be inspected in tests
+6793    #[cfg(test)]
+6794    pub(crate) fn decode_packet(&self, event: &ConnectionEvent) -> Option<Vec<u8>> {
+6795        let ConnectionEventInner::Datagram(DatagramConnectionEvent {
+6796            path_id,
+6797            first_decode,
+6798            remaining,
+6799            ..
+6800        }) = &event.0
+6801        else {
+6802            return None;
+6803        };
+6804
+6805        if remaining.is_some() {
+6806            panic!("Packets should never be coalesced in tests");
+6807        }
+6808
+6809        let decrypted_header = self
+6810            .crypto_state
+6811            .unprotect_header(first_decode.clone(), self.peer_params.stateless_reset_token)?;
+6812
+6813        let mut packet = decrypted_header.packet?;
+6814        self.crypto_state
+6815            .decrypt_packet_body(&mut packet, *path_id, &self.spaces)
+6816            .ok()?;
+6817
+6818        Some(packet.payload.to_vec())
+6819    }
+6820
+6821    /// The number of bytes of packets containing retransmittable frames that have not been
+6822    /// acknowledged or declared lost.
+6823    #[cfg(test)]
+6824    pub(crate) fn bytes_in_flight(&self) -> u64 {
+6825        // TODO(@divma): consider including for multipath?
+6826        self.path_data(PathId::ZERO).in_flight.bytes
+6827    }
+6828
+6829    /// Number of bytes worth of non-ack-only packets that may be sent
+6830    #[cfg(test)]
+6831    pub(crate) fn congestion_window(&self) -> u64 {
+6832        let path = self.path_data(PathId::ZERO);
+6833        path.congestion
+6834            .window()
+6835            .saturating_sub(path.in_flight.bytes)
+6836    }
+6837
+6838    /// Whether no timers but keepalive, idle, rtt, pushnewcid, and key discard are running
+6839    #[cfg(test)]
+6840    pub(crate) fn is_idle(&self) -> bool {
+6841        let current_timers = self.timers.values();
+6842        current_timers
+6843            .into_iter()
+6844            .filter(|(timer, _)| {
+6845                !matches!(
+6846                    timer,
+6847                    Timer::Conn(ConnTimer::KeepAlive)
+6848                        | Timer::PerPath(_, PathTimer::PathKeepAlive)
+6849                        | Timer::Conn(ConnTimer::PushNewCid)
+6850                        | Timer::Conn(ConnTimer::KeyDiscard)
+6851                )
+6852            })
+6853            .min_by_key(|(_, time)| *time)
+6854            .is_none_or(|(timer, _)| {
+6855                matches!(
+6856                    timer,
+6857                    Timer::Conn(ConnTimer::Idle) | Timer::PerPath(_, PathTimer::PathIdle)
+6858                )
+6859            })
+6860    }
+6861
+6862    /// Whether explicit congestion notification is in use on outgoing packets.
+6863    #[cfg(test)]
+6864    pub(crate) fn using_ecn(&self) -> bool {
+6865        self.path_data(PathId::ZERO).sending_ecn
+6866    }
+6867
+6868    /// The number of received bytes in the current path
+6869    #[cfg(test)]
+6870    pub(crate) fn total_recvd(&self) -> u64 {
+6871        self.path_data(PathId::ZERO).total_recvd
+6872    }
+6873
+6874    #[cfg(test)]
+6875    pub(crate) fn active_local_cid_seq(&self) -> (u64, u64) {
+6876        self.local_cid_state
+6877            .get(&PathId::ZERO)
+6878            .unwrap()
+6879            .active_seq()
+6880    }
+6881
+6882    #[cfg(test)]
+6883    #[track_caller]
+6884    pub(crate) fn active_local_path_cid_seq(&self, path_id: u32) -> (u64, u64) {
+6885        self.local_cid_state
+6886            .get(&PathId(path_id))
+6887            .unwrap()
+6888            .active_seq()
+6889    }
+6890
+6891    /// Instruct the peer to replace previously issued CIDs by sending a NEW_CONNECTION_ID frame
+6892    /// with updated `retire_prior_to` field set to `v`
+6893    #[cfg(test)]
+6894    pub(crate) fn rotate_local_cid(&mut self, v: u64, now: Instant) {
+6895        let n = self
+6896            .local_cid_state
+6897            .get_mut(&PathId::ZERO)
+6898            .unwrap()
+6899            .assign_retire_seq(v);
+6900        debug_assert!(!self.state.is_drained()); // requirement for endpoint_events
+6901        self.endpoint_events
+6902            .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n));
+6903    }
+6904
+6905    /// Check the current active remote CID sequence for `PathId::ZERO`
+6906    #[cfg(test)]
+6907    pub(crate) fn active_remote_cid_seq(&self) -> u64 {
+6908        self.remote_cids.get(&PathId::ZERO).unwrap().active_seq()
+6909    }
+6910
+6911    /// Returns the detected maximum udp payload size for the current path
+6912    #[cfg(test)]
+6913    pub(crate) fn path_mtu(&self, path_id: PathId) -> u16 {
+6914        self.path_data(path_id).current_mtu()
+6915    }
+6916
+6917    /// Triggers path validation on all paths
+6918    #[cfg(test)]
+6919    pub(crate) fn trigger_path_validation(&mut self) {
+6920        for path in self.paths.values_mut() {
+6921            path.data.pending_on_path_challenge = true;
+6922        }
+6923    }
+6924
+6925    /// Simulates a protocol violation error for test purposes.
+6926    #[cfg(test)]
+6927    pub fn simulate_protocol_violation(&mut self, now: Instant) {
+6928        if !self.state.is_closed() {
+6929            self.state
+6930                .move_to_closed(TransportError::PROTOCOL_VIOLATION("simulated violation"));
+6931            self.close_common();
+6932            if !self.state.is_drained() {
+6933                self.set_close_timer(now);
+6934            }
+6935            self.connection_close_pending = true;
+6936        }
+6937    }
+6938
+6939    /// Whether we have on-path 1-RTT data to send.
+6940    ///
+6941    /// This checks for frames that can only be sent in the data space (1-RTT):
+6942    /// - Pending PATH_CHALLENGE frames on the active and previous path if just migrated.
+6943    /// - Pending PATH_RESPONSE frames.
+6944    /// - Pending data to send in STREAM frames.
+6945    /// - Pending DATAGRAM frames to send.
+6946    ///
+6947    /// See also [`PacketSpace::can_send`] which keeps track of all other frame types that
+6948    /// may need to be sent.
+6949    fn can_send_1rtt(&self, path_id: PathId, max_size: usize) -> SendableFrames {
+6950        let space_specific = self.paths.get(&path_id).is_some_and(|path| {
+6951            path.data.pending_on_path_challenge || !path.data.path_responses.is_empty()
+6952        });
+6953
+6954        // Stream control frames are checked in PacketSpace::can_send, only check data here.
+6955        let other = self.streams.can_send_stream_data()
+6956            || self
+6957                .datagrams
+6958                .outgoing
+6959                .front()
+6960                .is_some_and(|x| x.size(true) <= max_size);
+6961
+6962        // All `false` fields are set in PacketSpace::can_send.
+6963        SendableFrames {
+6964            acks: false,
+6965            close: false,
+6966            space_specific,
+6967            other,
+6968        }
+6969    }
+6970
+6971    /// Terminate the connection instantly, without sending a close packet
+6972    fn kill(&mut self, reason: ConnectionError) {
+6973        self.close_common();
+6974        let was_draining = self.state.move_to_drained(Some(reason));
+6975        if !was_draining {
+6976            self.endpoint_events.push_back(EndpointEventInner::Draining);
+6977        }
+6978        // move_to_drained checks that we were never in drained before, so we
+6979        // never sent a `Drained` event before (it's illegal to send more events after drained).
+6980        self.endpoint_events.push_back(EndpointEventInner::Drained);
+6981    }
+6982
+6983    /// Storage size required for the largest packet that can be transmitted on all currently
+6984    /// available paths
+6985    ///
+6986    /// Buffers passed to [`Connection::poll_transmit`] should be at least this large.
+6987    ///
+6988    /// When multipath is enabled, this value is the minimum MTU across all available paths.
+6989    pub fn current_mtu(&self) -> u16 {
+6990        self.paths
+6991            .iter()
+6992            .filter(|&(path_id, _path_state)| !self.abandoned_paths.contains(path_id))
+6993            .map(|(_path_id, path_state)| path_state.data.current_mtu())
+6994            .min()
+6995            .unwrap_or(INITIAL_MTU)
+6996    }
+6997
+6998    /// Size of non-frame data for a 1-RTT packet
+6999    ///
+7000    /// Quantifies space consumed by the QUIC header and AEAD tag. All other bytes in a packet are
+7001    /// frames. Changes if the length of the remote connection ID changes, which is expected to be
+7002    /// rare. If `pn` is specified, may additionally change unpredictably due to variations in
+7003    /// latency and packet loss.
+7004    fn predict_1rtt_overhead(&mut self, pn: u64, path: PathId) -> usize {
+7005        let pn_len = PacketNumber::new(
+7006            pn,
+7007            self.spaces[SpaceId::Data]
+7008                .for_path(path)
+7009                .largest_acked_packet_pn
+7010                .unwrap_or(0),
+7011        )
+7012        .len();
+7013
+7014        // 1 byte for flags
+7015        1 + self
+7016            .remote_cids
+7017            .get(&path)
+7018            .map(|cids| cids.active().len())
+7019            .unwrap_or(20)      // Max CID len in QUIC v1
+7020            + pn_len
+7021            + self.tag_len_1rtt()
+7022    }
+7023
+7024    fn predict_1rtt_overhead_no_pn(&self) -> usize {
+7025        let pn_len = 4;
+7026
+7027        let cid_len = self
+7028            .remote_cids
+7029            .values()
+7030            .map(|cids| cids.active().len())
+7031            .max()
+7032            .unwrap_or(20); // Max CID len in QUIC v1
+7033
+7034        // 1 byte for flags
+7035        1 + cid_len + pn_len + self.tag_len_1rtt()
+7036    }
+7037
+7038    fn tag_len_1rtt(&self) -> usize {
+7039        // encryption_keys for Data space returns 1-RTT keys if available, otherwise 0-RTT keys
+7040        let packet_crypto = self
+7041            .crypto_state
+7042            .encryption_keys(SpaceKind::Data, self.side.side())
+7043            .map(|(_header, packet, _level)| packet);
+7044        // If neither Data nor 0-RTT keys are available, make a reasonable tag length guess. As of
+7045        // this writing, all QUIC cipher suites use 16-byte tags. We could return `None` instead,
+7046        // but that would needlessly prevent sending datagrams during 0-RTT.
+7047        packet_crypto.map_or(16, |x| x.tag_len())
+7048    }
+7049
+7050    /// Mark the path as validated, and enqueue NEW_TOKEN frames to be sent as appropriate
+7051    fn on_path_validated(&mut self, path_id: PathId) {
+7052        self.path_data_mut(path_id).validated = true;
+7053        let ConnectionSide::Server { server_config } = &self.side else {
+7054            return;
+7055        };
+7056        let network_path = self.path_data(path_id).network_path;
+7057        let new_tokens = &mut self.spaces[SpaceId::Data as usize].pending.new_tokens;
+7058        new_tokens.clear();
+7059        for _ in 0..server_config.validation_token.sent {
+7060            new_tokens.push(network_path);
+7061        }
+7062    }
+7063
+7064    /// Handle new path status information: PATH_STATUS_AVAILABLE, PATH_STATUS_BACKUP
+7065    fn on_path_status(&mut self, path_id: PathId, status: PathStatus, status_seq_no: VarInt) {
+7066        if let Some(path) = self.paths.get_mut(&path_id) {
+7067            path.data.status.remote_update(status, status_seq_no);
+7068        } else {
+7069            debug!("PATH_STATUS_AVAILABLE received unknown path {:?}", path_id);
+7070        }
+7071        self.events.push_back(
+7072            PathEvent::RemoteStatus {
+7073                id: path_id,
+7074                status,
+7075            }
+7076            .into(),
+7077        );
+7078    }
+7079
+7080    /// Returns the maximum [`PathId`] to be used for sending in this connection.
+7081    ///
+7082    /// This is calculated as minimum between the local and remote's maximums when multipath is
+7083    /// enabled, or `None` when disabled.
+7084    ///
+7085    /// For data that's received, we should use [`Self::local_max_path_id`] instead.
+7086    /// The reasoning is that the remote might already have updated to its own newer
+7087    /// [`Self::max_path_id`] after sending out a `MAX_PATH_ID` frame, but it got re-ordered.
+7088    fn max_path_id(&self) -> Option<PathId> {
+7089        if self.is_multipath_negotiated() {
+7090            Some(self.remote_max_path_id.min(self.local_max_path_id))
+7091        } else {
+7092            None
+7093        }
+7094    }
+7095
+7096    /// Returns whether this connection has a socket that supports IPv6.
+7097    ///
+7098    /// TODO(matheus23): This is related to noq endpoint state's `ipv6` bool. We should move that info
+7099    /// here instead of trying to hack around not knowing it exactly.
+7100    pub(crate) fn is_ipv6(&self) -> bool {
+7101        self.paths
+7102            .values()
+7103            .any(|p| p.data.network_path.remote.is_ipv6())
+7104    }
+7105
+7106    /// Add addresses the local endpoint considers are reachable for nat traversal.
+7107    pub fn add_nat_traversal_address(
+7108        &mut self,
+7109        address: SocketAddr,
+7110    ) -> Result<(), n0_nat_traversal::Error> {
+7111        if let Some(added) = self.n0_nat_traversal.add_local_address(address)? {
+7112            self.spaces[SpaceId::Data].pending.add_address.insert(added);
+7113        };
+7114        Ok(())
+7115    }
+7116
+7117    /// Removes an address the endpoing no longer considers reachable for nat traversal
+7118    ///
+7119    /// Addresses not present in the set will be silently ignored.
+7120    pub fn remove_nat_traversal_address(
+7121        &mut self,
+7122        address: SocketAddr,
+7123    ) -> Result<(), n0_nat_traversal::Error> {
+7124        if let Some(removed) = self.n0_nat_traversal.remove_local_address(address)? {
+7125            self.spaces[SpaceId::Data]
+7126                .pending
+7127                .remove_address
+7128                .insert(removed);
+7129        }
+7130        Ok(())
+7131    }
+7132
+7133    /// Get the current local nat traversal addresses
+7134    pub fn get_local_nat_traversal_addresses(
+7135        &self,
+7136    ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> {
+7137        self.n0_nat_traversal.get_local_nat_traversal_addresses()
+7138    }
+7139
+7140    /// Get the currently advertised nat traversal addresses by the server
+7141    pub fn get_remote_nat_traversal_addresses(
+7142        &self,
+7143    ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> {
+7144        Ok(self
+7145            .n0_nat_traversal
+7146            .client_side()?
+7147            .get_remote_nat_traversal_addresses())
+7148    }
+7149
+7150    /// Initiates a new nat traversal round
+7151    ///
+7152    /// A nat traversal round involves advertising the client's local addresses in
+7153    /// `REACH_OUT` frames, and initiating probing of the known remote addresses. When a new
+7154    /// round is initiated, the previous one is cancelled.
+7155    ///
+7156    /// For all probes that succeed, if any, a new path will be opened on the successful
+7157    /// 4-tuple.
+7158    ///
+7159    /// Returns the server addresses that are now being probed. If addresses fail due to
+7160    /// spurious errors, these might succeed later and not be returned in this set.
+7161    pub fn initiate_nat_traversal_round(
+7162        &mut self,
+7163        now: Instant,
+7164    ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> {
+7165        if self.state.is_closed() {
+7166            return Err(n0_nat_traversal::Error::Closed);
+7167        }
+7168
+7169        let ipv6 = self.is_ipv6();
+7170        let client_state = self.n0_nat_traversal.client_side_mut()?;
+7171        let (mut reach_out_frames, probed_addrs) =
+7172            client_state.initiate_nat_traversal_round(ipv6)?;
+7173        if let Some(delay) = self.n0_nat_traversal.retry_delay(self.config.initial_rtt) {
+7174            self.timers.set(
+7175                Timer::Conn(ConnTimer::NatTraversalProbeRetry),
+7176                now + delay,
+7177                self.qlog.with_time(now),
+7178            );
+7179        }
+7180
+7181        self.spaces[SpaceId::Data]
+7182            .pending
+7183            .reach_out
+7184            .append(&mut reach_out_frames);
+7185
+7186        Ok(probed_addrs)
+7187    }
+7188
+7189    /// Whether the handshake is considered **confirmed**.
+7190    ///
+7191    /// <https://www.rfc-editor.org/rfc/rfc9001#section-4.1.2> defines a handshake to be
+7192    /// confirmed when you know the peer successfully received and successfully processed
+7193    /// your TLS Finished message.
+7194    ///
+7195    /// Implementation-wise this is the point at which the handshake crypto keys are
+7196    /// discarded. So we can use this to know if the handshake is confirmed.
+7197    fn is_handshake_confirmed(&self) -> bool {
+7198        !self.is_handshaking() && !self.crypto_state.has_keys(EncryptionLevel::Handshake)
+7199    }
+7200}
+7201
+7202impl fmt::Debug for Connection {
+7203    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+7204        f.debug_struct("Connection")
+7205            .field("handshake_cid", &self.handshake_cid)
+7206            .finish()
+7207    }
+7208}
+7209
+7210/// Hints when the caller identifies a network change.
+7211pub trait NetworkChangeHint: std::fmt::Debug + 'static {
+7212    /// Inform the connection if a path may recover after a network change.
+7213    ///
+7214    /// After network changes, paths may not be recoverable. In this case, waiting for the path to
+7215    /// become idle may take longer than what is desirable. If [`Self::is_path_recoverable`]
+7216    /// returns `false`, a multipath-enabled, client-side connection will establish a new path to
+7217    /// the same remote, closing the current one, instead of migrating the path.
+7218    ///
+7219    /// Paths that are deemed recoverable will simply be sent a PING for a liveness check.
+7220    fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool;
+7221}
+7222
+7223/// Return value for [`Connection::poll_transmit_path_space`].
+7224#[derive(Debug)]
+7225enum PollPathSpaceStatus {
+7226    /// Nothing to send in the space, nothing was written into the [`TransmitBuf`].
+7227    NothingToSend {
+7228        /// If true there was data to send but congestion control did not allow so.
+7229        congestion_blocked: bool,
+7230    },
+7231    /// One or more packets have been written into the [`TransmitBuf`].
+7232    WrotePacket {
+7233        /// The highest packet number.
+7234        last_packet_number: u64,
+7235        /// Whether to pad an already started datagram in the next packet.
+7236        ///
+7237        /// When packets in Initial, 0-RTT or Handshake packet do not fill the entire
+7238        /// datagram they may decide to coalesce with the next packet from a higher
+7239        /// encryption level on the same path. But the earlier packet may require specific
+7240        /// size requirements for the datagram they are sent in.
+7241        ///
+7242        /// If a space did not complete the datagram, they use this to request the correct
+7243        /// padding in the final packet of the datagram so that the final datagram will have
+7244        /// the correct size.
+7245        ///
+7246        /// If a space did fill an entire datagram, it leaves this to the default of
+7247        /// [`PadDatagram::No`].
+7248        pad_datagram: PadDatagram,
+7249    },
+7250    /// Send the contents of the transmit immediately.
+7251    ///
+7252    /// Packets were written and the GSO batch must end now, regardless from whether higher
+7253    /// spaces still have frames to write. This is used when the last datagram written would
+7254    /// require too much padding to continue a GSO batch, which would waste space on the
+7255    /// wire.
+7256    Send {
+7257        /// The highest packet number written into the transmit.
+7258        last_packet_number: u64,
+7259    },
+7260}
+7261
+7262/// Information used to decide what frames to schedule into which packets.
+7263///
+7264/// Primarily used by [`Connection::poll_transmit_on_path`] and the functions that help
+7265/// building packets for it: [`Connection::poll_transmit_path_space`] and
+7266/// [`Connection::populate_packet`].
+7267#[derive(Debug, Copy, Clone)]
+7268struct PathSchedulingInfo {
+7269    /// Whether the path is abandoned.
+7270    ///
+7271    /// Note that a path that is abandoned but still has CIDs can still send a packet. After
+7272    /// sending that packet the CIDs issued by the remote have to be considered retired as
+7273    /// well.
+7274    is_abandoned: bool,
+7275    /// Whether the path may send [`SpaceKind::Data`] frames.
+7276    ///
+7277    /// Some paths should only send frames from [`SendableFrames::space_specific`]. All other
+7278    /// frames are essentially frames that can be sent on any [`SpaceKind::Data`] space. For
+7279    /// those we want to respect packet scheduling rules however.
+7280    ///
+7281    /// Roughly speaking data frames are only sent on spaces that have CIDs, are not
+7282    /// abandoned and have no *better* spaces. However see to comments where this is
+7283    /// populated for the exact packet scheduling implementation.
+7284    ///
+7285    /// This essentially marks this paths as the best validated space ID. Except during
+7286    /// the handshake in which case it does not need to be validated. Several paths could be
+7287    /// equally good and all have this set to `true`, in that case packet scheduling can
+7288    /// choose which path to use. Currently it chooses the lowest path that is not
+7289    /// congestion blocked.
+7290    ///
+7291    /// Note that once in the closed or draining states this will never be true.
+7292    may_send_data: bool,
+7293    /// Whether the path may send a CONNECTION_CLOSE frame.
+7294    ///
+7295    /// This essentially marks this path as the best validated space ID with a fallback
+7296    /// to unvalidated spaces if there are no validated spaces. Like for
+7297    /// [`Self::may_send_data`] other paths could be equally good.
+7298    may_send_close: bool,
+7299    may_self_abandon: bool,
+7300}
+7301
+7302#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+7303enum PathBlocked {
+7304    No,
+7305    AntiAmplification,
+7306    Congestion,
+7307    Pacing,
+7308}
+7309
+7310/// Fields of `Connection` specific to it being client-side or server-side
+7311enum ConnectionSide {
+7312    Client {
+7313        /// Sent in every outgoing Initial packet. Always empty after Initial keys are discarded
+7314        token: Bytes,
+7315        token_store: Arc<dyn TokenStore>,
+7316        server_name: String,
+7317    },
+7318    Server {
+7319        server_config: Arc<ServerConfig>,
+7320    },
+7321}
+7322
+7323impl ConnectionSide {
+7324    fn is_client(&self) -> bool {
+7325        self.side().is_client()
+7326    }
+7327
+7328    fn is_server(&self) -> bool {
+7329        self.side().is_server()
+7330    }
+7331
+7332    fn side(&self) -> Side {
+7333        match *self {
+7334            Self::Client { .. } => Side::Client,
+7335            Self::Server { .. } => Side::Server,
+7336        }
+7337    }
+7338}
+7339
+7340impl From<SideArgs> for ConnectionSide {
+7341    fn from(side: SideArgs) -> Self {
+7342        match side {
+7343            SideArgs::Client {
+7344                token_store,
+7345                server_name,
+7346            } => Self::Client {
+7347                token: token_store.take(&server_name).unwrap_or_default(),
+7348                token_store,
+7349                server_name,
+7350            },
+7351            SideArgs::Server {
+7352                server_config,
+7353                pref_addr_cid: _,
+7354                path_validated: _,
+7355            } => Self::Server { server_config },
+7356        }
+7357    }
+7358}
+7359
+7360/// Parameters to `Connection::new` specific to it being client-side or server-side
+7361pub(crate) enum SideArgs {
+7362    Client {
+7363        token_store: Arc<dyn TokenStore>,
+7364        server_name: String,
+7365    },
+7366    Server {
+7367        server_config: Arc<ServerConfig>,
+7368        pref_addr_cid: Option<ConnectionId>,
+7369        path_validated: bool,
+7370    },
+7371}
+7372
+7373impl SideArgs {
+7374    pub(crate) fn pref_addr_cid(&self) -> Option<ConnectionId> {
+7375        match *self {
+7376            Self::Client { .. } => None,
+7377            Self::Server { pref_addr_cid, .. } => pref_addr_cid,
+7378        }
+7379    }
+7380
+7381    pub(crate) fn path_validated(&self) -> bool {
+7382        match *self {
+7383            Self::Client { .. } => true,
+7384            Self::Server { path_validated, .. } => path_validated,
+7385        }
+7386    }
+7387
+7388    pub(crate) fn side(&self) -> Side {
+7389        match *self {
+7390            Self::Client { .. } => Side::Client,
+7391            Self::Server { .. } => Side::Server,
+7392        }
+7393    }
+7394}
+7395
+7396/// Reasons why a connection might be lost
+7397#[derive(Debug, Error, Clone, PartialEq, Eq)]
+7398pub enum ConnectionError {
+7399    /// The peer doesn't implement any supported version
+7400    #[error("peer doesn't implement any supported version")]
+7401    VersionMismatch,
+7402    /// The peer violated the QUIC specification as understood by this implementation
+7403    #[error(transparent)]
+7404    TransportError(#[from] TransportError),
+7405    /// The peer's QUIC stack aborted the connection automatically
+7406    #[error("aborted by peer: {0}")]
+7407    ConnectionClosed(frame::ConnectionClose),
+7408    /// The peer closed the connection
+7409    #[error("closed by peer: {0}")]
+7410    ApplicationClosed(frame::ApplicationClose),
+7411    /// The peer is unable to continue processing this connection, usually due to having restarted
+7412    #[error("reset by peer")]
+7413    Reset,
+7414    /// Communication with the peer has lapsed for longer than the negotiated idle timeout
+7415    ///
+7416    /// If neither side is sending keep-alives, a connection will time out after a long enough idle
+7417    /// period even if the peer is still reachable. See also [`TransportConfig::max_idle_timeout()`]
+7418    /// and [`TransportConfig::keep_alive_interval()`].
+7419    #[error("timed out")]
+7420    TimedOut,
+7421    /// The local application closed the connection
+7422    #[error("closed")]
+7423    LocallyClosed,
+7424    /// The connection could not be created because not enough of the CID space is available
+7425    ///
+7426    /// Try using longer connection IDs.
+7427    #[error("CIDs exhausted")]
+7428    CidsExhausted,
+7429}
+7430
+7431impl From<Close> for ConnectionError {
+7432    fn from(x: Close) -> Self {
+7433        match x {
+7434            Close::Connection(reason) => Self::ConnectionClosed(reason),
+7435            Close::Application(reason) => Self::ApplicationClosed(reason),
+7436        }
+7437    }
+7438}
+7439
+7440// For compatibility with API consumers
+7441impl From<ConnectionError> for io::Error {
+7442    fn from(x: ConnectionError) -> Self {
+7443        use ConnectionError::*;
+7444        let kind = match x {
+7445            TimedOut => io::ErrorKind::TimedOut,
+7446            Reset => io::ErrorKind::ConnectionReset,
+7447            ApplicationClosed(_) | ConnectionClosed(_) => io::ErrorKind::ConnectionAborted,
+7448            TransportError(_) | VersionMismatch | LocallyClosed | CidsExhausted => {
+7449                io::ErrorKind::Other
+7450            }
+7451        };
+7452        Self::new(kind, x)
+7453    }
+7454}
+7455
+7456/// Errors that might trigger a path being closed
+7457// TODO(@divma): maybe needs to be reworked based on what we want to do with the public API
+7458#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)]
+7459pub enum PathError {
+7460    /// The extension was not negotiated with the peer
+7461    #[error("multipath extension not negotiated")]
+7462    MultipathNotNegotiated,
+7463    /// Paths can only be opened client-side
+7464    #[error("the server side may not open a path")]
+7465    ServerSideNotAllowed,
+7466    /// Current limits do not allow us to open more paths
+7467    #[error("maximum number of concurrent paths reached")]
+7468    MaxPathIdReached,
+7469    /// No remote CIDs available to open a new path
+7470    #[error("remoted CIDs exhausted")]
+7471    RemoteCidsExhausted,
+7472    /// Path could not be validated and will be abandoned
+7473    #[error("path validation failed")]
+7474    ValidationFailed,
+7475    /// The remote address for the path is not supported by the endpoint
+7476    #[error("invalid remote address")]
+7477    InvalidRemoteAddress(SocketAddr),
+7478}
+7479
+7480/// Errors triggered when abandoning a path
+7481#[derive(Debug, Error, Clone, Eq, PartialEq)]
+7482pub enum ClosePathError {
+7483    /// Multipath is not negotiated
+7484    #[error("Multipath extension not negotiated")]
+7485    MultipathNotNegotiated,
+7486    /// The path is already closed or was never opened
+7487    #[error("closed path")]
+7488    ClosedPath,
+7489    /// Cannot close the last remaining open path via the local API.
+7490    ///
+7491    /// Use [`Connection::close`] to end the connection instead.
+7492    #[error("last open path")]
+7493    LastOpenPath,
+7494}
+7495
+7496/// Error when the multipath extension was not negotiated, but attempted to be used.
+7497#[derive(Debug, Error, Clone, Copy)]
+7498#[error("Multipath extension not negotiated")]
+7499pub struct MultipathNotNegotiated {
+7500    _private: (),
+7501}
+7502
+7503/// Events of interest to the application
+7504#[derive(Debug)]
+7505pub enum Event {
+7506    /// The connection's handshake data is ready
+7507    HandshakeDataReady,
+7508    /// The connection was successfully established
+7509    Connected,
+7510    /// The TLS handshake was confirmed
+7511    HandshakeConfirmed,
+7512    /// The connection was lost
+7513    ///
+7514    /// Emitted when the connection is closed due to an error, a timeout, or the peer closing it.
+7515    /// This is **not** emitted when the local application closes the connection via
+7516    /// [`Connection::close()`](crate::Connection::close). In that case, pending operations will
+7517    /// fail with [`ConnectionError::LocallyClosed`].
+7518    ConnectionLost {
+7519        /// Reason that the connection was closed
+7520        reason: ConnectionError,
+7521    },
+7522    /// Stream events
+7523    Stream(StreamEvent),
+7524    /// One or more application datagrams have been received
+7525    DatagramReceived,
+7526    /// One or more application datagrams have been sent after blocking
+7527    DatagramsUnblocked,
+7528    /// (Multi)Path events
+7529    Path(PathEvent),
+7530    /// n0's nat traversal events
+7531    NatTraversal(n0_nat_traversal::Event),
+7532}
+7533
+7534impl From<PathEvent> for Event {
+7535    fn from(source: PathEvent) -> Self {
+7536        Self::Path(source)
+7537    }
+7538}
+7539
+7540fn get_max_ack_delay(params: &TransportParameters) -> Duration {
+7541    Duration::from_micros(params.max_ack_delay.0 * 1000)
+7542}
+7543
+7544/// Prevents overflow and improves behavior in extreme circumstances.
+7545const MAX_BACKOFF_EXPONENT: u32 = 16;
+7546
+7547/// The max interval between successive tail-loss probes.
+7548///
+7549/// This is the "normal" value we use.
+7550const MAX_PTO_INTERVAL: Duration = Duration::from_secs(2);
+7551
+7552/// The idle time, below which we use the shorter [`MAX_PTO_FAST_INTERVAL`].
+7553const MIN_IDLE_FOR_FAST_PTO: Duration = Duration::from_secs(25);
+7554
+7555/// The max interval between successive tail-loss probes with short idle times.
+7556///
+7557/// If the path or connection idle time is less than [`MIN_IDLE_FOR_FAST_PTO`] then we use
+7558/// this value to ensure we have plenty of retransmits before we reach the idle time.
+7559const MAX_PTO_FAST_INTERVAL: Duration = Duration::from_secs(1);
+7560
+7561/// The RTT threshold above which we cap the PTO interval to 1.5 * smoothed_rtt
+7562///
+7563/// This is RTT time above which 1.5 * RTT > [`MAX_PTO_INTERVAL`], for these links we want
+7564/// to extend the interval between tail-loss probes to not fill the entire pipe with them.
+7565const SLOW_RTT_THRESHOLD: Duration =
+7566    Duration::from_millis((MAX_PTO_INTERVAL.as_millis() as u64 * 2) / 3);
+7567
+7568/// Minimal remaining size to allow packet coalescing, excluding cryptographic tag
+7569///
+7570/// This must be at least as large as the header for a well-formed empty packet to be coalesced,
+7571/// plus some space for frames. We only care about handshake headers because short header packets
+7572/// necessarily have smaller headers, and initial packets are only ever the first packet in a
+7573/// datagram (because we coalesce in ascending packet space order and the only reason to split a
+7574/// packet is when packet space changes).
+7575const MIN_PACKET_SPACE: usize = MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE + 32;
+7576
+7577/// Largest amount of space that could be occupied by a Handshake or 0-RTT packet's header
+7578///
+7579/// Excludes packet-type-specific fields such as packet number or Initial token
+7580// https://www.rfc-editor.org/rfc/rfc9000.html#name-0-rtt: flags + version + dcid len + dcid +
+7581// scid len + scid + length + pn
+7582const MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE: usize =
+7583    1 + 4 + 1 + MAX_CID_SIZE + 1 + MAX_CID_SIZE + VarInt::from_u32(u16::MAX as u32).size() + 4;
+7584
+7585#[derive(Default)]
+7586struct SentFrames {
+7587    retransmits: ThinRetransmits,
+7588    /// The packet number of the largest acknowledged packet for each path
+7589    largest_acked: FxHashMap<PathId, u64>,
+7590    stream_frames: StreamMetaVec,
+7591    /// Whether the packet contains non-retransmittable frames (like datagrams)
+7592    non_retransmits: bool,
+7593    /// If the datagram containing these frames should be padded to the min MTU
+7594    requires_padding: bool,
+7595}
+7596
+7597impl SentFrames {
+7598    /// Returns whether the packet contains only ACKs
+7599    fn is_ack_only(&self, streams: &StreamsState) -> bool {
+7600        !self.largest_acked.is_empty()
+7601            && !self.non_retransmits
+7602            && self.stream_frames.is_empty()
+7603            && self.retransmits.is_empty(streams)
+7604    }
+7605
+7606    fn retransmits_mut(&mut self) -> &mut Retransmits {
+7607        self.retransmits.get_or_create()
+7608    }
+7609
+7610    fn record_sent_frame(&mut self, frame: frame::EncodableFrame<'_>) {
+7611        use frame::EncodableFrame::*;
+7612        match frame {
+7613            PathAck(path_ack_encoder) => {
+7614                if let Some(max) = path_ack_encoder.ranges.max() {
+7615                    self.largest_acked.insert(path_ack_encoder.path_id, max);
+7616                }
+7617            }
+7618            Ack(ack_encoder) => {
+7619                if let Some(max) = ack_encoder.ranges.max() {
+7620                    self.largest_acked.insert(PathId::ZERO, max);
+7621                }
+7622            }
+7623            Close(_) => { /* non retransmittable, but after this we don't really care */ }
+7624            PathResponse(_) => self.non_retransmits = true,
+7625            HandshakeDone(_) => self.retransmits_mut().handshake_done = true,
+7626            ReachOut(frame) => self.retransmits_mut().reach_out.push(frame),
+7627            ObservedAddr(_) => self.retransmits_mut().observed_addr = true,
+7628            Ping(_) => self.non_retransmits = true,
+7629            ImmediateAck(_) => self.non_retransmits = true,
+7630            AckFrequency(_) => self.retransmits_mut().ack_frequency = true,
+7631            PathChallenge(_) => self.non_retransmits = true,
+7632            Crypto(crypto) => self.retransmits_mut().crypto.push_back(crypto),
+7633            PathAbandon(path_abandon) => {
+7634                self.retransmits_mut()
+7635                    .path_abandon
+7636                    .entry(path_abandon.path_id)
+7637                    .or_insert(path_abandon.error_code);
+7638            }
+7639            PathStatusAvailable(frame::PathStatusAvailable { path_id, .. })
+7640            | PathStatusBackup(frame::PathStatusBackup { path_id, .. }) => {
+7641                self.retransmits_mut().path_status.insert(path_id);
+7642            }
+7643            MaxPathId(_) => self.retransmits_mut().max_path_id = true,
+7644            PathsBlocked(_) => self.retransmits_mut().paths_blocked = true,
+7645            PathCidsBlocked(path_cids_blocked) => {
+7646                self.retransmits_mut()
+7647                    .path_cids_blocked
+7648                    .insert(path_cids_blocked.path_id);
+7649            }
+7650            ResetStream(reset) => self
+7651                .retransmits_mut()
+7652                .reset_stream
+7653                .push((reset.id, reset.error_code)),
+7654            StopSending(stop_sending) => self.retransmits_mut().stop_sending.push(stop_sending),
+7655            NewConnectionId(new_cid) => self.retransmits_mut().new_cids.push(new_cid.issued()),
+7656            RetireConnectionId(retire_cid) => self
+7657                .retransmits_mut()
+7658                .retire_cids
+7659                .push((retire_cid.path_id.unwrap_or_default(), retire_cid.sequence)),
+7660            Datagram(_) => self.non_retransmits = true,
+7661            NewToken(_) => {}
+7662            AddAddress(add_address) => {
+7663                self.retransmits_mut().add_address.insert(add_address);
+7664            }
+7665            RemoveAddress(remove_address) => {
+7666                self.retransmits_mut().remove_address.insert(remove_address);
+7667            }
+7668            StreamMeta(stream_meta_encoder) => self.stream_frames.push(stream_meta_encoder.meta),
+7669            MaxData(_) => self.retransmits_mut().max_data = true,
+7670            MaxStreamData(max) => {
+7671                self.retransmits_mut().max_stream_data.insert(max.id);
+7672            }
+7673            MaxStreams(max_streams) => {
+7674                self.retransmits_mut().max_stream_id[max_streams.dir as usize] = true
+7675            }
+7676            StreamsBlocked(streams_blocked) => {
+7677                self.retransmits_mut().streams_blocked[streams_blocked.dir as usize] = true
+7678            }
+7679        }
+7680    }
+7681}
+7682
+7683/// Compute the negotiated idle timeout based on local and remote max_idle_timeout transport parameters.
+7684///
+7685/// 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.>
+7686///
+7687/// 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.>
+7688///
+7689/// Returns the negotiated idle timeout as a `Duration`, or `None` when both endpoints have opted out of idle timeout.
+7690fn negotiate_max_idle_timeout(x: Option<VarInt>, y: Option<VarInt>) -> Option<Duration> {
+7691    match (x, y) {
+7692        (Some(VarInt(0)) | None, Some(VarInt(0)) | None) => None,
+7693        (Some(VarInt(0)) | None, Some(y)) => Some(Duration::from_millis(y.0)),
+7694        (Some(x), Some(VarInt(0)) | None) => Some(Duration::from_millis(x.0)),
+7695        (Some(x), Some(y)) => Some(Duration::from_millis(cmp::min(x, y).0)),
+7696    }
+7697}
+7698
+7699#[cfg(test)]
+7700mod tests {
+7701    use super::*;
+7702
+7703    #[test]
+7704    fn negotiate_max_idle_timeout_commutative() {
+7705        let test_params = [
+7706            (None, None, None),
+7707            (None, Some(VarInt(0)), None),
+7708            (None, Some(VarInt(2)), Some(Duration::from_millis(2))),
+7709            (Some(VarInt(0)), Some(VarInt(0)), None),
+7710            (
+7711                Some(VarInt(2)),
+7712                Some(VarInt(0)),
+7713                Some(Duration::from_millis(2)),
+7714            ),
+7715            (
+7716                Some(VarInt(1)),
+7717                Some(VarInt(4)),
+7718                Some(Duration::from_millis(1)),
+7719            ),
+7720        ];
+7721
+7722        for (left, right, result) in test_params {
+7723            assert_eq!(negotiate_max_idle_timeout(left, right), result);
+7724            assert_eq!(negotiate_max_idle_timeout(right, left), result);
+7725        }
+7726    }
+7727}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/mtud.rs.html b/pr/672/docs/src/noq_proto/connection/mtud.rs.html new file mode 100644 index 000000000..c102ed67d --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/mtud.rs.html @@ -0,0 +1,982 @@ +mtud.rs - source

noq_proto/connection/
mtud.rs

1use super::SpaceKind;
+2use crate::{Instant, MAX_UDP_PAYLOAD, MtuDiscoveryConfig};
+3use std::cmp;
+4use tracing::trace;
+5
+6/// Implements Datagram Packetization Layer Path Maximum Transmission Unit Discovery
+7///
+8/// See [`MtuDiscoveryConfig`] for details
+9#[derive(Clone, Debug)]
+10pub(crate) struct MtuDiscovery {
+11    /// Detected MTU for the path
+12    current_mtu: u16,
+13    /// The state of the MTU discovery, if enabled
+14    state: Option<EnabledMtuDiscovery>,
+15    /// The state of the black hole detector
+16    black_hole_detector: BlackHoleDetector,
+17}
+18
+19impl MtuDiscovery {
+20    pub(crate) fn new(
+21        initial_plpmtu: u16,
+22        min_mtu: u16,
+23        peer_max_udp_payload_size: Option<u16>,
+24        config: MtuDiscoveryConfig,
+25    ) -> Self {
+26        debug_assert!(
+27            initial_plpmtu >= min_mtu,
+28            "initial_max_udp_payload_size must be at least {min_mtu}"
+29        );
+30
+31        let mut mtud = Self::with_state(
+32            initial_plpmtu,
+33            min_mtu,
+34            Some(EnabledMtuDiscovery::new(config)),
+35        );
+36
+37        // We might be migrating an existing connection to a new path, in which case the transport
+38        // parameters have already been transmitted, and we already know the value of
+39        // `peer_max_udp_payload_size`
+40        if let Some(peer_max_udp_payload_size) = peer_max_udp_payload_size {
+41            mtud.on_peer_max_udp_payload_size_received(peer_max_udp_payload_size);
+42        }
+43
+44        mtud
+45    }
+46
+47    /// MTU discovery will be disabled and the current MTU will be fixed to the provided value
+48    pub(crate) fn disabled(plpmtu: u16, min_mtu: u16) -> Self {
+49        Self::with_state(plpmtu, min_mtu, None)
+50    }
+51
+52    fn with_state(current_mtu: u16, min_mtu: u16, state: Option<EnabledMtuDiscovery>) -> Self {
+53        Self {
+54            current_mtu,
+55            state,
+56            black_hole_detector: BlackHoleDetector::new(min_mtu),
+57        }
+58    }
+59
+60    /// Returns the current MTU
+61    pub(crate) fn current_mtu(&self) -> u16 {
+62        self.current_mtu
+63    }
+64
+65    /// Returns the amount of bytes that should be sent as an MTU probe, if any.
+66    ///
+67    /// Returns [`None`] if MTUD discovery is disabled. Otherwise delegates to
+68    /// [`EnabledMtuDiscovery::poll_transmit`].
+69    pub(crate) fn poll_transmit(&mut self, now: Instant, next_pn: u64) -> Option<u16> {
+70        self.state
+71            .as_mut()
+72            .and_then(|state| state.poll_transmit(now, self.current_mtu, next_pn))
+73    }
+74
+75    /// Notifies the [`MtuDiscovery`] that the peer's `max_udp_payload_size` transport parameter has
+76    /// been received
+77    pub(crate) fn on_peer_max_udp_payload_size_received(&mut self, peer_max_udp_payload_size: u16) {
+78        self.current_mtu = self.current_mtu.min(peer_max_udp_payload_size);
+79
+80        if let Some(state) = self.state.as_mut() {
+81            // It is possible for black hole detection to trigger before the connection has been
+82            // fully established, if the initial MTU is greater the minimum MTU. We should never
+83            // send probes before the connection has been fully established and we have received
+84            // the peer's transport parameters though.
+85            debug_assert!(
+86                !matches!(state.phase, Phase::Searching(_)),
+87                "Transport parameters received after MTU probing started"
+88            );
+89            state.peer_max_udp_payload_size = peer_max_udp_payload_size;
+90        }
+91    }
+92
+93    /// Notifies the [`MtuDiscovery`] that a packet has been ACKed
+94    ///
+95    /// Returns true if the packet was an MTU probe
+96    pub(crate) fn on_acked(&mut self, space: SpaceKind, pn: u64, len: u16) -> bool {
+97        // MTU probes are only sent in application data space
+98        if space != SpaceKind::Data {
+99            return false;
+100        }
+101
+102        // Update the state of the MTU search
+103        if let Some(new_mtu) = self
+104            .state
+105            .as_mut()
+106            .and_then(|state| state.on_probe_acked(pn))
+107        {
+108            self.current_mtu = new_mtu;
+109            trace!(current_mtu = self.current_mtu, "new MTU detected");
+110
+111            self.black_hole_detector.on_probe_acked(pn, len);
+112            true
+113        } else {
+114            self.black_hole_detector.on_non_probe_acked(pn, len);
+115            false
+116        }
+117    }
+118
+119    /// Returns the packet number of the in-flight MTU probe, if any
+120    pub(crate) fn in_flight_mtu_probe(&self) -> Option<u64> {
+121        match &self.state {
+122            Some(EnabledMtuDiscovery {
+123                phase: Phase::Searching(search_state),
+124                ..
+125            }) => search_state.in_flight_probe,
+126            _ => None,
+127        }
+128    }
+129
+130    /// Notifies the [`MtuDiscovery`] that the in-flight MTU probe was lost
+131    pub(crate) fn on_probe_lost(&mut self) {
+132        if let Some(state) = &mut self.state {
+133            state.on_probe_lost();
+134        }
+135    }
+136
+137    /// Notifies the [`MtuDiscovery`] that a non-probe packet was lost
+138    ///
+139    /// When done notifying of lost packets, [`MtuDiscovery::black_hole_detected`] must be called, to
+140    /// ensure the last loss burst is properly processed and to trigger black hole recovery logic if
+141    /// necessary.
+142    pub(crate) fn on_non_probe_lost(&mut self, pn: u64, len: u16) {
+143        self.black_hole_detector.on_non_probe_lost(pn, len);
+144    }
+145
+146    /// Returns true if a black hole was detected
+147    ///
+148    /// Calling this function will close the previous loss burst. If a black hole is detected, the
+149    /// current MTU will be reset to `min_mtu`.
+150    pub(crate) fn black_hole_detected(&mut self, now: Instant) -> bool {
+151        if !self.black_hole_detector.black_hole_detected() {
+152            return false;
+153        }
+154
+155        self.current_mtu = self.black_hole_detector.min_mtu;
+156
+157        if let Some(state) = &mut self.state {
+158            state.on_black_hole_detected(now);
+159        }
+160
+161        true
+162    }
+163}
+164
+165/// Additional state for enabled MTU discovery
+166#[derive(Debug, Clone)]
+167struct EnabledMtuDiscovery {
+168    phase: Phase,
+169    peer_max_udp_payload_size: u16,
+170    config: MtuDiscoveryConfig,
+171}
+172
+173impl EnabledMtuDiscovery {
+174    fn new(config: MtuDiscoveryConfig) -> Self {
+175        Self {
+176            phase: Phase::Initial,
+177            peer_max_udp_payload_size: MAX_UDP_PAYLOAD,
+178            config,
+179        }
+180    }
+181
+182    /// Returns the amount of bytes that should be sent as an MTU probe, if any.
+183    ///
+184    /// A probe only needs to be sent if:
+185    ///
+186    /// - There is no current in-flight probe.
+187    /// - A search for a new MTU is in progress.
+188    /// - The MTU discovery was completed but the [`MtuDiscoveryConfig::interval`] expired,
+189    ///   this re-starts a n MTU search.
+190    fn poll_transmit(&mut self, now: Instant, current_mtu: u16, next_pn: u64) -> Option<u16> {
+191        if let Phase::Initial = &self.phase {
+192            // Start the first search
+193            self.phase = Phase::Searching(SearchState::new(
+194                current_mtu,
+195                self.peer_max_udp_payload_size,
+196                &self.config,
+197            ));
+198        } else if let Phase::Complete(next_mtud_activation) = &self.phase {
+199            if now < *next_mtud_activation {
+200                return None;
+201            }
+202
+203            // Start a new search (we have reached the next activation time)
+204            self.phase = Phase::Searching(SearchState::new(
+205                current_mtu,
+206                self.peer_max_udp_payload_size,
+207                &self.config,
+208            ));
+209        }
+210
+211        if let Phase::Searching(state) = &mut self.phase {
+212            // Nothing to do while there is a probe in flight
+213            if state.in_flight_probe.is_some() {
+214                return None;
+215            }
+216
+217            // Retransmit lost probes, if any
+218            if 0 < state.lost_probe_count && state.lost_probe_count < MAX_PROBE_RETRANSMITS {
+219                state.in_flight_probe = Some(next_pn);
+220                return Some(state.last_probed_mtu);
+221            }
+222
+223            let last_probe_succeeded = state.lost_probe_count == 0;
+224
+225            // The probe is definitely lost (we reached the MAX_PROBE_RETRANSMITS threshold)
+226            if !last_probe_succeeded {
+227                state.lost_probe_count = 0;
+228                state.in_flight_probe = None;
+229            }
+230
+231            if let Some(probe_udp_payload_size) = state.next_mtu_to_probe(last_probe_succeeded) {
+232                state.in_flight_probe = Some(next_pn);
+233                state.last_probed_mtu = probe_udp_payload_size;
+234                return Some(probe_udp_payload_size);
+235            } else {
+236                let next_mtud_activation = now + self.config.interval;
+237                self.phase = Phase::Complete(next_mtud_activation);
+238                return None;
+239            }
+240        }
+241
+242        None
+243    }
+244
+245    /// Called when a packet is acknowledged in [`SpaceId::Data`]
+246    ///
+247    /// Returns the new `current_mtu` if the packet number corresponds to the in-flight MTU probe
+248    ///
+249    /// [`SpaceId::Data`]: crate::packet::SpaceId
+250    fn on_probe_acked(&mut self, pn: u64) -> Option<u16> {
+251        match &mut self.phase {
+252            Phase::Searching(state) if state.in_flight_probe == Some(pn) => {
+253                state.in_flight_probe = None;
+254                state.lost_probe_count = 0;
+255                Some(state.last_probed_mtu)
+256            }
+257            _ => None,
+258        }
+259    }
+260
+261    /// Called when the in-flight MTU probe was lost
+262    fn on_probe_lost(&mut self) {
+263        // We might no longer be searching, e.g. if a black hole was detected
+264        if let Phase::Searching(state) = &mut self.phase {
+265            state.in_flight_probe = None;
+266            state.lost_probe_count += 1;
+267        }
+268    }
+269
+270    /// Called when a black hole is detected
+271    fn on_black_hole_detected(&mut self, now: Instant) {
+272        // Stop searching, if applicable, and reset the timer
+273        let next_mtud_activation = now + self.config.black_hole_cooldown;
+274        self.phase = Phase::Complete(next_mtud_activation);
+275    }
+276}
+277
+278#[derive(Debug, Clone, Copy)]
+279enum Phase {
+280    /// We haven't started polling yet
+281    Initial,
+282    /// We are currently searching for a higher PMTU
+283    Searching(SearchState),
+284    /// Searching has completed and will be triggered again at the provided instant
+285    Complete(Instant),
+286}
+287
+288#[derive(Debug, Clone, Copy)]
+289struct SearchState {
+290    /// The lower bound for the current binary search
+291    lower_bound: u16,
+292    /// The upper bound for the current binary search
+293    upper_bound: u16,
+294    /// The minimum change to stop the current binary search
+295    minimum_change: u16,
+296    /// The UDP payload size we last sent a probe for
+297    last_probed_mtu: u16,
+298    /// Packet number of an in-flight probe (if any)
+299    in_flight_probe: Option<u64>,
+300    /// Lost probes at the current probe size
+301    lost_probe_count: usize,
+302}
+303
+304impl SearchState {
+305    /// Creates a new search state, with the specified lower bound (the upper bound is derived from
+306    /// the config and the peer's `max_udp_payload_size` transport parameter)
+307    fn new(
+308        mut lower_bound: u16,
+309        peer_max_udp_payload_size: u16,
+310        config: &MtuDiscoveryConfig,
+311    ) -> Self {
+312        lower_bound = lower_bound.min(peer_max_udp_payload_size);
+313        let upper_bound = config
+314            .upper_bound
+315            .clamp(lower_bound, peer_max_udp_payload_size);
+316
+317        Self {
+318            in_flight_probe: None,
+319            lost_probe_count: 0,
+320            lower_bound,
+321            upper_bound,
+322            minimum_change: config.minimum_change,
+323            // During initialization, we consider the lower bound to have already been
+324            // successfully probed
+325            last_probed_mtu: lower_bound,
+326        }
+327    }
+328
+329    /// Determines the next MTU to probe using binary search
+330    fn next_mtu_to_probe(&mut self, last_probe_succeeded: bool) -> Option<u16> {
+331        debug_assert_eq!(self.in_flight_probe, None);
+332
+333        if last_probe_succeeded {
+334            self.lower_bound = self.last_probed_mtu;
+335        } else {
+336            self.upper_bound = self.last_probed_mtu - 1;
+337        }
+338
+339        let next_mtu = (self.lower_bound as i32 + self.upper_bound as i32) / 2;
+340
+341        // Binary search stopping condition
+342        if ((next_mtu - self.last_probed_mtu as i32).unsigned_abs() as u16) < self.minimum_change {
+343            // Special case: if the upper bound is far enough, we want to probe it as a last
+344            // step (otherwise we will never achieve the upper bound)
+345            if self.upper_bound.saturating_sub(self.last_probed_mtu) >= self.minimum_change {
+346                return Some(self.upper_bound);
+347            }
+348
+349            return None;
+350        }
+351
+352        Some(next_mtu as u16)
+353    }
+354}
+355
+356/// Judges whether packet loss might indicate a drop in MTU
+357///
+358/// Our MTU black hole detection scheme is a heuristic based on the order in which packets were sent
+359/// (the packet number order), their sizes, and which are deemed lost.
+360///
+361/// First, contiguous groups of lost packets ("loss bursts") are aggregated, because a group of
+362/// packets all lost together were probably lost for the same reason.
+363///
+364/// A loss burst is deemed "suspicious" if it contains no packets that are (a) smaller than the
+365/// minimum MTU or (b) smaller than a more recent acknowledged packet, because such a burst could be
+366/// fully explained by a reduction in MTU.
+367///
+368/// When the number of suspicious loss bursts exceeds [`BLACK_HOLE_THRESHOLD`], we judge the
+369/// evidence for an MTU black hole to be sufficient.
+370#[derive(Clone, Debug)]
+371struct BlackHoleDetector {
+372    /// Packet loss bursts currently considered suspicious
+373    suspicious_loss_bursts: Vec<LossBurst>,
+374    /// Loss burst currently being aggregated, if any
+375    current_loss_burst: Option<CurrentLossBurst>,
+376    /// Packet number of the biggest packet larger than `min_mtu` which we've received
+377    /// acknowledgment of more recently than any suspicious loss burst, if any
+378    largest_post_loss_packet: u64,
+379    /// The maximum of `min_mtu` and the size of `largest_post_loss_packet`, or exactly `min_mtu` if
+380    /// no larger packets have been received since the most recent loss burst.
+381    acked_mtu: u16,
+382    /// The UDP payload size guaranteed to be supported by the network
+383    min_mtu: u16,
+384}
+385
+386impl BlackHoleDetector {
+387    fn new(min_mtu: u16) -> Self {
+388        Self {
+389            suspicious_loss_bursts: Vec::with_capacity(BLACK_HOLE_THRESHOLD + 1),
+390            current_loss_burst: None,
+391            largest_post_loss_packet: 0,
+392            acked_mtu: min_mtu,
+393            min_mtu,
+394        }
+395    }
+396
+397    fn on_probe_acked(&mut self, pn: u64, len: u16) {
+398        // MTU probes are always larger than the previous MTU, so no previous loss bursts are
+399        // suspicious. At most one MTU probe is in flight at a time, so we don't need to worry about
+400        // reordering between them.
+401        self.suspicious_loss_bursts.clear();
+402        self.acked_mtu = len;
+403        // This might go backwards, but that's okay: a successful ACK means we haven't yet judged a
+404        // more recently sent packet lost, and we just want to track the largest packet that's been
+405        // successfully delivered more recently than a loss.
+406        self.largest_post_loss_packet = pn;
+407    }
+408
+409    fn on_non_probe_acked(&mut self, pn: u64, len: u16) {
+410        if len <= self.acked_mtu {
+411            // We've already seen a larger packet since the most recent suspicious loss burst;
+412            // nothing to do.
+413            return;
+414        }
+415        self.acked_mtu = len;
+416        // This might go backwards, but that's okay as described in `on_probe_acked`.
+417        self.largest_post_loss_packet = pn;
+418        // Loss bursts packets smaller than this are retroactively deemed non-suspicious.
+419        self.suspicious_loss_bursts
+420            .retain(|burst| burst.smallest_packet_size > len);
+421    }
+422
+423    fn on_non_probe_lost(&mut self, pn: u64, len: u16) {
+424        // A loss burst is a group of consecutive packets that are declared lost, so a distance
+425        // greater than 1 indicates a new burst
+426        let end_last_burst = self
+427            .current_loss_burst
+428            .as_ref()
+429            .is_some_and(|current| pn - current.latest_non_probe != 1);
+430
+431        if end_last_burst {
+432            self.finish_loss_burst();
+433        }
+434
+435        self.current_loss_burst = Some(CurrentLossBurst {
+436            latest_non_probe: pn,
+437            smallest_packet_size: self
+438                .current_loss_burst
+439                .map_or(len, |prev| cmp::min(prev.smallest_packet_size, len)),
+440        });
+441    }
+442
+443    fn black_hole_detected(&mut self) -> bool {
+444        self.finish_loss_burst();
+445
+446        if self.suspicious_loss_bursts.len() <= BLACK_HOLE_THRESHOLD {
+447            return false;
+448        }
+449
+450        self.suspicious_loss_bursts.clear();
+451
+452        true
+453    }
+454
+455    /// Marks the end of the current loss burst, checking whether it was suspicious
+456    fn finish_loss_burst(&mut self) {
+457        let Some(burst) = self.current_loss_burst.take() else {
+458            return;
+459        };
+460        // If a loss burst contains a packet smaller than the minimum MTU or a more recently
+461        // transmitted packet, it is not suspicious.
+462        if burst.smallest_packet_size <= self.min_mtu
+463            || (burst.latest_non_probe < self.largest_post_loss_packet
+464                && burst.smallest_packet_size <= self.acked_mtu)
+465        {
+466            return;
+467        }
+468        // The loss burst is now deemed suspicious.
+469
+470        // A suspicious loss burst more recent than `largest_post_loss_packet` invalidates it. This
+471        // makes `acked_mtu` a conservative approximation. Ideally we'd update `safe_mtu` and
+472        // `largest_post_loss_packet` to describe the largest acknowledged packet sent later than
+473        // this burst, but that would require tracking the size of an unpredictable number of
+474        // recently acknowledged packets, and erring on the side of false positives is safe.
+475        if burst.latest_non_probe > self.largest_post_loss_packet {
+476            self.acked_mtu = self.min_mtu;
+477        }
+478
+479        let burst = LossBurst {
+480            smallest_packet_size: burst.smallest_packet_size,
+481        };
+482
+483        if self.suspicious_loss_bursts.len() <= BLACK_HOLE_THRESHOLD {
+484            self.suspicious_loss_bursts.push(burst);
+485            return;
+486        }
+487
+488        // To limit memory use, only track the most suspicious loss bursts.
+489        let smallest = self
+490            .suspicious_loss_bursts
+491            .iter_mut()
+492            .min_by_key(|prev| prev.smallest_packet_size)
+493            .filter(|prev| prev.smallest_packet_size < burst.smallest_packet_size);
+494        if let Some(smallest) = smallest {
+495            *smallest = burst;
+496        }
+497    }
+498
+499    #[cfg(test)]
+500    fn suspicious_loss_burst_count(&self) -> usize {
+501        self.suspicious_loss_bursts.len()
+502    }
+503
+504    #[cfg(test)]
+505    fn largest_non_probe_lost(&self) -> Option<u64> {
+506        self.current_loss_burst.as_ref().map(|x| x.latest_non_probe)
+507    }
+508}
+509
+510#[derive(Copy, Clone, Debug)]
+511struct LossBurst {
+512    smallest_packet_size: u16,
+513}
+514
+515#[derive(Copy, Clone, Debug)]
+516struct CurrentLossBurst {
+517    smallest_packet_size: u16,
+518    latest_non_probe: u64,
+519}
+520
+521// Corresponds to the RFC's `MAX_PROBES` constant (see
+522// https://www.rfc-editor.org/rfc/rfc8899#section-5.1.2)
+523const MAX_PROBE_RETRANSMITS: usize = 3;
+524/// Maximum number of suspicious loss bursts that will not trigger black hole detection
+525const BLACK_HOLE_THRESHOLD: usize = 3;
+526
+527#[cfg(test)]
+528mod tests {
+529    use super::*;
+530    use crate::Duration;
+531    use crate::MAX_UDP_PAYLOAD;
+532    use assert_matches::assert_matches;
+533
+534    fn default_mtud() -> MtuDiscovery {
+535        let config = MtuDiscoveryConfig::default();
+536        MtuDiscovery::new(1_200, 1_200, None, config)
+537    }
+538
+539    fn completed(mtud: &MtuDiscovery) -> bool {
+540        matches!(mtud.state.as_ref().unwrap().phase, Phase::Complete(_))
+541    }
+542
+543    /// Drives mtud until it reaches `Phase::Completed`
+544    fn drive_to_completion(
+545        mtud: &mut MtuDiscovery,
+546        now: Instant,
+547        link_payload_size_limit: u16,
+548    ) -> Vec<u16> {
+549        let mut probed_sizes = Vec::new();
+550        for probe_pn in 1..100 {
+551            let result = mtud.poll_transmit(now, probe_pn);
+552
+553            if completed(mtud) {
+554                break;
+555            }
+556
+557            // "Send" next probe
+558            assert!(result.is_some());
+559            let probe_size = result.unwrap();
+560            probed_sizes.push(probe_size);
+561
+562            if probe_size <= link_payload_size_limit {
+563                mtud.on_acked(SpaceKind::Data, probe_pn, probe_size);
+564            } else {
+565                mtud.on_probe_lost();
+566            }
+567        }
+568        probed_sizes
+569    }
+570
+571    #[test]
+572    fn black_hole_detector_ignores_burst_containing_non_suspicious_packet() {
+573        let mut mtud = default_mtud();
+574        mtud.on_non_probe_lost(2, 1300);
+575        mtud.on_non_probe_lost(3, 1300);
+576        assert_eq!(mtud.black_hole_detector.largest_non_probe_lost(), Some(3));
+577        assert_eq!(mtud.black_hole_detector.suspicious_loss_burst_count(), 0);
+578
+579        mtud.on_non_probe_lost(4, 800);
+580        assert!(!mtud.black_hole_detected(Instant::now()));
+581        assert_eq!(mtud.black_hole_detector.largest_non_probe_lost(), None);
+582        assert_eq!(mtud.black_hole_detector.suspicious_loss_burst_count(), 0);
+583    }
+584
+585    #[test]
+586    fn black_hole_detector_counts_burst_containing_only_suspicious_packets() {
+587        let mut mtud = default_mtud();
+588        mtud.on_non_probe_lost(2, 1300);
+589        mtud.on_non_probe_lost(3, 1300);
+590        assert_eq!(mtud.black_hole_detector.largest_non_probe_lost(), Some(3));
+591        assert_eq!(mtud.black_hole_detector.suspicious_loss_burst_count(), 0);
+592
+593        assert!(!mtud.black_hole_detected(Instant::now()));
+594        assert_eq!(mtud.black_hole_detector.largest_non_probe_lost(), None);
+595        assert_eq!(mtud.black_hole_detector.suspicious_loss_burst_count(), 1);
+596    }
+597
+598    #[test]
+599    fn black_hole_detector_ignores_empty_burst() {
+600        let mut mtud = default_mtud();
+601        assert!(!mtud.black_hole_detected(Instant::now()));
+602        assert_eq!(mtud.black_hole_detector.suspicious_loss_burst_count(), 0);
+603    }
+604
+605    #[test]
+606    fn mtu_discovery_disabled_does_nothing() {
+607        let mut mtud = MtuDiscovery::disabled(1_200, 1_200);
+608        let probe_size = mtud.poll_transmit(Instant::now(), 0);
+609        assert_eq!(probe_size, None);
+610    }
+611
+612    #[test]
+613    fn mtu_discovery_disabled_lost_four_packet_bursts_triggers_black_hole_detection() {
+614        let mut mtud = MtuDiscovery::disabled(1_400, 1_250);
+615        let now = Instant::now();
+616
+617        for i in 0..4 {
+618            // The packets are never contiguous, so each one has its own burst
+619            mtud.on_non_probe_lost(i * 2, 1300);
+620        }
+621
+622        assert!(mtud.black_hole_detected(now));
+623        assert_eq!(mtud.current_mtu, 1250);
+624        assert_matches!(mtud.state, None);
+625    }
+626
+627    #[test]
+628    fn mtu_discovery_lost_two_packet_bursts_does_not_trigger_black_hole_detection() {
+629        let mut mtud = default_mtud();
+630        let now = Instant::now();
+631
+632        for i in 0..2 {
+633            mtud.on_non_probe_lost(i, 1300);
+634            assert!(!mtud.black_hole_detected(now));
+635        }
+636    }
+637
+638    #[test]
+639    fn mtu_discovery_lost_four_packet_bursts_triggers_black_hole_detection_and_resets_timer() {
+640        let mut mtud = default_mtud();
+641        let now = Instant::now();
+642
+643        for i in 0..4 {
+644            // The packets are never contiguous, so each one has its own burst
+645            mtud.on_non_probe_lost(i * 2, 1300);
+646        }
+647
+648        assert!(mtud.black_hole_detected(now));
+649        assert_eq!(mtud.current_mtu, 1200);
+650        if let Phase::Complete(next_mtud_activation) = mtud.state.unwrap().phase {
+651            assert_eq!(next_mtud_activation, now + Duration::from_secs(60));
+652        } else {
+653            panic!("Unexpected MTUD phase!");
+654        }
+655    }
+656
+657    #[test]
+658    fn mtu_discovery_after_complete_reactivates_when_interval_elapsed() {
+659        let mut config = MtuDiscoveryConfig::default();
+660        config.upper_bound(9_000);
+661        let mut mtud = MtuDiscovery::new(1_200, 1_200, None, config);
+662        let now = Instant::now();
+663        drive_to_completion(&mut mtud, now, 1_500);
+664
+665        // Polling right after completion does not cause new packets to be sent
+666        assert_eq!(mtud.poll_transmit(now, 42), None);
+667        assert!(completed(&mtud));
+668        assert_eq!(mtud.current_mtu, 1_471);
+669
+670        // Polling after the interval has passed does (taking the current mtu as lower bound)
+671        assert_eq!(
+672            mtud.poll_transmit(now + Duration::from_secs(600), 43),
+673            Some(5235)
+674        );
+675
+676        match mtud.state.unwrap().phase {
+677            Phase::Searching(state) => {
+678                assert_eq!(state.lower_bound, 1_471);
+679                assert_eq!(state.upper_bound, 9_000);
+680            }
+681            _ => {
+682                panic!("Unexpected MTUD phase!")
+683            }
+684        }
+685    }
+686
+687    #[test]
+688    fn mtu_discovery_lost_three_probes_lowers_probe_size() {
+689        let mut mtud = default_mtud();
+690
+691        let mut probe_sizes = (0..4).map(|i| {
+692            let probe_size = mtud.poll_transmit(Instant::now(), i);
+693            assert!(probe_size.is_some(), "no probe returned for packet {i}");
+694
+695            mtud.on_probe_lost();
+696            probe_size.unwrap()
+697        });
+698
+699        // After the first probe is lost, it gets retransmitted twice
+700        let first_probe_size = probe_sizes.next().unwrap();
+701        for _ in 0..2 {
+702            assert_eq!(probe_sizes.next().unwrap(), first_probe_size)
+703        }
+704
+705        // After the third probe is lost, we decrement our probe size
+706        let fourth_probe_size = probe_sizes.next().unwrap();
+707        assert!(fourth_probe_size < first_probe_size);
+708        assert_eq!(
+709            fourth_probe_size,
+710            first_probe_size - (first_probe_size - 1_200) / 2 - 1
+711        );
+712    }
+713
+714    #[test]
+715    fn mtu_discovery_with_peer_max_udp_payload_size_clamps_upper_bound() {
+716        let mut mtud = default_mtud();
+717
+718        mtud.on_peer_max_udp_payload_size_received(1300);
+719        let probed_sizes = drive_to_completion(&mut mtud, Instant::now(), 1500);
+720
+721        assert_eq!(mtud.state.as_ref().unwrap().peer_max_udp_payload_size, 1300);
+722        assert_eq!(mtud.current_mtu, 1300);
+723        let expected_probed_sizes = &[1250, 1275, 1300];
+724        assert_eq!(probed_sizes, expected_probed_sizes);
+725        assert!(completed(&mtud));
+726    }
+727
+728    #[test]
+729    fn mtu_discovery_with_previous_peer_max_udp_payload_size_clamps_upper_bound() {
+730        let mut mtud = MtuDiscovery::new(1500, 1_200, Some(1400), MtuDiscoveryConfig::default());
+731
+732        assert_eq!(mtud.current_mtu, 1400);
+733        assert_eq!(mtud.state.as_ref().unwrap().peer_max_udp_payload_size, 1400);
+734
+735        let probed_sizes = drive_to_completion(&mut mtud, Instant::now(), 1500);
+736
+737        assert_eq!(mtud.current_mtu, 1400);
+738        assert!(probed_sizes.is_empty());
+739        assert!(completed(&mtud));
+740    }
+741
+742    #[cfg(debug_assertions)]
+743    #[test]
+744    #[should_panic(expected = "Transport parameters received after MTU probing started")]
+745    fn mtu_discovery_with_peer_max_udp_payload_size_during_search_panics() {
+746        let mut mtud = default_mtud();
+747        assert!(mtud.poll_transmit(Instant::now(), 0).is_some());
+748        assert!(matches!(
+749            mtud.state.as_ref().unwrap().phase,
+750            Phase::Searching(_)
+751        ));
+752        mtud.on_peer_max_udp_payload_size_received(1300);
+753    }
+754
+755    #[test]
+756    fn mtu_discovery_with_1500_limit() {
+757        let mut mtud = default_mtud();
+758
+759        let probed_sizes = drive_to_completion(&mut mtud, Instant::now(), 1500);
+760
+761        let expected_probed_sizes = &[1326, 1389, 1420, 1452];
+762        assert_eq!(probed_sizes, expected_probed_sizes);
+763        assert_eq!(mtud.current_mtu, 1452);
+764        assert!(completed(&mtud));
+765    }
+766
+767    #[test]
+768    fn mtu_discovery_with_1500_limit_and_10000_upper_bound() {
+769        let mut config = MtuDiscoveryConfig::default();
+770        config.upper_bound(10_000);
+771        let mut mtud = MtuDiscovery::new(1_200, 1_200, None, config);
+772
+773        let probed_sizes = drive_to_completion(&mut mtud, Instant::now(), 1500);
+774
+775        let expected_probed_sizes = &[
+776            5600, 5600, 5600, 3399, 3399, 3399, 2299, 2299, 2299, 1749, 1749, 1749, 1474, 1611,
+777            1611, 1611, 1542, 1542, 1542, 1507, 1507, 1507,
+778        ];
+779        assert_eq!(probed_sizes, expected_probed_sizes);
+780        assert_eq!(mtud.current_mtu, 1474);
+781        assert!(completed(&mtud));
+782    }
+783
+784    #[test]
+785    fn mtu_discovery_no_lost_probes_finds_maximum_udp_payload() {
+786        let mut config = MtuDiscoveryConfig::default();
+787        config.upper_bound(MAX_UDP_PAYLOAD);
+788        let mut mtud = MtuDiscovery::new(1200, 1200, None, config);
+789
+790        drive_to_completion(&mut mtud, Instant::now(), u16::MAX);
+791
+792        assert_eq!(mtud.current_mtu, 65527);
+793        assert!(completed(&mtud));
+794    }
+795
+796    #[test]
+797    fn mtu_discovery_lost_half_of_probes_finds_maximum_udp_payload() {
+798        let mut config = MtuDiscoveryConfig::default();
+799        config.upper_bound(MAX_UDP_PAYLOAD);
+800        let mut mtud = MtuDiscovery::new(1200, 1200, None, config);
+801
+802        let now = Instant::now();
+803        let mut iterations = 0;
+804        for i in 1..100 {
+805            iterations += 1;
+806
+807            let probe_pn = i * 2 - 1;
+808            let other_pn = i * 2;
+809
+810            let result = mtud.poll_transmit(Instant::now(), probe_pn);
+811
+812            if completed(&mtud) {
+813                break;
+814            }
+815
+816            // "Send" next probe
+817            assert!(result.is_some());
+818            assert!(mtud.in_flight_mtu_probe().is_some());
+819
+820            // Nothing else to send while the probe is in-flight
+821            assert_matches!(mtud.poll_transmit(now, other_pn), None);
+822
+823            if i % 2 == 0 {
+824                // ACK probe and ensure it results in an increase of current_mtu
+825                let previous_max_size = mtud.current_mtu;
+826                mtud.on_acked(SpaceKind::Data, probe_pn, result.unwrap());
+827                println!(
+828                    "ACK packet {}. Previous MTU = {previous_max_size}. New MTU = {}",
+829                    result.unwrap(),
+830                    mtud.current_mtu
+831                );
+832                // assert!(mtud.current_mtu > previous_max_size);
+833            } else {
+834                mtud.on_probe_lost();
+835            }
+836        }
+837
+838        assert_eq!(iterations, 25);
+839        assert_eq!(mtud.current_mtu, 65527);
+840        assert!(completed(&mtud));
+841    }
+842
+843    #[test]
+844    fn search_state_lower_bound_higher_than_upper_bound_clamps_upper_bound() {
+845        let mut config = MtuDiscoveryConfig::default();
+846        config.upper_bound(1400);
+847
+848        let state = SearchState::new(1500, u16::MAX, &config);
+849        assert_eq!(state.lower_bound, 1500);
+850        assert_eq!(state.upper_bound, 1500);
+851    }
+852
+853    #[test]
+854    fn search_state_lower_bound_higher_than_peer_max_udp_payload_size_clamps_lower_bound() {
+855        let mut config = MtuDiscoveryConfig::default();
+856        config.upper_bound(9000);
+857
+858        let state = SearchState::new(1500, 1300, &config);
+859        assert_eq!(state.lower_bound, 1300);
+860        assert_eq!(state.upper_bound, 1300);
+861    }
+862
+863    #[test]
+864    fn search_state_upper_bound_higher_than_peer_max_udp_payload_size_clamps_upper_bound() {
+865        let mut config = MtuDiscoveryConfig::default();
+866        config.upper_bound(9000);
+867
+868        let state = SearchState::new(1200, 1450, &config);
+869        assert_eq!(state.lower_bound, 1200);
+870        assert_eq!(state.upper_bound, 1450);
+871    }
+872
+873    // Loss of packets larger than have been acknowledged should indicate a black hole
+874    #[test]
+875    fn simple_black_hole_detection() {
+876        let mut bhd = BlackHoleDetector::new(1200);
+877        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 2, 1300);
+878        for i in 0..BLACK_HOLE_THRESHOLD {
+879            bhd.on_non_probe_lost(i as u64 * 2, 1400);
+880        }
+881        // But not before `BLACK_HOLE_THRESHOLD + 1` bursts
+882        assert!(!bhd.black_hole_detected());
+883        bhd.on_non_probe_lost(BLACK_HOLE_THRESHOLD as u64 * 2, 1400);
+884        assert!(bhd.black_hole_detected());
+885    }
+886
+887    // Loss of packets followed in transmission order by confirmation of a larger packet should not
+888    // indicate a black hole
+889    #[test]
+890    fn non_suspicious_bursts() {
+891        let mut bhd = BlackHoleDetector::new(1200);
+892        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 2, 1500);
+893        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+894            bhd.on_non_probe_lost(i as u64 * 2, 1400);
+895        }
+896        assert!(!bhd.black_hole_detected());
+897    }
+898
+899    // Loss of packets smaller than have been acknowledged previously should still indicate a black
+900    // hole
+901    #[test]
+902    fn dynamic_mtu_reduction() {
+903        let mut bhd = BlackHoleDetector::new(1200);
+904        bhd.on_non_probe_acked(0, 1500);
+905        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+906            bhd.on_non_probe_lost(i as u64 * 2, 1400);
+907        }
+908        assert!(bhd.black_hole_detected());
+909    }
+910
+911    // Bursts containing heterogeneous packets are judged based on the smallest
+912    #[test]
+913    fn mixed_non_suspicious_bursts() {
+914        let mut bhd = BlackHoleDetector::new(1200);
+915        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 3, 1400);
+916        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+917            bhd.on_non_probe_lost(i as u64 * 3, 1500);
+918            bhd.on_non_probe_lost(i as u64 * 3 + 1, 1300);
+919        }
+920        assert!(!bhd.black_hole_detected());
+921    }
+922
+923    // Multi-packet bursts are only counted once
+924    #[test]
+925    fn bursts_count_once() {
+926        let mut bhd = BlackHoleDetector::new(1200);
+927        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 3, 1400);
+928        for i in 0..(BLACK_HOLE_THRESHOLD) {
+929            bhd.on_non_probe_lost(i as u64 * 3, 1500);
+930            bhd.on_non_probe_lost(i as u64 * 3 + 1, 1500);
+931        }
+932        assert!(!bhd.black_hole_detected());
+933        bhd.on_non_probe_lost(BLACK_HOLE_THRESHOLD as u64 * 3, 1500);
+934        assert!(bhd.black_hole_detected());
+935    }
+936
+937    // Non-suspicious bursts don't interfere with detection of suspicious bursts
+938    #[test]
+939    fn interleaved_bursts() {
+940        let mut bhd = BlackHoleDetector::new(1200);
+941        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 4, 1400);
+942        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+943            bhd.on_non_probe_lost(i as u64 * 4, 1500);
+944            bhd.on_non_probe_lost(i as u64 * 4 + 2, 1300);
+945        }
+946        assert!(bhd.black_hole_detected());
+947    }
+948
+949    // Bursts that are non-suspicious before a delivered packet become suspicious past it
+950    #[test]
+951    fn suspicious_after_acked() {
+952        let mut bhd = BlackHoleDetector::new(1200);
+953        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 2, 1400);
+954        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+955            bhd.on_non_probe_lost(i as u64 * 2, 1300);
+956        }
+957        assert!(
+958            !bhd.black_hole_detected(),
+959            "1300 byte losses preceding a 1400 byte delivery are not suspicious"
+960        );
+961        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+962            bhd.on_non_probe_lost((BLACK_HOLE_THRESHOLD as u64 + 1 + i as u64) * 2, 1300);
+963        }
+964        assert!(
+965            bhd.black_hole_detected(),
+966            "1300 byte losses following a 1400 byte delivery are suspicious"
+967        );
+968    }
+969
+970    // Acknowledgment of a packet marks prior loss bursts with the same packet size as
+971    // non-suspicious
+972    #[test]
+973    fn retroactively_non_suspicious() {
+974        let mut bhd = BlackHoleDetector::new(1200);
+975        for i in 0..BLACK_HOLE_THRESHOLD {
+976            bhd.on_non_probe_lost(i as u64 * 2, 1400);
+977        }
+978        bhd.on_non_probe_acked(BLACK_HOLE_THRESHOLD as u64 * 2, 1400);
+979        bhd.on_non_probe_lost(BLACK_HOLE_THRESHOLD as u64 * 2 + 1, 1400);
+980        assert!(!bhd.black_hole_detected());
+981    }
+982}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/pacing.rs.html b/pr/672/docs/src/noq_proto/connection/pacing.rs.html new file mode 100644 index 000000000..f3253fa9b --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/pacing.rs.html @@ -0,0 +1,444 @@ +pacing.rs - source

noq_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    max_bytes_per_second: Option<u64>,
+22    prev: Instant,
+23}
+24
+25impl Pacer {
+26    /// Obtains a new [`Pacer`].
+27    pub(super) fn new(
+28        smoothed_rtt: Duration,
+29        window: u64,
+30        mtu: u16,
+31        max_bytes_per_second: Option<u64>,
+32        now: Instant,
+33    ) -> Self {
+34        let window = rate_limited_window(smoothed_rtt, window, max_bytes_per_second);
+35        let capacity = optimal_capacity(smoothed_rtt, window, mtu);
+36        Self {
+37            capacity,
+38            last_window: window,
+39            last_mtu: mtu,
+40            tokens: capacity,
+41            max_bytes_per_second,
+42            prev: now,
+43        }
+44    }
+45
+46    /// Obtains the `max_bytes_per_second` used when this [`Pacer`] was constructed.
+47    pub(crate) fn max_bytes_per_second(&self) -> Option<u64> {
+48        self.max_bytes_per_second
+49    }
+50
+51    /// Record that a packet has been transmitted.
+52    pub(super) fn on_transmit(&mut self, packet_length: u16) {
+53        self.tokens = self.tokens.saturating_sub(packet_length.into())
+54    }
+55
+56    /// Return how long we need to wait before sending `bytes_to_send`.
+57    ///
+58    /// If we can send a packet right away, this returns `None`. Otherwise, returns
+59    /// `Some(d)`, where `d` is the duration after which this function should be called
+60    /// again.
+61    ///
+62    /// The 5/4 ratio used here comes from the suggestion that N = 1.25 in the draft IETF
+63    /// RFC for QUIC.
+64    ///
+65    /// `capacity` (bytes) and `pacing_rate` (bytes/s) are optional overrides supplied by
+66    /// the congestion controller (e.g. BBRv3's `send_quantum` / `pacing_rate`). They take
+67    /// precedence over the window-derived defaults, but are still subject to the static
+68    /// `max_bytes_per_second` cap configured at construction time.
+69    pub(super) fn delay(
+70        &mut self,
+71        smoothed_rtt: Duration,
+72        bytes_to_send: u64,
+73        mtu: u16,
+74        window: u64,
+75        now: Instant,
+76        capacity: Option<u64>,
+77        pacing_rate: Option<u64>,
+78    ) -> Option<Duration> {
+79        debug_assert_ne!(
+80            window, 0,
+81            "zero-sized congestion control window is nonsense"
+82        );
+83
+84        let window = rate_limited_window(smoothed_rtt, window, self.max_bytes_per_second);
+85        if window != self.last_window || mtu != self.last_mtu {
+86            self.capacity = optimal_capacity(smoothed_rtt, window, mtu);
+87
+88            // Clamp the tokens
+89            self.tokens = self.capacity.min(self.tokens);
+90            self.last_window = window;
+91            self.last_mtu = mtu;
+92        }
+93
+94        if let Some(capacity) = capacity {
+95            self.capacity = capacity;
+96            self.tokens = self.capacity.min(self.tokens);
+97        }
+98
+99        if let Some(pacing_rate) = pacing_rate
+100            && bytes_to_send > self.capacity
+101        {
+102            // Pace at the controller-supplied rate; cap the static rate-limit through the
+103            // `rate_limited_window` window above.
+104            let capped_bytes_to_send = bytes_to_send.max(self.capacity);
+105            let delay = Duration::from_secs_f64(capped_bytes_to_send as f64 / pacing_rate as f64);
+106            return Some(delay);
+107        }
+108
+109        // if we can already send a packet, there is no need for delay
+110        if self.tokens >= bytes_to_send {
+111            return None;
+112        }
+113
+114        // we disable pacing for extremely large windows
+115        if window > u64::from(u32::MAX) {
+116            return None;
+117        }
+118
+119        let window = window as u32;
+120
+121        let time_elapsed = now.checked_duration_since(self.prev).unwrap_or_else(|| {
+122            warn!("received a timestamp early than a previous recorded time, ignoring");
+123            Default::default()
+124        });
+125
+126        if smoothed_rtt.as_nanos() == 0 {
+127            return None;
+128        }
+129
+130        let elapsed_rtts = time_elapsed.as_secs_f64() / smoothed_rtt.as_secs_f64();
+131        let new_tokens = (window as f64 * 1.25 * elapsed_rtts).round() as u64;
+132        self.tokens = self.tokens.saturating_add(new_tokens).min(self.capacity);
+133
+134        // In the unlikely event that we're getting polled faster than tokens are generated, ensure
+135        // that `elapsed_rtts` can grow until we make progress.
+136        if new_tokens > 0 {
+137            self.prev = now;
+138        }
+139
+140        // if we can already send a packet, there is no need for delay
+141        if self.tokens >= bytes_to_send {
+142            return None;
+143        }
+144
+145        let unscaled_delay = smoothed_rtt
+146            .checked_mul((bytes_to_send.max(self.capacity) - self.tokens) as _)
+147            .unwrap_or(Duration::MAX)
+148            / window;
+149
+150        // divisions come before multiplications to prevent overflow
+151        // this is the time at which the pacing window becomes empty
+152        Some((unscaled_delay / 5) * 4)
+153    }
+154}
+155
+156/// Calculates a pacer capacity for a certain window and RTT
+157///
+158/// The goal is to emit a burst (of size `capacity`) in timer intervals
+159/// which compromise between
+160/// - ideally distributing datagrams over time
+161/// - constantly waking up the connection to produce additional datagrams
+162///
+163/// Too short burst intervals means we will never meet them since the timer
+164/// accuracy in user-space is not high enough. If we miss the interval by more
+165/// than 25%, we will lose that part of the congestion window since no additional
+166/// tokens for the extra-elapsed time can be stored.
+167///
+168/// Too long burst intervals make pacing less effective.
+169fn optimal_capacity(smoothed_rtt: Duration, window: u64, mtu: u16) -> u64 {
+170    let rtt = smoothed_rtt.as_nanos().max(1);
+171    let mtu = u64::from(mtu);
+172
+173    let target_capacity = ((window as u128 * TARGET_BURST_INTERVAL.as_nanos()) / rtt) as u64;
+174    // Never restrict capacity below one MTU.
+175    let max_capacity = Ord::max(
+176        ((window as u128 * MAX_BURST_INTERVAL.as_nanos()) / rtt) as u64,
+177        mtu,
+178    );
+179
+180    // Batch the greater of `TARGET_BURST_INTERVAL` or `MIN_BURST_SIZE` worth of traffic at a
+181    // time. To avoid inducing excessive latency, limit that result to at most `MAX_BURST_INTERVAL`
+182    // worth of traffic.
+183    Ord::min(
+184        max_capacity,
+185        target_capacity.clamp(MIN_BURST_SIZE * mtu, MAX_BURST_SIZE * mtu),
+186    )
+187}
+188
+189/// Clamps the window to limit the sending rate to `max_bytes_per_second`.
+190///
+191/// If `max_bytes_per_second` is `None`, the original window is returned.
+192fn rate_limited_window(
+193    smoothed_rtt: Duration,
+194    window: u64,
+195    max_bytes_per_second: Option<u64>,
+196) -> u64 {
+197    let Some(max_bytes_per_second) = max_bytes_per_second else {
+198        return window;
+199    };
+200
+201    let rate_window = max_bytes_per_second as f64 * smoothed_rtt.as_secs_f64();
+202
+203    // the pacer refills tokens at x1.25 speed, so we shrink the window to cancel out the speedup
+204    // (otherwise the actual sending rate could be higher than `max_bytes_per_second`)
+205    let adjusted_rate_window = (rate_window / 1.25).round();
+206
+207    Ord::min(window, Ord::max(adjusted_rate_window as u64, 1))
+208}
+209
+210/// Period of traffic to batch together on a reasonably fast connection
+211const TARGET_BURST_INTERVAL: Duration = Duration::from_millis(2);
+212
+213/// Maximum period of traffic to batch together on a slow connection
+214///
+215/// Takes precedence over [`MIN_BURST_SIZE`].
+216const MAX_BURST_INTERVAL: Duration = Duration::from_millis(10);
+217
+218/// Minimum number of datagrams to batch together, so long as we won't have to wait for more than
+219/// [`MAX_BURST_INTERVAL`]
+220const MIN_BURST_SIZE: u64 = 10;
+221
+222/// Creating 256 packets took 1ms in a benchmark, so larger bursts don't make sense.
+223const MAX_BURST_SIZE: u64 = 256;
+224
+225#[cfg(test)]
+226mod tests {
+227    use super::*;
+228
+229    #[test]
+230    fn does_not_panic_on_bad_instant() {
+231        let old_instant = Instant::now();
+232        let new_instant = old_instant + Duration::from_micros(15);
+233        let rtt = Duration::from_micros(400);
+234
+235        assert!(
+236            Pacer::new(rtt, 30000, 1500, None, new_instant)
+237                .delay(
+238                    Duration::from_micros(0),
+239                    0,
+240                    1500,
+241                    1,
+242                    old_instant,
+243                    None,
+244                    None
+245                )
+246                .is_none()
+247        );
+248        assert!(
+249            Pacer::new(rtt, 30000, 1500, None, new_instant)
+250                .delay(
+251                    Duration::from_micros(0),
+252                    1600,
+253                    1500,
+254                    1,
+255                    old_instant,
+256                    None,
+257                    None
+258                )
+259                .is_none()
+260        );
+261        assert!(
+262            Pacer::new(rtt, 30000, 1500, None, new_instant)
+263                .delay(
+264                    Duration::from_micros(0),
+265                    1500,
+266                    1500,
+267                    3000,
+268                    old_instant,
+269                    None,
+270                    None
+271                )
+272                .is_none()
+273        );
+274    }
+275
+276    #[test]
+277    fn derives_initial_capacity() {
+278        let window = 2_000_000;
+279        let mtu = 1500;
+280        let rtt = Duration::from_millis(50);
+281        let now = Instant::now();
+282
+283        let pacer = Pacer::new(rtt, window, mtu, None, now);
+284        assert_eq!(
+285            pacer.capacity,
+286            (window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+287        );
+288        assert_eq!(pacer.tokens, pacer.capacity);
+289
+290        let pacer = Pacer::new(Duration::from_millis(0), window, mtu, None, now);
+291        assert_eq!(pacer.capacity, MAX_BURST_SIZE * mtu as u64);
+292        assert_eq!(pacer.tokens, pacer.capacity);
+293
+294        let pacer = Pacer::new(rtt, 1, mtu, None, now);
+295        assert_eq!(pacer.capacity, mtu as u64);
+296        assert_eq!(pacer.tokens, pacer.capacity);
+297    }
+298
+299    #[test]
+300    fn adjusts_capacity() {
+301        let window = 2_000_000;
+302        let mtu = 1500;
+303        let rtt = Duration::from_millis(50);
+304        let now = Instant::now();
+305
+306        let mut pacer = Pacer::new(rtt, window, mtu, None, now);
+307        assert_eq!(
+308            pacer.capacity,
+309            (window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+310        );
+311        assert_eq!(pacer.tokens, pacer.capacity);
+312        let initial_tokens = pacer.tokens;
+313
+314        pacer.delay(rtt, mtu as u64, mtu, window * 2, now, None, None);
+315        assert_eq!(
+316            pacer.capacity,
+317            (2 * window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+318        );
+319        assert_eq!(pacer.tokens, initial_tokens);
+320
+321        pacer.delay(rtt, mtu as u64, mtu, window / 2, now, None, None);
+322        assert_eq!(
+323            pacer.capacity,
+324            (window as u128 / 2 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+325        );
+326        assert_eq!(pacer.tokens, initial_tokens / 2);
+327
+328        pacer.delay(rtt, mtu as u64, mtu * 2, window, now, None, None);
+329        assert_eq!(
+330            pacer.capacity,
+331            (window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+332        );
+333
+334        pacer.delay(rtt, mtu as u64, 20_000, window, now, None, None);
+335        assert_eq!(pacer.capacity, 20_000_u64 * MIN_BURST_SIZE);
+336    }
+337
+338    #[test]
+339    fn computes_pause_correctly() {
+340        let window = 2_000_000u64;
+341        let mtu = 1000;
+342        let rtt = Duration::from_millis(50);
+343        let old_instant = Instant::now();
+344
+345        let mut pacer = Pacer::new(rtt, window, mtu, None, old_instant);
+346        let packet_capacity = pacer.capacity / mtu as u64;
+347
+348        for _ in 0..packet_capacity {
+349            assert_eq!(
+350                pacer.delay(rtt, mtu as u64, mtu, window, old_instant, None, None),
+351                None,
+352                "When capacity is available packets should be sent immediately"
+353            );
+354
+355            pacer.on_transmit(mtu);
+356        }
+357
+358        let pace_duration = Duration::from_nanos((TARGET_BURST_INTERVAL.as_nanos() * 4 / 5) as u64);
+359
+360        let actual_delay = pacer
+361            .delay(rtt, mtu as u64, mtu, window, old_instant, None, None)
+362            .expect("Send must be delayed");
+363
+364        let diff = actual_delay.abs_diff(pace_duration);
+365
+366        // Allow up to 2ns difference due to rounding
+367        assert!(
+368            diff < Duration::from_nanos(2),
+369            "expected ≈ {pace_duration:?}, got {actual_delay:?} (diff {diff:?})"
+370        );
+371        // Refill half of the tokens
+372        assert_eq!(
+373            pacer.delay(
+374                rtt,
+375                mtu as u64,
+376                mtu,
+377                window,
+378                old_instant + pace_duration / 2,
+379                None,
+380                None,
+381            ),
+382            None
+383        );
+384        assert_eq!(pacer.tokens, pacer.capacity / 2);
+385
+386        for _ in 0..packet_capacity / 2 {
+387            assert_eq!(
+388                pacer.delay(rtt, mtu as u64, mtu, window, old_instant, None, None),
+389                None,
+390                "When capacity is available packets should be sent immediately"
+391            );
+392
+393            pacer.on_transmit(mtu);
+394        }
+395
+396        // Refill all capacity by waiting more than the expected duration
+397        assert_eq!(
+398            pacer.delay(
+399                rtt,
+400                mtu as u64,
+401                mtu,
+402                window,
+403                old_instant + pace_duration * 3 / 2,
+404                None,
+405                None,
+406            ),
+407            None
+408        );
+409        assert_eq!(pacer.tokens, pacer.capacity);
+410    }
+411
+412    #[test]
+413    fn computes_pause_correctly_for_rate_limited() {
+414        let window = 2_000_000u64;
+415        let mtu = 1000;
+416        let rtt = Duration::from_millis(50);
+417        let old_instant = Instant::now();
+418
+419        let mut pacer = Pacer::new(rtt, window, mtu, Some(2_000), old_instant);
+420        assert_eq!(
+421            pacer.delay(rtt, 1_000, mtu, window, old_instant, None, None),
+422            None,
+423            "When capacity is available packets should be sent immediately"
+424        );
+425        pacer.on_transmit(mtu);
+426
+427        let actual_delay = pacer
+428            .delay(rtt, 1_000, mtu, window, old_instant, None, None)
+429            .expect("Send must be delayed");
+430
+431        let expected_delay = Duration::from_millis(500);
+432        let diff = actual_delay.abs_diff(expected_delay);
+433
+434        // Allow up to 2ns difference due to rounding
+435        assert!(
+436            diff < Duration::from_nanos(2),
+437            "expected ≈ {expected_delay:?}, got {actual_delay:?} (diff {diff:?})"
+438        );
+439
+440        // Should be able to send after a while
+441        let now = old_instant + expected_delay / 2;
+442        assert_eq!(pacer.delay(rtt, 500, mtu, window, now, None, None), None);
+443    }
+444}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/packet_builder.rs.html b/pr/672/docs/src/noq_proto/connection/packet_builder.rs.html new file mode 100644 index 000000000..05213bfaa --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/packet_builder.rs.html @@ -0,0 +1,432 @@ +packet_builder.rs - source

noq_proto/connection/
packet_builder.rs

1use bytes::{BufMut, Bytes};
+2use rand::RngExt;
+3use tracing::{debug, trace, trace_span};
+4
+5use super::{Connection, PathId, SentFrames, TransmitBuf, spaces::SentPacket};
+6use crate::{
+7    ConnectionId, FrameStats, Instant, MIN_INITIAL_SIZE, TransportError,
+8    coding::Encodable,
+9    connection::{ConnectionSide, EncryptionLevel, qlog::QlogSentPacket, spaces::Retransmits},
+10    frame::EncodableFrame,
+11    packet::{FIXED_BIT, Header, InitialHeader, LongType, PacketNumber, PartialEncode, SpaceId},
+12};
+13
+14/// QUIC packet builder
+15///
+16/// This allows building QUIC packets: it takes care of writing the header, allows writing
+17/// frames and on [`PacketBuilder::finish`] (or [`PacketBuilder::finish_and_track`]) it
+18/// encrypts the packet so it is ready to be sent on the wire.
+19///
+20/// The builder manages the write buffer into which the packet is written, and directly
+21/// implements [`BufMut`] to write frames into the packet.
+22pub(super) struct PacketBuilder<'a, 'b> {
+23    pub(super) buf: &'a mut TransmitBuf<'b>,
+24    pub(super) space: SpaceId,
+25    path: PathId,
+26    pub(super) partial_encode: PartialEncode,
+27    pub(super) ack_eliciting: bool,
+28    pub(super) packet_number: u64,
+29    /// Is this packet allowed to be coalesced?
+30    pub(super) can_coalesce: bool,
+31    /// Smallest absolute position in the associated buffer that must be occupied by this packet's
+32    /// frames
+33    pub(super) min_size: usize,
+34    pub(super) tag_len: usize,
+35    level: EncryptionLevel,
+36    pub(super) _span: tracing::span::EnteredSpan,
+37    qlog: QlogSentPacket,
+38    sent_frames: SentFrames,
+39}
+40
+41impl<'a, 'b> PacketBuilder<'a, 'b> {
+42    /// Write a new packet header to `buffer` and determine the packet's properties
+43    ///
+44    /// Marks the connection drained and returns `None` if the confidentiality limit would be
+45    /// violated.
+46    pub(super) fn new(
+47        now: Instant,
+48        space_id: SpaceId,
+49        path_id: PathId,
+50        dst_cid: ConnectionId,
+51        buffer: &'a mut TransmitBuf<'b>,
+52        conn: &mut Connection,
+53    ) -> Option<Self>
+54    where
+55        'b: 'a,
+56    {
+57        let mut qlog = QlogSentPacket::default();
+58
+59        let version = conn.version;
+60        // Initiate key update if we're approaching the confidentiality limit
+61        let (_header_crypto, _packet_crypto, level) = conn
+62            .crypto_state
+63            .encryption_keys(space_id.kind(), conn.side.side())
+64            .expect("tried to build packet without encryption keys");
+65
+66        let remaining_packet_budget = conn
+67            .crypto_state
+68            .remaining_packet_budget(level)
+69            .expect("keys are installed");
+70
+71        if level == EncryptionLevel::OneRtt {
+72            // 1-RTT keys can be rotated, so exhaustion just triggers a key update
+73            if remaining_packet_budget == 0 {
+74                debug!("routine key update due to phase exhaustion");
+75                conn.force_key_update();
+76            }
+77        } else {
+78            // Other encryption levels have a fixed key budget
+79            if remaining_packet_budget == 0 {
+80                let close = TransportError::AEAD_LIMIT_REACHED("confidentiality limit reached");
+81                conn.kill(close.into());
+82                return None;
+83            } else if remaining_packet_budget == 1 {
+84                let close = TransportError::AEAD_LIMIT_REACHED("confidentiality limit reached");
+85                conn.close_inner(now, close.into());
+86            }
+87        }
+88
+89        let (header_crypto, packet_crypto, level) = conn
+90            .crypto_state
+91            .encryption_keys(space_id.kind(), conn.side.side())
+92            .expect("verified");
+93
+94        let space = &mut conn.spaces[space_id];
+95        let packet_number = space.for_path(path_id).get_tx_number(&mut conn.rng);
+96        let span = trace_span!("send", space = ?space_id, pn = packet_number, %path_id).entered();
+97
+98        let number = PacketNumber::new(
+99            packet_number,
+100            space.for_path(path_id).largest_acked_packet_pn.unwrap_or(0),
+101        );
+102        let header = match level {
+103            EncryptionLevel::OneRtt => Header::Short {
+104                dst_cid,
+105                number,
+106                spin: if conn.spin_enabled {
+107                    conn.spin
+108                } else {
+109                    conn.rng.random()
+110                },
+111                key_phase: conn.crypto_state.key_phase,
+112            },
+113            EncryptionLevel::ZeroRtt => Header::Long {
+114                ty: LongType::ZeroRtt,
+115                src_cid: conn.handshake_cid,
+116                dst_cid,
+117                number,
+118                version,
+119            },
+120            EncryptionLevel::Handshake => Header::Long {
+121                ty: LongType::Handshake,
+122                src_cid: conn.handshake_cid,
+123                dst_cid,
+124                number,
+125                version,
+126            },
+127            EncryptionLevel::Initial => Header::Initial(InitialHeader {
+128                src_cid: conn.handshake_cid,
+129                dst_cid,
+130                token: match &conn.side {
+131                    ConnectionSide::Client { token, .. } => token.clone(),
+132                    ConnectionSide::Server { .. } => Bytes::new(),
+133                },
+134                number,
+135                version,
+136            }),
+137        };
+138
+139        let partial_encode = header.encode(buffer);
+140        if conn.peer_params.grease_quic_bit && conn.rng.random() {
+141            buffer.as_mut_slice()[partial_encode.start] ^= FIXED_BIT;
+142        }
+143
+144        let (sample_size, tag_len) = (header_crypto.sample_size(), packet_crypto.tag_len());
+145
+146        // Each packet must be large enough for header protection sampling, i.e. the combined
+147        // lengths of the encoded packet number and protected payload must be at least 4 bytes
+148        // longer than the sample required for header protection. Further, each packet should be at
+149        // least tag_len + 6 bytes larger than the destination CID on incoming packets so that the
+150        // peer may send stateless resets that are indistinguishable from regular traffic.
+151
+152        // pn_len + payload_len + tag_len >= sample_size + 4
+153        // payload_len >= sample_size + 4 - pn_len - tag_len
+154        let min_size = Ord::max(
+155            buffer.len() + (sample_size + 4).saturating_sub(number.len() + tag_len),
+156            partial_encode.start + dst_cid.len() + 6,
+157        );
+158        let max_size = buffer.datagram_max_offset() - tag_len;
+159        debug_assert!(max_size >= min_size);
+160
+161        qlog.header(&header, Some(packet_number), level, path_id);
+162
+163        Some(Self {
+164            buf: buffer,
+165            space: space_id,
+166            path: path_id,
+167            partial_encode,
+168            packet_number,
+169            can_coalesce: header.can_coalesce(),
+170            min_size,
+171            tag_len,
+172            level,
+173            ack_eliciting: false,
+174            qlog,
+175            sent_frames: SentFrames::default(),
+176            _span: span,
+177        })
+178    }
+179
+180    #[cfg(test)]
+181    pub(crate) fn simple_data_buf(buf: &'a mut TransmitBuf<'b>) -> Self {
+182        Self {
+183            buf,
+184            space: SpaceId::Data,
+185            path: PathId::ZERO,
+186            partial_encode: PartialEncode::no_header(),
+187            ack_eliciting: true,
+188            packet_number: 0,
+189            can_coalesce: true,
+190            min_size: 0,
+191            tag_len: 0,
+192            _span: trace_span!("test").entered(),
+193            qlog: QlogSentPacket::default(),
+194            sent_frames: SentFrames::default(),
+195            level: EncryptionLevel::Initial,
+196        }
+197    }
+198
+199    /// Append the minimum amount of padding to the packet such that, after encryption, the
+200    /// enclosing datagram will occupy at least `min_size` bytes
+201    pub(super) fn pad_to(&mut self, min_size: u16) {
+202        // The datagram might already have a larger minimum size than the caller is requesting, if
+203        // e.g. we're coalescing packets and have populated more than `min_size` bytes with packets
+204        // already.
+205        self.min_size = Ord::max(
+206            self.min_size,
+207            self.buf.datagram_start_offset() + (min_size as usize) - self.tag_len,
+208        );
+209    }
+210
+211    /// Writes a frame into the underlying buffer.
+212    ///
+213    /// It will also:
+214    /// - Track the frame so that it's registered with the path once [`Self::finish_and_track`] is
+215    ///   called.
+216    /// - Register the sent frame with the given [`FrameStats`].
+217    /// - If the qlog feature is enabled, register the frame.
+218    /// - Log the frame.
+219    pub(super) fn write_frame<'c>(
+220        &mut self,
+221        frame: impl Into<EncodableFrame<'c>>,
+222        stats: &mut FrameStats,
+223    ) {
+224        self.write_frame_with_log_msg(frame, stats, None);
+225    }
+226
+227    /// Writes a frame into the underlying buffer.
+228    ///
+229    /// It will also:
+230    /// - Track the frame so that it's registered with the path once [`Self::finish_and_track`] is
+231    ///   called.
+232    /// - Register the sent frame with the given [`FrameStats`].
+233    /// - If the qlog feature is enabled, register the frame.
+234    /// - Log the frame. If a `msg` is given, this will be added to the log.
+235    pub(super) fn write_frame_with_log_msg<'c>(
+236        &mut self,
+237        frame: impl Into<EncodableFrame<'c>>,
+238        stats: &mut FrameStats,
+239        msg: Option<&'static str>,
+240    ) {
+241        let frame = frame.into();
+242        frame.encode(&mut self.frame_space_mut());
+243        self.ack_eliciting |= frame.is_ack_eliciting();
+244        stats.record(frame.get_type());
+245        self.qlog.record(&frame);
+246        match msg {
+247            Some(msg) => trace!(%frame, msg),
+248            None => trace!(%frame),
+249        }
+250        self.sent_frames.record_sent_frame(frame);
+251    }
+252
+253    /// Returns a writable buffer limited to the remaining frame space
+254    ///
+255    /// The [`BufMut::remaining_mut`] call on the returned buffer indicates the amount of
+256    /// space available to write QUIC frames into.
+257    // In rust 1.82 we can use `-> impl BufMut + use<'_, 'a, 'b>`
+258    fn frame_space_mut(&mut self) -> bytes::buf::Limit<&mut TransmitBuf<'b>> {
+259        self.buf.limit(self.frame_space_remaining())
+260    }
+261
+262    pub(super) fn sent_frames(&self) -> &SentFrames {
+263        &self.sent_frames
+264    }
+265
+266    pub(super) fn finish_and_track(
+267        mut self,
+268        now: Instant,
+269        conn: &mut Connection,
+270        path_id: PathId,
+271        pad_datagram: PadDatagram,
+272    ) {
+273        match pad_datagram {
+274            PadDatagram::No => (),
+275            PadDatagram::ToSize(size) => self.pad_to(size),
+276            PadDatagram::ToSegmentSize => self.pad_to(self.buf.segment_size() as u16),
+277            PadDatagram::ToMinMtu => self.pad_to(MIN_INITIAL_SIZE),
+278        }
+279        let ack_eliciting = self.ack_eliciting;
+280        let packet_number = self.packet_number;
+281        let space_id = self.space;
+282        let (size, padded, sent) = self.finish(conn, now);
+283
+284        let size = match padded || ack_eliciting {
+285            true => size as u16,
+286            false => 0,
+287        };
+288
+289        let packet = SentPacket {
+290            path_generation: conn.paths.get_mut(&path_id).unwrap().data.generation(),
+291            largest_acked: sent.largest_acked,
+292            time_sent: now,
+293            size,
+294            ack_eliciting,
+295            retransmits: sent.retransmits,
+296            stream_frames: sent.stream_frames,
+297        };
+298
+299        conn.paths.get_mut(&path_id).unwrap().data.sent(
+300            packet_number,
+301            packet,
+302            conn.spaces[space_id].for_path(path_id),
+303        );
+304        conn.reset_keep_alive(path_id, now);
+305        if size != 0 {
+306            if ack_eliciting {
+307                conn.spaces[space_id]
+308                    .for_path(path_id)
+309                    .time_of_last_ack_eliciting_packet = Some(now);
+310                if conn.path_data(path_id).permit_idle_reset {
+311                    conn.reset_idle_timeout(now, space_id.kind(), path_id);
+312                }
+313                conn.path_data_mut(path_id).permit_idle_reset = false;
+314            }
+315            conn.set_loss_detection_timer(now, path_id);
+316            conn.path_data_mut(path_id).pacing.on_transmit(size);
+317        }
+318    }
+319
+320    /// Encrypt packet, returning the length of the packet and whether padding was added
+321    pub(super) fn finish(
+322        mut self,
+323        conn: &mut Connection,
+324        now: Instant,
+325    ) -> (usize, bool, SentFrames) {
+326        debug_assert!(
+327            self.buf.len() <= self.buf.datagram_max_offset() - self.tag_len,
+328            "packet exceeds maximum size"
+329        );
+330        let pad = self.buf.len() < self.min_size;
+331        if pad {
+332            let padding = self.min_size - self.buf.len();
+333            trace!("PADDING * {}", padding);
+334            self.buf.put_bytes(0, padding);
+335            self.qlog.frame_padding(padding);
+336        }
+337
+338        let (header_crypto, packet_crypto) = conn
+339            .crypto_state
+340            .local_crypto(self.level)
+341            .expect("tried to send packet without keys");
+342
+343        debug_assert_eq!(
+344            packet_crypto.tag_len(),
+345            self.tag_len,
+346            "Mismatching crypto tag len"
+347        );
+348
+349        self.buf.put_bytes(0, packet_crypto.tag_len());
+350        let encode_start = self.partial_encode.start;
+351        let packet_buf = &mut self.buf.as_mut_slice()[encode_start..];
+352        // for packet protection, PathId::ZERO and no path are equivalent.
+353        self.partial_encode.finish(
+354            packet_buf,
+355            header_crypto,
+356            Some((self.packet_number, self.path, packet_crypto)),
+357        );
+358
+359        conn.crypto_state.inc_sent_with_keys(self.level);
+360
+361        let packet_len = self.buf.len() - encode_start;
+362        trace!(size = %packet_len, "wrote packet");
+363        self.qlog.finalize(packet_len);
+364        conn.qlog.emit_packet_sent(self.qlog, now);
+365        (packet_len, pad, self.sent_frames)
+366    }
+367
+368    /// The number of additional bytes the current packet would take up if it was finished now
+369    ///
+370    /// This will include any padding which is required to make the size large enough to be
+371    /// encrypted correctly.
+372    pub(super) fn predict_packet_end(&self) -> usize {
+373        self.buf.len().max(self.min_size) + self.tag_len - self.buf.len()
+374    }
+375
+376    /// Returns the remaining space in the packet that can be taken up by QUIC frames
+377    ///
+378    /// This leaves space in the datagram for the cryptographic tag that needs to be written
+379    /// when the packet is finished.
+380    pub(super) fn frame_space_remaining(&self) -> usize {
+381        let max_offset = self.buf.datagram_max_offset() - self.tag_len;
+382        max_offset.saturating_sub(self.buf.len())
+383    }
+384
+385    pub(crate) fn require_padding(&mut self) {
+386        self.sent_frames.requires_padding = true;
+387    }
+388
+389    pub(crate) fn retransmits_mut(&mut self) -> &mut Retransmits {
+390        self.sent_frames.retransmits_mut()
+391    }
+392}
+393
+394#[derive(Debug, Copy, Clone)]
+395pub(super) enum PadDatagram {
+396    /// Do not pad the datagram
+397    No,
+398    /// To a specific size
+399    ToSize(u16),
+400    /// Pad to the current MTU/segment size
+401    ///
+402    /// For the first datagram in a transmit the MTU is the same as the
+403    /// [`TransmitBuf::segment_size`].
+404    ToSegmentSize,
+405    /// Pad to [`MIN_INITIAL_SIZE`], the minimal QUIC MTU of 1200 bytes
+406    ToMinMtu,
+407}
+408
+409impl std::ops::BitOrAssign for PadDatagram {
+410    fn bitor_assign(&mut self, rhs: Self) {
+411        *self = *self | rhs;
+412    }
+413}
+414
+415impl std::ops::BitOr for PadDatagram {
+416    type Output = Self;
+417
+418    fn bitor(self, rhs: Self) -> Self::Output {
+419        match (self, rhs) {
+420            (Self::No, rhs) => rhs,
+421            (Self::ToSize(size), Self::No) => Self::ToSize(size),
+422            (Self::ToSize(a), Self::ToSize(b)) => Self::ToSize(a.max(b)),
+423            (Self::ToSize(_), Self::ToSegmentSize) => Self::ToSegmentSize,
+424            (Self::ToSize(_), Self::ToMinMtu) => Self::ToMinMtu,
+425            (Self::ToSegmentSize, Self::No) => Self::ToSegmentSize,
+426            (Self::ToSegmentSize, Self::ToSize(_)) => Self::ToSegmentSize,
+427            (Self::ToSegmentSize, Self::ToSegmentSize) => Self::ToSegmentSize,
+428            (Self::ToSegmentSize, Self::ToMinMtu) => Self::ToMinMtu,
+429            (Self::ToMinMtu, _) => Self::ToMinMtu,
+430        }
+431    }
+432}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/packet_crypto.rs.html b/pr/672/docs/src/noq_proto/connection/packet_crypto.rs.html new file mode 100644 index 000000000..138025d42 --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/packet_crypto.rs.html @@ -0,0 +1,509 @@ +packet_crypto.rs - source

noq_proto/connection/
packet_crypto.rs

1use std::mem;
+2use std::ops::{Index, IndexMut};
+3
+4use tracing::{debug, trace};
+5
+6use super::SpaceKind;
+7use crate::connection::assembler::Assembler;
+8use crate::crypto::{self, HeaderKey, KeyPair, Keys, PacketKey};
+9use crate::packet::{Packet, PartialDecode};
+10use crate::token::ResetToken;
+11use rand::{CryptoRng, RngExt};
+12
+13use crate::{ConnectionId, Instant, Side};
+14use crate::{RESET_TOKEN_SIZE, TransportError};
+15
+16use super::PathId;
+17use super::spaces::PacketSpace;
+18
+19/// Perform key updates this many packets before the AEAD confidentiality limit.
+20///
+21/// Chosen arbitrarily, intended to be large enough to prevent spurious connection loss.
+22const KEY_UPDATE_MARGIN: u64 = 10_000;
+23
+24pub(super) struct UnprotectHeaderResult {
+25    /// The packet with the now unprotected header (`None` in the case of stateless reset packets
+26    /// that fail to be decoded)
+27    pub(super) packet: Option<Packet>,
+28    /// Whether the packet was a stateless reset packet
+29    pub(super) stateless_reset: bool,
+30}
+31
+32pub(super) struct DecryptPacketResult {
+33    /// The packet number
+34    pub(super) packet_number: u64,
+35    /// Whether a locally initiated key update has been acknowledged by the peer
+36    pub(super) outgoing_key_update_acked: bool,
+37    /// Whether the peer has initiated a key update
+38    pub(super) incoming_key_update: bool,
+39}
+40
+41pub(super) struct PrevCrypto {
+42    /// The keys used for the previous key phase, temporarily retained to decrypt packets sent by
+43    /// the peer prior to its own key update.
+44    pub(super) crypto: KeyPair<Box<dyn PacketKey>>,
+45    /// The incoming packet that ends the interval for which these keys are applicable, and the time
+46    /// of its receipt.
+47    ///
+48    /// Incoming packets should be decrypted using these keys iff this is `None` or their packet
+49    /// number is lower. `None` indicates that we have not yet received a packet using newer keys,
+50    /// which implies that the update was locally initiated.
+51    pub(super) end_packet: Option<(u64, Instant)>,
+52    /// Whether the following key phase is from a remotely initiated update that we haven't acked
+53    pub(super) update_unacked: bool,
+54}
+55
+56pub(super) struct ZeroRttCrypto {
+57    pub(super) header: Box<dyn HeaderKey>,
+58    pub(super) packet: Box<dyn PacketKey>,
+59}
+60
+61impl ZeroRttCrypto {
+62    fn keys(&self) -> (&dyn HeaderKey, &dyn PacketKey) {
+63        (self.header.as_ref(), self.packet.as_ref())
+64    }
+65}
+66
+67/// Consolidated crypto state for a connection.
+68///
+69/// This struct groups all cryptographic state together, including:
+70/// - The TLS session
+71/// - Per-space keys and crypto streams
+72/// - Key update state (prev/next keys)
+73/// - 0-RTT state
+74pub(super) struct CryptoState {
+75    /// Per encryption level crypto data (Initial, Handshake, Data).
+76    pub(super) spaces: [CryptoSpace; 3],
+77    /// The TLS session.
+78    pub(super) session: Box<dyn crypto::Session>,
+79
+80    /*
+81     * 0-RTT related fields
+82     */
+83    /// Whether 0-RTT was accepted.
+84    pub(super) accepted_0rtt: bool,
+85    /// Whether or not 0-RTT was enabled during the handshake. Does not imply acceptance.
+86    pub(super) zero_rtt_enabled: bool,
+87    /// 0-RTT crypto state, cleared when no longer needed.
+88    pub(super) zero_rtt_crypto: Option<ZeroRttCrypto>,
+89    /// Number of packets encrypted with 0-RTT keys. Client only.
+90    sent_with_zero_rtt: u64,
+91
+92    /*
+93     * State to manage 1-RTT key updates
+94     */
+95    /// 1-RTT keys to be used for the next key update.
+96    ///
+97    /// These are generated in advance to prevent timing attacks and/or DoS by third-party
+98    /// attackers spoofing key updates.
+99    pub(super) next_crypto: Option<KeyPair<Box<dyn PacketKey>>>,
+100    /// 1-RTT keys used prior to a key update.
+101    pub(super) prev_crypto: Option<PrevCrypto>,
+102    /// Current key phase, toggled on each 1-RTT key update.
+103    pub(super) key_phase: bool,
+104    /// How many packets are in the current key phase. Used only for `Data` space.
+105    pub(super) key_phase_size: u64,
+106}
+107
+108impl CryptoState {
+109    pub(super) fn new(
+110        session: Box<dyn crypto::Session>,
+111        init_cid: ConnectionId,
+112        side: Side,
+113        rng: &mut impl CryptoRng,
+114    ) -> Self {
+115        let initial_keys = session.initial_keys(init_cid, side);
+116        let initial_space = CryptoSpace {
+117            keys: Some(initial_keys),
+118            ..Default::default()
+119        };
+120        Self {
+121            spaces: [initial_space, Default::default(), Default::default()],
+122            session,
+123            next_crypto: None,
+124            prev_crypto: None,
+125            accepted_0rtt: false,
+126            zero_rtt_enabled: false,
+127            zero_rtt_crypto: None,
+128            sent_with_zero_rtt: 0,
+129            key_phase: false,
+130            // A small initial key phase size ensures peers that don't handle key updates correctly
+131            // fail sooner rather than later. It's okay for both peers to do this, as the first one
+132            // to perform an update will reset the other's key phase size in `update_keys`, and a
+133            // simultaneous key update by both is just like a regular key update with a really fast
+134            // response. Inspired by quic-go's similar behavior of performing the first key update
+135            // at the 100th short-header packet.
+136            key_phase_size: rng.random_range(10..1000),
+137        }
+138    }
+139
+140    /// Removes header protection of a packet, or returns `None` if the packet was dropped.
+141    pub(super) fn unprotect_header(
+142        &self,
+143        partial_decode: PartialDecode,
+144        stateless_reset_token: Option<ResetToken>,
+145    ) -> Option<UnprotectHeaderResult> {
+146        let encryption_level = partial_decode.encryption_level();
+147        let header_crypto = match encryption_level {
+148            Some(level) => match self.remote_crypto(level) {
+149                Some(crypto) => Some(crypto.0),
+150                None => {
+151                    let bytes = partial_decode.len();
+152                    debug!(?encryption_level, bytes, "dropping unexpected packet");
+153                    return None;
+154                }
+155            },
+156            // Unprotected packet
+157            None => None,
+158        };
+159
+160        let packet = partial_decode.data();
+161        let stateless_reset = packet.len() >= RESET_TOKEN_SIZE + 5
+162            && stateless_reset_token.as_deref() == Some(&packet[packet.len() - RESET_TOKEN_SIZE..]);
+163
+164        match partial_decode.finish(header_crypto) {
+165            Ok(packet) => Some(UnprotectHeaderResult {
+166                packet: Some(packet),
+167                stateless_reset,
+168            }),
+169            Err(_) if stateless_reset => Some(UnprotectHeaderResult {
+170                packet: None,
+171                stateless_reset: true,
+172            }),
+173            Err(e) => {
+174                trace!("unable to complete packet decoding: {}", e);
+175                None
+176            }
+177        }
+178    }
+179
+180    /// Decrypts a packet's body in-place.
+181    pub(super) fn decrypt_packet_body(
+182        &self,
+183        packet: &mut Packet,
+184        path_id: PathId,
+185        spaces: &[PacketSpace; 3],
+186    ) -> Result<Option<DecryptPacketResult>, Option<TransportError>> {
+187        let conn_key_phase = self.key_phase;
+188        if !packet.header.is_protected() {
+189            // Unprotected packets also don't have packet numbers
+190            return Ok(None);
+191        }
+192        let space = packet.header.space();
+193
+194        if path_id != PathId::ZERO && space != SpaceKind::Data {
+195            // do not try to decrypt illegal multipath packets
+196            return Err(Some(TransportError::PROTOCOL_VIOLATION(
+197                "multipath packet on non Data packet number space",
+198            )));
+199        }
+200        // Packets that do not belong to known path ids are valid as long as they can be decrypted.
+201        // If we didn't have a path, that's for the purposes of this function equivalent to not
+202        // having received packets on that path yet. So both of these cases are represented by `None`.
+203        let rx_packet_number = spaces[space]
+204            .path_space(path_id)
+205            .and_then(|s| s.largest_received_packet_number);
+206        let packet_number = packet
+207            .header
+208            .number()
+209            .ok_or(None)?
+210            .expand(rx_packet_number.map(|n| n + 1).unwrap_or_default());
+211        let packet_key_phase = packet.header.key_phase();
+212
+213        let mut crypto_update = false;
+214        let crypto = if packet.header.is_0rtt() {
+215            let (_, packet) = self.remote_crypto(EncryptionLevel::ZeroRtt).unwrap();
+216            packet
+217        } else if packet_key_phase == conn_key_phase || space != SpaceKind::Data {
+218            let (_, packet) = self.remote_crypto(space.encryption_level()).unwrap();
+219            packet
+220        } else if let Some(prev) = self.prev_crypto.as_ref().and_then(|crypto| {
+221            // If this packet comes prior to acknowledgment of the key update by the peer,
+222            if crypto.end_packet.is_none_or(|(pn, _)| packet_number < pn) {
+223                // use the previous keys.
+224                Some(crypto)
+225            } else {
+226                // Otherwise, this must be a remotely-initiated key update, so fall through to the
+227                // final case.
+228                None
+229            }
+230        }) {
+231            &*prev.crypto.remote
+232        } else {
+233            // We're in the Data space with a key phase mismatch and either there is no locally
+234            // initiated key update or the locally initiated key update was acknowledged by a
+235            // lower-numbered packet. The key phase mismatch must therefore represent a new
+236            // remotely-initiated key update.
+237            crypto_update = true;
+238            &*self.next_crypto.as_ref().unwrap().remote
+239        };
+240
+241        crypto
+242            .decrypt(
+243                path_id,
+244                packet_number,
+245                &packet.header_data,
+246                &mut packet.payload,
+247            )
+248            .map_err(|_| {
+249                trace!("decryption failed with packet number {}", packet_number);
+250                None
+251            })?;
+252
+253        if !packet.reserved_bits_valid() {
+254            return Err(Some(TransportError::PROTOCOL_VIOLATION(
+255                "reserved bits set",
+256            )));
+257        }
+258
+259        let mut outgoing_key_update_acked = false;
+260        if let Some(ref prev) = self.prev_crypto
+261            && prev.end_packet.is_none()
+262            && packet_key_phase == conn_key_phase
+263        {
+264            outgoing_key_update_acked = true;
+265        }
+266
+267        if crypto_update {
+268            // Validate incoming key update
+269            // If `rx_packet` is `None`, then either the path is entirely new, or we haven't received
+270            // any packets on this path yet. In that case, having the first packet be a crypto update
+271            // is fine.
+272            let invalid_packet_number =
+273                rx_packet_number.is_some_and(|rx_packet| packet_number <= rx_packet);
+274            if invalid_packet_number || self.prev_crypto.as_ref().is_some_and(|x| x.update_unacked)
+275            {
+276                trace!(?packet_number, ?rx_packet_number, %path_id, "crypto update failed");
+277                return Err(Some(TransportError::KEY_UPDATE_ERROR("")));
+278            }
+279        }
+280
+281        Ok(Some(DecryptPacketResult {
+282            packet_number,
+283            outgoing_key_update_acked,
+284            incoming_key_update: crypto_update,
+285        }))
+286    }
+287
+288    /// Check if keys are available for the given encryption level.
+289    pub(super) fn has_keys(&self, level: EncryptionLevel) -> bool {
+290        match level {
+291            EncryptionLevel::Initial => self.spaces[0].keys.is_some(),
+292            EncryptionLevel::ZeroRtt => self.zero_rtt_crypto.is_some(),
+293            EncryptionLevel::Handshake => self.spaces[1].keys.is_some(),
+294            EncryptionLevel::OneRtt => self.spaces[2].keys.is_some(),
+295        }
+296    }
+297
+298    /// Discard temporary key state (0-RTT and previous keys).
+299    pub(super) fn discard_temporary_keys(&mut self) {
+300        self.zero_rtt_crypto = None;
+301        self.prev_crypto = None;
+302    }
+303
+304    /// Enable 0-RTT crypto with the given keys.
+305    pub(super) fn enable_zero_rtt(
+306        &mut self,
+307        header: Box<dyn HeaderKey>,
+308        packet: Box<dyn PacketKey>,
+309    ) {
+310        self.zero_rtt_enabled = true;
+311        self.zero_rtt_crypto = Some(ZeroRttCrypto { header, packet });
+312    }
+313
+314    /// Discard 0-RTT crypto keys.
+315    pub(super) fn discard_zero_rtt(&mut self) {
+316        self.zero_rtt_crypto = None;
+317    }
+318
+319    /// Get the integrity limit for the given space's local packet keys.
+320    pub(super) fn integrity_limit(&self, space: SpaceKind) -> Option<u64> {
+321        let keys = self.spaces[space].keys.as_ref()?;
+322        Some(keys.packet.local.integrity_limit())
+323    }
+324
+325    /// Get local (sending) crypto keys for the given encryption level.
+326    ///
+327    /// Use this only when sure the keys are allowed to be used. [`Self::encryption_keys`] should
+328    /// be preferred otherwise.
+329    pub(super) fn local_crypto(
+330        &self,
+331        level: EncryptionLevel,
+332    ) -> Option<(&dyn HeaderKey, &dyn PacketKey)> {
+333        match level {
+334            EncryptionLevel::Initial => self.spaces[0].keys.as_ref().map(Keys::local),
+335            EncryptionLevel::Handshake => self.spaces[1].keys.as_ref().map(Keys::local),
+336            EncryptionLevel::OneRtt => self.spaces[2].keys.as_ref().map(Keys::local),
+337            // 0-RTT uses the same keys for both directions
+338            EncryptionLevel::ZeroRtt => self.zero_rtt_crypto.as_ref().map(ZeroRttCrypto::keys),
+339        }
+340    }
+341
+342    /// Get remote (receiving) crypto keys for the given encryption level.
+343    ///
+344    /// Returns header and packet keys used for decrypting incoming packets.
+345    fn remote_crypto(&self, level: EncryptionLevel) -> Option<(&dyn HeaderKey, &dyn PacketKey)> {
+346        match level {
+347            EncryptionLevel::Initial => self.spaces[0].keys.as_ref().map(Keys::remote),
+348            EncryptionLevel::Handshake => self.spaces[1].keys.as_ref().map(Keys::remote),
+349            EncryptionLevel::OneRtt => self.spaces[2].keys.as_ref().map(Keys::remote),
+350            // 0-RTT uses the same keys for both directions
+351            EncryptionLevel::ZeroRtt => self.zero_rtt_crypto.as_ref().map(ZeroRttCrypto::keys),
+352        }
+353    }
+354
+355    /// Get local (sending) crypto keys and the actual encryption level for a given space.
+356    ///
+357    /// This method takes a [`SpaceKind`] and resolves the encryption level automatically: for the
+358    /// [`SpaceKind::Data`] space on the client side, it falls back to 0-RTT keys when 1-RTT keys
+359    /// are not yet available. Resolving the appropriate encryption keys makes this method
+360    /// preferable to [`Self::local_crypto`] in general.
+361    ///
+362    /// Returns `None` if no keys are available.
+363    pub(super) fn encryption_keys(
+364        &self,
+365        kind: SpaceKind,
+366        side: Side,
+367    ) -> Option<(&dyn HeaderKey, &dyn PacketKey, EncryptionLevel)> {
+368        let mut keys = self.spaces[kind].keys.as_ref().map(Keys::local);
+369        let mut level = match kind {
+370            SpaceKind::Initial => EncryptionLevel::Initial,
+371            SpaceKind::Handshake => EncryptionLevel::Handshake,
+372            SpaceKind::Data => EncryptionLevel::OneRtt,
+373        };
+374
+375        // Clients use 0-RTT keys if 1-RTT keys are not available. Servers never encrypt 0-RTT
+376        if keys.is_none() && kind == SpaceKind::Data && side.is_client() {
+377            keys = self.zero_rtt_crypto.as_ref().map(ZeroRttCrypto::keys);
+378            level = EncryptionLevel::ZeroRtt;
+379        }
+380
+381        keys.map(|(header_keys, packet_keys)| (header_keys, packet_keys, level))
+382    }
+383
+384    /// Perform a 1-RTT key update.
+385    ///
+386    /// Generates the next set of keys, rotates current keys into previous, and installs the new
+387    /// keys. Updates `key_phase` and `key_phase_size` accordingly.
+388    ///
+389    /// PANICS: If 1-RTT keys are missing.
+390    pub(super) fn update_keys(&mut self, end_packet: Option<(u64, Instant)>, remote: bool) {
+391        trace!("executing key update");
+392
+393        let new = self
+394            .session
+395            .next_1rtt_keys()
+396            .expect("only called for `Data` packets");
+397        let confidentiality_limit = new.local.confidentiality_limit();
+398        let old = mem::replace(
+399            &mut self.spaces[SpaceKind::Data]
+400                .keys
+401                .as_mut()
+402                .unwrap() // safe because update_keys() can only be triggered by short packets
+403                .packet,
+404            mem::replace(self.next_crypto.as_mut().unwrap(), new),
+405        );
+406        self.prev_crypto = Some(PrevCrypto {
+407            crypto: old,
+408            end_packet,
+409            update_unacked: remote,
+410        });
+411
+412        self.key_phase_size = confidentiality_limit.saturating_sub(KEY_UPDATE_MARGIN);
+413        self.key_phase = !self.key_phase;
+414        self.spaces[2].sent_with_keys = 0;
+415    }
+416
+417    /// Number of packets encrypted with the current set of keys at `level`.
+418    ///
+419    /// For [`EncryptionLevel::OneRtt`], this counter resets to zero on every key update (see
+420    /// [`Self::update_keys`]).
+421    pub(crate) fn sent_with_keys(&self, level: EncryptionLevel) -> u64 {
+422        match level {
+423            EncryptionLevel::Initial => self.spaces[0].sent_with_keys,
+424            EncryptionLevel::ZeroRtt => self.sent_with_zero_rtt,
+425            EncryptionLevel::Handshake => self.spaces[1].sent_with_keys,
+426            EncryptionLevel::OneRtt => self.spaces[2].sent_with_keys,
+427        }
+428    }
+429
+430    /// Number of packets that may still be sent before the AEAD confidentiality limit is reached
+431    /// at the given encryption level.
+432    ///
+433    /// For [`EncryptionLevel::OneRtt`] the effective limit is the minimum of the AEAD
+434    /// confidentiality limit and the current key-phase size. For all other levels the raw AEAD
+435    /// confidentiality limit is used.
+436    ///
+437    /// Returns `None` when no keys are available for `level`.
+438    pub(crate) fn remaining_packet_budget(&self, level: EncryptionLevel) -> Option<u64> {
+439        let sent_with_keys = self.sent_with_keys(level);
+440        let (_header_keys, packet_keys) = self.local_crypto(level)?;
+441        let limit = match level {
+442            EncryptionLevel::OneRtt => self.key_phase_size.min(packet_keys.confidentiality_limit()),
+443            _ => packet_keys.confidentiality_limit(),
+444        };
+445
+446        Some(limit.saturating_sub(sent_with_keys))
+447    }
+448
+449    /// Record that a packet has been encrypted at the given level.
+450    pub(crate) fn inc_sent_with_keys(&mut self, level: EncryptionLevel) {
+451        let count = match level {
+452            EncryptionLevel::Initial => &mut self.spaces[0].sent_with_keys,
+453            EncryptionLevel::ZeroRtt => &mut self.sent_with_zero_rtt,
+454            EncryptionLevel::Handshake => &mut self.spaces[1].sent_with_keys,
+455            EncryptionLevel::OneRtt => &mut self.spaces[2].sent_with_keys,
+456        };
+457        *count = count.saturating_add(1u64);
+458    }
+459}
+460
+461/// Per space kind cryptographic state.
+462#[derive(Default)]
+463pub(super) struct CryptoSpace {
+464    /// Packet protection keys for this space.
+465    pub(super) keys: Option<Keys>,
+466    /// Incoming cryptographic handshake stream.
+467    pub(super) crypto_stream: Assembler,
+468    /// Current offset of outgoing cryptographic handshake stream.
+469    pub(super) crypto_offset: u64,
+470    /// Number of packets encrypted with the current set of keys.
+471    pub(super) sent_with_keys: u64,
+472}
+473
+474/// QUIC packet protection levels (RFC 9001).
+475#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+476pub(crate) enum EncryptionLevel {
+477    /// Initial packets (client and server).
+478    Initial,
+479    /// Early data (0-RTT), client only.
+480    ZeroRtt,
+481    /// Handshake packets.
+482    Handshake,
+483    /// Application data (1-RTT).
+484    OneRtt,
+485}
+486
+487impl From<SpaceKind> for crate::packet::SpaceId {
+488    fn from(kind: SpaceKind) -> Self {
+489        match kind {
+490            SpaceKind::Initial => Self::Initial,
+491            SpaceKind::Handshake => Self::Handshake,
+492            SpaceKind::Data => Self::Data,
+493        }
+494    }
+495}
+496
+497impl IndexMut<SpaceKind> for [CryptoSpace; 3] {
+498    fn index_mut(&mut self, index: SpaceKind) -> &mut Self::Output {
+499        &mut self[index as usize]
+500    }
+501}
+502
+503impl Index<SpaceKind> for [CryptoSpace; 3] {
+504    type Output = CryptoSpace;
+505
+506    fn index(&self, index: SpaceKind) -> &Self::Output {
+507        &self[index as usize]
+508    }
+509}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/paths.rs.html b/pr/672/docs/src/noq_proto/connection/paths.rs.html new file mode 100644 index 000000000..9bf534ede --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/paths.rs.html @@ -0,0 +1,1166 @@ +paths.rs - source

noq_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    PathStats, SpaceKind,
+9    mtud::MtuDiscovery,
+10    pacing::Pacer,
+11    spaces::{PacketNumberSpace, SentPacket},
+12};
+13use crate::{
+14    ConnectionId, Duration, FourTuple, Instant, TIMER_GRANULARITY, TransportConfig,
+15    TransportErrorCode, VarInt,
+16    coding::{self, Decodable, Encodable},
+17    congestion,
+18    frame::ObservedAddr,
+19};
+20
+21#[cfg(feature = "qlog")]
+22use qlog::events::quic::RecoveryMetricsUpdated;
+23
+24/// Id representing different paths when using multipath extension
+25#[cfg_attr(test, derive(test_strategy::Arbitrary))]
+26#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
+27pub struct PathId(pub(crate) u32);
+28
+29impl std::hash::Hash for PathId {
+30    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+31        state.write_u32(self.0);
+32    }
+33}
+34
+35impl Decodable for PathId {
+36    fn decode<B: bytes::Buf>(r: &mut B) -> coding::Result<Self> {
+37        let v = VarInt::decode(r)?;
+38        let v = u32::try_from(v.0).map_err(|_| coding::UnexpectedEnd)?;
+39        Ok(Self(v))
+40    }
+41}
+42
+43impl Encodable for PathId {
+44    fn encode<B: bytes::BufMut>(&self, w: &mut B) {
+45        VarInt(self.0.into()).encode(w)
+46    }
+47}
+48
+49impl PathId {
+50    /// The maximum path ID allowed.
+51    pub const MAX: Self = Self(u32::MAX);
+52
+53    /// The 0 path id.
+54    pub const ZERO: Self = Self(0);
+55
+56    /// The number of bytes this [`PathId`] uses when encoded as a [`VarInt`]
+57    pub(crate) const fn size(&self) -> usize {
+58        VarInt(self.0 as u64).size()
+59    }
+60
+61    /// Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead
+62    /// of overflowing.
+63    pub fn saturating_add(self, rhs: impl Into<Self>) -> Self {
+64        let rhs = rhs.into();
+65        let inner = self.0.saturating_add(rhs.0);
+66        Self(inner)
+67    }
+68
+69    /// Saturating integer subtraction. Computes self - rhs, saturating at the numeric bounds
+70    /// instead of overflowing.
+71    pub fn saturating_sub(self, rhs: impl Into<Self>) -> Self {
+72        let rhs = rhs.into();
+73        let inner = self.0.saturating_sub(rhs.0);
+74        Self(inner)
+75    }
+76
+77    /// Get the next [`PathId`]
+78    pub(crate) fn next(&self) -> Self {
+79        self.saturating_add(Self(1))
+80    }
+81
+82    /// Get the underlying u32
+83    pub(crate) fn as_u32(&self) -> u32 {
+84        self.0
+85    }
+86}
+87
+88impl std::fmt::Display for PathId {
+89    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+90        self.0.fmt(f)
+91    }
+92}
+93
+94impl<T: Into<u32>> From<T> for PathId {
+95    fn from(source: T) -> Self {
+96        Self(source.into())
+97    }
+98}
+99
+100/// State needed for a single path ID.
+101///
+102/// A single path ID can migrate according to the rules in RFC9000 §9, either voluntary or
+103/// involuntary. We need to keep the [`PathData`] of the previously used such path available
+104/// in order to defend against migration attacks (see RFC9000 §9.3.1, §9.3.2 and §9.3.3) as
+105/// well as to support path probing (RFC9000 §9.1).
+106#[derive(Debug)]
+107pub(super) struct PathState {
+108    pub(super) data: PathData,
+109    pub(super) prev: Option<(ConnectionId, PathData)>,
+110}
+111
+112impl PathState {
+113    /// Update counters to account for a packet becoming acknowledged, lost, or abandoned
+114    pub(super) fn remove_in_flight(&mut self, packet: &SentPacket) {
+115        // Visit known paths from newest to oldest to find the one `pn` was sent on
+116        for path_data in [&mut self.data]
+117            .into_iter()
+118            .chain(self.prev.as_mut().map(|(_, data)| data))
+119        {
+120            if path_data.remove_in_flight(packet) {
+121                return;
+122            }
+123        }
+124    }
+125}
+126
+127#[derive(Debug)]
+128pub(super) struct SentChallengeInfo {
+129    /// When was the challenge sent on the wire.
+130    pub(super) sent_instant: Instant,
+131    /// The 4-tuple on which this path challenge was sent.
+132    pub(super) network_path: FourTuple,
+133}
+134
+135/// State of particular network path 4-tuple within a [`PacketNumberSpace`].
+136///
+137/// With QUIC-Multipath a path is identified by a [`PathId`] and it is possible to have
+138/// multiple paths on the same 4-tuple. Furthermore a single QUIC-Multipath path can migrate
+139/// to a different 4-tuple, in a similar manner as an RFC9000 connection can use "path
+140/// migration" to move to a different 4-tuple. There are thus two states we keep for paths:
+141///
+142/// - [`PacketNumberSpace`]: The state for a single packet number space, i.e. [`PathId`],
+143///   which remains in place across path migrations to different 4-tuples.
+144///
+145///   This is stored in [`PacketSpace::number_spaces`] indexed on [`PathId`].
+146///
+147/// - [`PathData`]: The state we keep for each unique 4-tuple within a space. Of note is
+148///   that a single [`PathData`] can never belong to a different [`PacketNumberSpace`].
+149///
+150///   This is stored in [`Connection::paths`] indexed by the current [`PathId`] for which
+151///   space it exists. Either as the primary 4-tuple or as the previous 4-tuple just after a
+152///   migration.
+153///
+154/// It follows that there might be several [`PathData`] structs for the same 4-tuple if
+155/// several spaces are sharing the same 4-tuple. Note that during the handshake, the
+156/// Initial, Handshake and Data spaces for [`PathId::ZERO`] all share the same [`PathData`].
+157///
+158/// [`PacketSpace::number_spaces`]: super::spaces::PacketSpace::number_spaces
+159/// [`Connection::paths`]: super::Connection::paths
+160#[derive(Debug)]
+161pub(super) struct PathData {
+162    pub(super) network_path: FourTuple,
+163    pub(super) rtt: RttEstimator,
+164    /// Whether we're enabling ECN on outgoing packets
+165    pub(super) sending_ecn: bool,
+166    /// Congestion controller state
+167    pub(super) congestion: Box<dyn congestion::Controller>,
+168    /// Pacing state
+169    pub(super) pacing: Pacer,
+170    /// Whether the last `poll_transmit_on_path` call yielded no data because there was
+171    /// no outgoing application data.
+172    ///
+173    /// The RFC writes:
+174    /// > When bytes in flight is smaller than the congestion window and sending is not pacing limited,
+175    /// > the congestion window is underutilized. This can happen due to insufficient application data
+176    /// > or flow control limits. When this occurs, the congestion window SHOULD NOT be increased in
+177    /// > either slow start or congestion avoidance.
+178    ///
+179    /// (RFC9002, section 7.8)
+180    ///
+181    /// I.e. when app_limited is true, the congestion controller doesn't increase the congestion window.
+182    pub(super) app_limited: bool,
+183
+184    /// Path challenges sent (on the wire, on-path) that we didn't receive a path response for yet
+185    on_path_challenges_unconfirmed: IntMap<u64, SentChallengeInfo>,
+186    /// Whether to trigger sending another PATH_CHALLENGE in the next poll_transmit.
+187    ///
+188    /// This is picked up by [`super::Connection::space_can_send`].
+189    ///
+190    /// Only used for RFC9000-style path migration and multipath path validation (for opening).
+191    ///
+192    /// This is **not used** for n0 nat traversal challenge sending.
+193    pub(super) pending_on_path_challenge: bool,
+194    /// Pending responses to PATH_CHALLENGE frames
+195    pub(super) path_responses: PathResponses,
+196    /// Whether we're certain the peer can both send and receive on this address
+197    ///
+198    /// Initially equal to `use_stateless_retry` for servers, and becomes false again on every
+199    /// migration. Always true for clients.
+200    pub(super) validated: bool,
+201    /// Total size of all UDP datagrams sent on this path
+202    pub(super) total_sent: u64,
+203    /// Total size of all UDP datagrams received on this path
+204    pub(super) total_recvd: u64,
+205    /// The state of the MTU discovery process
+206    pub(super) mtud: MtuDiscovery,
+207    /// Packet number of the first packet sent after an RTT sample was collected on this path
+208    ///
+209    /// Used in persistent congestion determination.
+210    pub(super) first_packet_after_rtt_sample: Option<(SpaceKind, u64)>,
+211    /// The in-flight packets and bytes
+212    ///
+213    /// Note that this is across all spaces on this path
+214    pub(super) in_flight: InFlight,
+215    /// Whether this path has had it's remote address reported back to the peer. This only happens
+216    /// if both peers agree to so based on their transport parameters.
+217    pub(super) observed_addr_sent: bool,
+218    /// Observed address frame with the largest sequence number received from the peer on this path.
+219    pub(super) last_observed_addr_report: Option<ObservedAddr>,
+220    /// The QUIC-MULTIPATH path status
+221    pub(super) status: PathStatusState,
+222    /// Number of the first packet sent on this path
+223    ///
+224    /// With RFC9000 §9 style migration (i.e. not multipath) the PathId does not change and
+225    /// hence packet numbers continue. This is used to determine whether a packet was sent
+226    /// on such an earlier path. Insufficient to determine if a packet was sent on a later
+227    /// path.
+228    first_packet: Option<u64>,
+229    /// The number of times a tail-loss probe has been sent without receiving an ack.
+230    ///
+231    /// This is incremented by one every time the [`LossDetection`] timer fires because a
+232    /// tail-loss probe needs to be sent. Once an acknowledgement for a packet is received
+233    /// again it is reset to 0. Used to compute the PTO duration.
+234    ///
+235    /// [`LossDetection`]: super::timer::PathTimer::LossDetection
+236    pub(super) pto_count: u32,
+237
+238    //
+239    // Per-path idle & keep alive
+240    //
+241    /// Idle timeout for the path
+242    ///
+243    /// If expired, the path will be abandoned.  This is different from the connection-wide
+244    /// idle timeout which closes the connection if expired.
+245    pub(super) idle_timeout: Option<Duration>,
+246    /// Keep alives to send on this path
+247    ///
+248    /// There is also a connection-level keep alive configured in the
+249    /// [`TransportParameters`].  This triggers activity on any path which can keep the
+250    /// connection alive.
+251    ///
+252    /// [`TransportParameters`]: crate::transport_parameters::TransportParameters
+253    pub(super) keep_alive: Option<Duration>,
+254    /// Whether to reset the idle timer when the next ack-eliciting packet is sent.
+255    ///
+256    /// Whenever we receive an authenticated packet the connection and path idle timers are
+257    /// reset if a maximum idle timeout was negotiated. However on the first ack-eliciting
+258    /// packet *sent* after this the idle timer also needs to be reset to avoid the idle
+259    /// timer firing while the sent packet is in-fight. See
+260    /// <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.1>.
+261    pub(super) permit_idle_reset: bool,
+262
+263    /// Whether the path has already been considered opened from an application perspective.
+264    ///
+265    /// This means, for paths other than the original [`PathId::ZERO`], a first path challenge has
+266    /// been responded to, regardless of the initial validation status of the path. This state is
+267    /// irreversible, since it's not affected by the path being closed.
+268    ///
+269    /// Sending a PATH_CHALLENGE and receiving a valid response before the application is informed
+270    /// of the path, is a way to ensure the path is usable before it is reported. This is not
+271    /// required by the spec, and in the future might be changed for simply requiring a first ack'd
+272    /// packet.
+273    pub(super) open_status: OpenStatus,
+274
+275    /// Whether we're currently draining the path after having abandoned it.
+276    ///
+277    /// This should only be true when a path discard timer is armed, and after the path was
+278    /// abandoned (and added to the abandoned_paths set).
+279    ///
+280    /// This will only ever be set from false to true.
+281    pub(super) draining: bool,
+282
+283    /// Snapshot of the qlog recovery metrics
+284    #[cfg(feature = "qlog")]
+285    recovery_metrics: RecoveryMetrics,
+286
+287    /// Tag uniquely identifying a path in a connection.
+288    ///
+289    /// When a migration happens on the same [`PathId`] we still detect a change in the
+290    /// 4-tuple and generate a new [`PathData`] for it. Each such generation has a unique
+291    /// value to keep track of which 4-tuple a packet belonged to.
+292    generation: u64,
+293}
+294
+295impl PathData {
+296    pub(super) fn new(
+297        network_path: FourTuple,
+298        allow_mtud: bool,
+299        peer_max_udp_payload_size: Option<u16>,
+300        generation: u64,
+301        now: Instant,
+302        config: &TransportConfig,
+303    ) -> Self {
+304        let congestion = config
+305            .congestion_controller_factory
+306            .clone()
+307            .build(now, config.get_initial_mtu());
+308        Self {
+309            network_path,
+310            rtt: RttEstimator::new(config.initial_rtt),
+311            sending_ecn: true,
+312            pacing: Pacer::new(
+313                config.initial_rtt,
+314                congestion.initial_window(),
+315                config.get_initial_mtu(),
+316                config.max_outgoing_bytes_per_second,
+317                now,
+318            ),
+319            congestion,
+320            app_limited: false,
+321            on_path_challenges_unconfirmed: Default::default(),
+322            pending_on_path_challenge: false,
+323            path_responses: PathResponses::default(),
+324            validated: false,
+325            total_sent: 0,
+326            total_recvd: 0,
+327            mtud: config
+328                .mtu_discovery_config
+329                .as_ref()
+330                .filter(|_| allow_mtud)
+331                .map_or_else(
+332                    || MtuDiscovery::disabled(config.get_initial_mtu(), config.min_mtu),
+333                    |mtud_config| {
+334                        MtuDiscovery::new(
+335                            config.get_initial_mtu(),
+336                            config.min_mtu,
+337                            peer_max_udp_payload_size,
+338                            mtud_config.clone(),
+339                        )
+340                    },
+341                ),
+342            first_packet_after_rtt_sample: None,
+343            in_flight: InFlight::new(),
+344            observed_addr_sent: false,
+345            last_observed_addr_report: None,
+346            status: Default::default(),
+347            first_packet: None,
+348            pto_count: 0,
+349            idle_timeout: config.default_path_max_idle_timeout,
+350            keep_alive: config.default_path_keep_alive_interval,
+351            permit_idle_reset: true,
+352            open_status: OpenStatus::default(),
+353            draining: false,
+354            #[cfg(feature = "qlog")]
+355            recovery_metrics: RecoveryMetrics::default(),
+356            generation,
+357        }
+358    }
+359
+360    /// Create a new path from a previous one.
+361    ///
+362    /// This should only be called when migrating paths.
+363    pub(super) fn from_previous(
+364        network_path: FourTuple,
+365        prev: &Self,
+366        generation: u64,
+367        now: Instant,
+368    ) -> Self {
+369        let congestion = prev.congestion.clone_box();
+370        let smoothed_rtt = prev.rtt.get();
+371        Self {
+372            network_path,
+373            rtt: prev.rtt,
+374            pacing: Pacer::new(
+375                smoothed_rtt,
+376                congestion.window(),
+377                prev.current_mtu(),
+378                prev.pacing.max_bytes_per_second(),
+379                now,
+380            ),
+381            sending_ecn: true,
+382            congestion,
+383            app_limited: false,
+384            on_path_challenges_unconfirmed: Default::default(),
+385            pending_on_path_challenge: false,
+386            path_responses: PathResponses::default(),
+387            validated: false,
+388            total_sent: 0,
+389            total_recvd: 0,
+390            mtud: prev.mtud.clone(),
+391            first_packet_after_rtt_sample: prev.first_packet_after_rtt_sample,
+392            in_flight: InFlight::new(),
+393            observed_addr_sent: false,
+394            last_observed_addr_report: None,
+395            status: prev.status.clone(),
+396            first_packet: None,
+397            pto_count: 0,
+398            idle_timeout: prev.idle_timeout,
+399            keep_alive: prev.keep_alive,
+400            permit_idle_reset: true,
+401            open_status: OpenStatus::default(),
+402            draining: false,
+403            #[cfg(feature = "qlog")]
+404            recovery_metrics: prev.recovery_metrics.clone(),
+405            generation,
+406        }
+407    }
+408
+409    /// Whether we're in the process of validating this path with PATH_CHALLENGEs
+410    pub(super) fn is_validating_path(&self) -> bool {
+411        !self.on_path_challenges_unconfirmed.is_empty() || self.pending_on_path_challenge
+412    }
+413
+414    /// Indicates whether we're a server that hasn't validated the peer's address and hasn't
+415    /// received enough data from the peer to permit sending `bytes_to_send` additional bytes
+416    pub(super) fn anti_amplification_blocked(&self, bytes_to_send: u64) -> bool {
+417        !self.validated && self.total_recvd * 3 < self.total_sent + bytes_to_send
+418    }
+419
+420    /// Returns the path's current MTU
+421    pub(super) fn current_mtu(&self) -> u16 {
+422        self.mtud.current_mtu()
+423    }
+424
+425    /// Account for transmission of `packet` with number `pn` in `space`
+426    pub(super) fn sent(&mut self, pn: u64, packet: SentPacket, space: &mut PacketNumberSpace) {
+427        self.in_flight.insert(&packet);
+428        if self.first_packet.is_none() {
+429            self.first_packet = Some(pn);
+430        }
+431        if let Some(forgotten) = space.sent(pn, packet) {
+432            self.remove_in_flight(&forgotten);
+433        }
+434    }
+435
+436    pub(super) fn record_path_challenge_sent(
+437        &mut self,
+438        now: Instant,
+439        token: u64,
+440        network_path: FourTuple,
+441    ) {
+442        let info = SentChallengeInfo {
+443            sent_instant: now,
+444            network_path,
+445        };
+446        debug_assert_eq!(network_path, self.network_path);
+447        self.on_path_challenges_unconfirmed.insert(token, info);
+448    }
+449
+450    /// Remove `packet` with number `pn` from this path's congestion control counters, or return
+451    /// `false` if `pn` was sent before this path was established.
+452    pub(super) fn remove_in_flight(&mut self, packet: &SentPacket) -> bool {
+453        if packet.path_generation != self.generation {
+454            return false;
+455        }
+456        self.in_flight.remove(packet);
+457        true
+458    }
+459
+460    /// Increment the total size of sent UDP datagrams
+461    pub(super) fn inc_total_sent(&mut self, inc: u64) {
+462        self.total_sent = self.total_sent.saturating_add(inc);
+463        if !self.validated {
+464            trace!(
+465                network_path = %self.network_path,
+466                anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent),
+467                "anti amplification budget decreased"
+468            );
+469        }
+470    }
+471
+472    /// Increment the total size of received UDP datagrams
+473    pub(super) fn inc_total_recvd(&mut self, inc: u64) {
+474        self.total_recvd = self.total_recvd.saturating_add(inc);
+475        if !self.validated {
+476            trace!(
+477                network_path = %self.network_path,
+478                anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent),
+479                "anti amplification budget increased"
+480            );
+481        }
+482    }
+483
+484    /// The earliest time at which an on-path challenge we sent is considered lost.
+485    pub(super) fn earliest_on_path_expiring_challenge(&self) -> Option<Instant> {
+486        if self.on_path_challenges_unconfirmed.is_empty() {
+487            return None;
+488        }
+489        let pto = self.rtt.pto_base();
+490        self.on_path_challenges_unconfirmed
+491            .values()
+492            .map(|info| info.sent_instant + pto)
+493            .min()
+494    }
+495
+496    /// Handle receiving a PATH_RESPONSE.
+497    pub(super) fn on_path_response_received(
+498        &mut self,
+499        now: Instant,
+500        token: u64,
+501        network_path: FourTuple,
+502    ) -> OnPathResponseReceived {
+503        // > § 8.2.3
+504        // > Path validation succeeds when a PATH_RESPONSE frame is received that contains the
+505        // > data that was sent in a previous PATH_CHALLENGE frame. A PATH_RESPONSE frame
+506        // > received on any network path validates the path on which the PATH_CHALLENGE was
+507        // > sent.
+508        //
+509        // At this point we have three potentially different network paths:
+510        // - current network path (`Self::network_path`)
+511        // - network path used to send the path challenge (`SentChallengeInfo::network_path`)
+512        // - network path over which the response arrived (`network_path`)
+513        //
+514        // As per the spec, this only validates the network path on which this was *sent*.
+515        match self.on_path_challenges_unconfirmed.remove(&token) {
+516            // Response to an on-path PathChallenge that validates this path.
+517            // The sent path should match the current path. However, it's possible that the
+518            // challenge was sent when no local_ip was known. This case is allowed as well.
+519            Some(info) if info.network_path.is_probably_same_path(&self.network_path) => {
+520                self.network_path.update_local_if_same_remote(&network_path);
+521                let sent_instant = info.sent_instant;
+522                if !std::mem::replace(&mut self.validated, true) {
+523                    trace!("new path validated");
+524                }
+525                // Clear any other on-path sent challenges and stop sending new ones.
+526                self.on_path_challenges_unconfirmed.clear();
+527                self.pending_on_path_challenge = false;
+528
+529                // This RTT can only be used for the initial RTT, not as a normal
+530                // sample: https://www.rfc-editor.org/rfc/rfc9002#section-6.2.2-2.
+531                let rtt = now.saturating_duration_since(sent_instant);
+532                self.rtt.reset_initial_rtt(rtt);
+533
+534                let prev_status = std::mem::replace(&mut self.open_status, OpenStatus::Informed);
+535                OnPathResponseReceived::OnPath {
+536                    was_open: matches!(prev_status, OpenStatus::Informed),
+537                }
+538            }
+539            // Response to an on-path PathChallenge that does not validate this path.
+540            Some(info) => {
+541                // This is a valid path response, but this validates a 4-tuple we no longer
+542                // have in use. Keep only sent challenges for the current path.
+543                self.on_path_challenges_unconfirmed
+544                    .retain(|_token, i| i.network_path == self.network_path);
+545
+546                // If there are no challenges for the current path, schedule one
+547                if !self.on_path_challenges_unconfirmed.is_empty() {
+548                    self.pending_on_path_challenge = true;
+549                }
+550                OnPathResponseReceived::Ignored {
+551                    sent_on: info.network_path,
+552                    current_path: self.network_path,
+553                }
+554            }
+555            None => {
+556                // Response to an unknown PathChallenge. Does not indicate failure.
+557                OnPathResponseReceived::Unknown
+558            }
+559        }
+560    }
+561
+562    /// Removes all on-path challenges we remember and cancels sending new on-path challenges.
+563    pub(super) fn reset_on_path_challenges(&mut self) {
+564        self.on_path_challenges_unconfirmed.clear();
+565        self.pending_on_path_challenge = false;
+566    }
+567
+568    #[cfg(feature = "qlog")]
+569    pub(super) fn qlog_recovery_metrics(
+570        &mut self,
+571        path_id: PathId,
+572    ) -> Option<RecoveryMetricsUpdated> {
+573        let controller_metrics = self.congestion.metrics();
+574
+575        let metrics = RecoveryMetrics {
+576            min_rtt: Some(self.rtt.min),
+577            smoothed_rtt: Some(self.rtt.get()),
+578            latest_rtt: Some(self.rtt.latest),
+579            rtt_variance: Some(self.rtt.var),
+580            pto_count: Some(self.pto_count),
+581            bytes_in_flight: Some(self.in_flight.bytes),
+582            packets_in_flight: Some(self.in_flight.ack_eliciting),
+583
+584            congestion_window: Some(controller_metrics.congestion_window),
+585            ssthresh: controller_metrics.ssthresh,
+586            pacing_rate: controller_metrics.pacing_rate,
+587        };
+588
+589        let event = metrics.to_qlog_event(path_id, &self.recovery_metrics);
+590        self.recovery_metrics = metrics;
+591        event
+592    }
+593
+594    /// Return how long we need to wait before sending `bytes_to_send`
+595    ///
+596    /// See [`Pacer::delay`].
+597    pub(super) fn pacing_delay(&mut self, bytes_to_send: u64, now: Instant) -> Option<Duration> {
+598        let smoothed_rtt = self.rtt.get();
+599        let metrics = self.congestion.metrics();
+600        self.pacing.delay(
+601            smoothed_rtt,
+602            bytes_to_send,
+603            self.current_mtu(),
+604            metrics.congestion_window,
+605            now,
+606            metrics.send_quantum,
+607            metrics.pacing_rate,
+608        )
+609    }
+610
+611    /// Updates the last observed address report received on this path.
+612    ///
+613    /// If the address was updated, it's returned to be informed to the application.
+614    #[must_use = "updated observed address must be reported to the application"]
+615    pub(super) fn update_observed_addr_report(
+616        &mut self,
+617        observed: ObservedAddr,
+618    ) -> Option<SocketAddr> {
+619        match self.last_observed_addr_report.as_mut() {
+620            Some(prev) => {
+621                if prev.seq_no >= observed.seq_no {
+622                    // frames that do not increase the sequence number on this path are ignored
+623                    None
+624                } else if prev.ip == observed.ip && prev.port == observed.port {
+625                    // keep track of the last seq_no but do not report the address as updated
+626                    prev.seq_no = observed.seq_no;
+627                    None
+628                } else {
+629                    let addr = observed.socket_addr();
+630                    self.last_observed_addr_report = Some(observed);
+631                    Some(addr)
+632                }
+633            }
+634            None => {
+635                let addr = observed.socket_addr();
+636                self.last_observed_addr_report = Some(observed);
+637                Some(addr)
+638            }
+639        }
+640    }
+641
+642    pub(crate) fn remote_status(&self) -> Option<PathStatus> {
+643        self.status.remote_status.map(|(_seq, status)| status)
+644    }
+645
+646    pub(crate) fn local_status(&self) -> PathStatus {
+647        self.status.local_status
+648    }
+649
+650    /// Tag uniquely identifying a path in a connection.
+651    ///
+652    /// When a migration happens on the same [`PathId`] we still detect a change in the
+653    /// 4-tuple and generate a new [`PathData`] for it. Each such generation has a unique
+654    /// value to keep track of which 4-tuple a packet belonged to.
+655    pub(super) fn generation(&self) -> u64 {
+656        self.generation
+657    }
+658}
+659
+660pub(super) enum OnPathResponseReceived {
+661    /// This response validates the path on its current remote address.
+662    OnPath { was_open: bool },
+663    /// The received token is unknown.
+664    Unknown,
+665    /// The response is valid but it's not usable for path validation.
+666    Ignored {
+667        sent_on: FourTuple,
+668        current_path: FourTuple,
+669    },
+670}
+671
+672#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
+673pub(super) enum OpenStatus {
+674    /// A first packet has not been sent using this [`PathId`].
+675    #[default]
+676    Pending,
+677    /// The first packet has been sent using this [`PathId`]. However, it is not yet deemed good
+678    /// enough to be reported to the application.
+679    Sent,
+680    /// The application has been informed of this path.
+681    Informed,
+682}
+683
+684/// Congestion metrics as described in [`recovery_metrics_updated`].
+685///
+686/// [`recovery_metrics_updated`]: https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-quic-events.html#name-recovery_metrics_updated
+687#[cfg(feature = "qlog")]
+688#[derive(Default, Clone, PartialEq, Debug)]
+689#[non_exhaustive]
+690struct RecoveryMetrics {
+691    pub min_rtt: Option<Duration>,
+692    pub smoothed_rtt: Option<Duration>,
+693    pub latest_rtt: Option<Duration>,
+694    pub rtt_variance: Option<Duration>,
+695    pub pto_count: Option<u32>,
+696    pub bytes_in_flight: Option<u64>,
+697    pub packets_in_flight: Option<u64>,
+698    pub congestion_window: Option<u64>,
+699    pub ssthresh: Option<u64>,
+700    pub pacing_rate: Option<u64>,
+701}
+702
+703#[cfg(feature = "qlog")]
+704impl RecoveryMetrics {
+705    /// Retain only values that have been updated since the last snapshot.
+706    fn retain_updated(&self, previous: &Self) -> Self {
+707        macro_rules! keep_if_changed {
+708            ($name:ident) => {
+709                if previous.$name == self.$name {
+710                    None
+711                } else {
+712                    self.$name
+713                }
+714            };
+715        }
+716
+717        Self {
+718            min_rtt: keep_if_changed!(min_rtt),
+719            smoothed_rtt: keep_if_changed!(smoothed_rtt),
+720            latest_rtt: keep_if_changed!(latest_rtt),
+721            rtt_variance: keep_if_changed!(rtt_variance),
+722            pto_count: keep_if_changed!(pto_count),
+723            bytes_in_flight: keep_if_changed!(bytes_in_flight),
+724            packets_in_flight: keep_if_changed!(packets_in_flight),
+725            congestion_window: keep_if_changed!(congestion_window),
+726            ssthresh: keep_if_changed!(ssthresh),
+727            pacing_rate: keep_if_changed!(pacing_rate),
+728        }
+729    }
+730
+731    /// Emit a `MetricsUpdated` event containing only updated values
+732    fn to_qlog_event(&self, path_id: PathId, previous: &Self) -> Option<RecoveryMetricsUpdated> {
+733        let updated = self.retain_updated(previous);
+734
+735        if updated == Self::default() {
+736            return None;
+737        }
+738
+739        Some(RecoveryMetricsUpdated {
+740            min_rtt: updated.min_rtt.map(|rtt| rtt.as_micros() as f32 / 1000.0),
+741            smoothed_rtt: updated
+742                .smoothed_rtt
+743                .map(|rtt| rtt.as_micros() as f32 / 1000.0),
+744            latest_rtt: updated
+745                .latest_rtt
+746                .map(|rtt| rtt.as_micros() as f32 / 1000.0),
+747            rtt_variance: updated
+748                .rtt_variance
+749                .map(|rtt| rtt.as_micros() as f32 / 1000.0),
+750            pto_count: updated
+751                .pto_count
+752                .map(|count| count.try_into().unwrap_or(u16::MAX)),
+753            bytes_in_flight: updated.bytes_in_flight,
+754            packets_in_flight: updated.packets_in_flight,
+755            congestion_window: updated.congestion_window,
+756            ssthresh: updated.ssthresh,
+757            pacing_rate: updated.pacing_rate,
+758            path_id: Some(path_id.as_u32() as u64),
+759        })
+760    }
+761}
+762
+763/// RTT estimation for a particular network path
+764#[derive(Copy, Clone, Debug)]
+765pub struct RttEstimator {
+766    /// The most recent RTT measurement made when receiving an ack for a previously unacked packet
+767    latest: Duration,
+768    /// The smoothed RTT of the connection, computed as described in RFC6298
+769    smoothed: Option<Duration>,
+770    /// The RTT variance, computed as described in RFC6298
+771    var: Duration,
+772    /// The minimum RTT seen in the connection, ignoring ack delay.
+773    min: Duration,
+774}
+775
+776impl RttEstimator {
+777    pub(super) fn new(initial_rtt: Duration) -> Self {
+778        Self {
+779            latest: initial_rtt,
+780            smoothed: None,
+781            var: initial_rtt / 2,
+782            min: initial_rtt,
+783        }
+784    }
+785
+786    /// Resets the estimator using a new initial_rtt value.
+787    ///
+788    /// This only resets the initial_rtt **if** no samples have been recorded yet. If there
+789    /// are any recorded samples the initial estimate can not be adjusted after the fact.
+790    ///
+791    /// This is useful when you receive a PATH_RESPONSE in the first packet received on a
+792    /// new path. In this case you can use the delay of the PATH_CHALLENGE-PATH_RESPONSE as
+793    /// the initial RTT to get a better expected estimation.
+794    ///
+795    /// A PATH_CHALLENGE-PATH_RESPONSE pair later in the connection should not be used
+796    /// explicitly as an estimation since PATH_CHALLENGE is an ACK-eliciting packet itself
+797    /// already.
+798    pub(crate) fn reset_initial_rtt(&mut self, initial_rtt: Duration) {
+799        if self.smoothed.is_none() {
+800            self.latest = initial_rtt;
+801            self.var = initial_rtt / 2;
+802            self.min = initial_rtt;
+803        }
+804    }
+805
+806    /// The current best RTT estimation.
+807    pub fn get(&self) -> Duration {
+808        self.smoothed.unwrap_or(self.latest)
+809    }
+810
+811    /// Conservative estimate of RTT
+812    ///
+813    /// Takes the maximum of smoothed and latest RTT, as recommended
+814    /// in 6.1.2 of the recovery spec (draft 29).
+815    pub fn conservative(&self) -> Duration {
+816        self.get().max(self.latest)
+817    }
+818
+819    /// Minimum RTT registered so far for this estimator.
+820    pub fn min(&self) -> Duration {
+821        self.min
+822    }
+823
+824    /// PTO computed as described in RFC9002#6.2.1.
+825    pub(crate) fn pto_base(&self) -> Duration {
+826        self.get() + cmp::max(4 * self.var, TIMER_GRANULARITY)
+827    }
+828
+829    /// Records an RTT sample.
+830    pub(crate) fn update(&mut self, ack_delay: Duration, rtt: Duration) {
+831        self.latest = rtt;
+832        // https://www.rfc-editor.org/rfc/rfc9002.html#section-5.2-3:
+833        // min_rtt does not adjust for ack_delay to avoid underestimating.
+834        self.min = cmp::min(self.min, self.latest);
+835        // Based on RFC6298.
+836        if let Some(smoothed) = self.smoothed {
+837            let adjusted_rtt = if self.min + ack_delay <= self.latest {
+838                self.latest - ack_delay
+839            } else {
+840                self.latest
+841            };
+842            let var_sample = smoothed.abs_diff(adjusted_rtt);
+843            self.var = (3 * self.var + var_sample) / 4;
+844            self.smoothed = Some((7 * smoothed + adjusted_rtt) / 8);
+845        } else {
+846            self.smoothed = Some(self.latest);
+847            self.var = self.latest / 2;
+848            self.min = self.latest;
+849        }
+850    }
+851}
+852
+853#[derive(Default, Debug)]
+854pub(crate) struct PathResponses {
+855    pending: Vec<PathResponse>,
+856}
+857
+858impl PathResponses {
+859    pub(crate) fn push(&mut self, packet: u64, token: u64, network_path: FourTuple) {
+860        /// An arbitrary permissive limit to prevent abuse.
+861        ///
+862        /// If we've negotiated the n0 NAT Traversal extension, and one user might have a lot
+863        /// of addresses, e.g. because of having lots of interfaces (we've seen >25 interfaces
+864        /// on Macs with docker and other things), then we need to be able to process at least
+865        /// as many PATH_CHALLENGE frames as there are interfaces.
+866        /// On top of that, there are retries, which make it possible that we need to process
+867        /// even more.
+868        ///
+869        /// Considering that there can be up to 2 `PathData`s per active `PathId`, and
+870        /// reasonable default values for maximum concurrent multipath paths are ~8 and each
+871        /// `PathResponse` struct takes up 72 bytes at the moment this, means an attacker can
+872        /// cause us to keep `32 * 2 * 8 * 72 = ~37KB` of data around.
+873        const MAX_PATH_RESPONSES: usize = 32;
+874        let response = PathResponse {
+875            packet,
+876            token,
+877            network_path,
+878        };
+879        let existing = self
+880            .pending
+881            .iter_mut()
+882            .find(|x| x.network_path.remote == network_path.remote);
+883        if let Some(existing) = existing {
+884            // Update a queued response
+885            if existing.packet <= packet {
+886                *existing = response;
+887            }
+888            return;
+889        }
+890        if self.pending.len() < MAX_PATH_RESPONSES {
+891            self.pending.push(response);
+892        } else {
+893            // We don't expect to ever hit this with well-behaved peers, so we don't bother dropping
+894            // older challenges.
+895            trace!("ignoring excessive PATH_CHALLENGE");
+896        }
+897    }
+898
+899    pub(crate) fn pop_off_path(&mut self, network_path: FourTuple) -> Option<(u64, FourTuple)> {
+900        let response = *self.pending.last()?;
+901        // We use an exact comparison here, because once we've received for the first time,
+902        // we really should either already have a local_ip, or we will never get one
+903        // (because our OS doesn't support it).
+904        if response.network_path == network_path {
+905            // We don't bother searching further because we expect that the on-path response will
+906            // get drained in the immediate future by a call to `pop_on_path`
+907            return None;
+908        }
+909        self.pending.pop();
+910        Some((response.token, response.network_path))
+911    }
+912
+913    pub(crate) fn pop_on_path(&mut self, network_path: FourTuple) -> Option<u64> {
+914        let response = *self.pending.last()?;
+915        // Using an exact comparison. See explanation in `pop_off_path`.
+916        if response.network_path != network_path {
+917            // We don't bother searching further because we expect that the off-path response will
+918            // get drained in the immediate future by a call to `pop_off_path`
+919            return None;
+920        }
+921        self.pending.pop();
+922        Some(response.token)
+923    }
+924
+925    pub(crate) fn is_empty(&self) -> bool {
+926        self.pending.is_empty()
+927    }
+928}
+929
+930#[derive(Copy, Clone, Debug)]
+931struct PathResponse {
+932    /// The packet number the corresponding PATH_CHALLENGE was received in
+933    packet: u64,
+934    /// The token of the PATH_CHALLENGE
+935    token: u64,
+936    /// The path the corresponding PATH_CHALLENGE was received from
+937    network_path: FourTuple,
+938}
+939
+940/// Summary statistics of packets that have been sent on a particular path, but which have not yet
+941/// been acked or deemed lost
+942#[derive(Debug)]
+943pub(super) struct InFlight {
+944    /// Sum of the sizes of all sent packets considered "in flight" by congestion control
+945    ///
+946    /// The size does not include IP or UDP overhead. Packets only containing ACK frames do not
+947    /// count towards this to ensure congestion control does not impede congestion feedback.
+948    pub(super) bytes: u64,
+949    /// Number of packets in flight containing frames other than ACK and PADDING
+950    ///
+951    /// This can be 0 even when bytes is not 0 because PADDING frames cause a packet to be
+952    /// considered "in flight" by congestion control. However, if this is nonzero, bytes will always
+953    /// also be nonzero.
+954    pub(super) ack_eliciting: u64,
+955}
+956
+957impl InFlight {
+958    fn new() -> Self {
+959        Self {
+960            bytes: 0,
+961            ack_eliciting: 0,
+962        }
+963    }
+964
+965    fn insert(&mut self, packet: &SentPacket) {
+966        self.bytes += u64::from(packet.size);
+967        self.ack_eliciting += u64::from(packet.ack_eliciting);
+968    }
+969
+970    /// Update counters to account for a packet becoming acknowledged, lost, or abandoned
+971    fn remove(&mut self, packet: &SentPacket) {
+972        self.bytes -= u64::from(packet.size);
+973        self.ack_eliciting -= u64::from(packet.ack_eliciting);
+974    }
+975}
+976
+977/// State for QUIC-MULTIPATH PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP frames
+978#[derive(Debug, Clone, Default)]
+979pub(super) struct PathStatusState {
+980    /// The local status
+981    local_status: PathStatus,
+982    /// Local sequence number, for both PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP
+983    ///
+984    /// This is the number of the *next* path status frame to be sent.
+985    local_seq: VarInt,
+986    /// The status set by the remote
+987    remote_status: Option<(VarInt, PathStatus)>,
+988}
+989
+990impl PathStatusState {
+991    /// To be called on received PATH_STATUS_AVAILABLE/PATH_STATUS_BACKUP frames
+992    pub(super) fn remote_update(&mut self, status: PathStatus, seq: VarInt) {
+993        if self.remote_status.is_some_and(|(curr, _)| curr >= seq) {
+994            return trace!(%seq, "ignoring path status update");
+995        }
+996
+997        let prev = self.remote_status.replace((seq, status)).map(|(_, s)| s);
+998        if prev != Some(status) {
+999            debug!(?status, ?seq, "remote changed path status");
+1000        }
+1001    }
+1002
+1003    /// Updates the local status
+1004    ///
+1005    /// If the local status changed, the previous value is returned
+1006    pub(super) fn local_update(&mut self, status: PathStatus) -> Option<PathStatus> {
+1007        if self.local_status == status {
+1008            return None;
+1009        }
+1010
+1011        self.local_seq = self.local_seq.saturating_add(1u8);
+1012        Some(std::mem::replace(&mut self.local_status, status))
+1013    }
+1014
+1015    pub(crate) fn seq(&self) -> VarInt {
+1016        self.local_seq
+1017    }
+1018}
+1019
+1020/// The QUIC-MULTIPATH path status
+1021///
+1022/// See section "3.3 Path Status Management":
+1023/// <https://quicwg.org/multipath/draft-ietf-quic-multipath.html#name-path-status-management>
+1024#[cfg_attr(test, derive(test_strategy::Arbitrary))]
+1025#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
+1026pub enum PathStatus {
+1027    /// Paths marked with as available will be used when scheduling packets
+1028    ///
+1029    /// If multiple paths are available, packets will be scheduled on whichever has
+1030    /// capacity.
+1031    #[default]
+1032    Available,
+1033    /// Paths marked as backup will only be used if there are no available paths
+1034    ///
+1035    /// If the max_idle_timeout is specified the path will be kept alive so that it does not
+1036    /// expire.
+1037    Backup,
+1038}
+1039
+1040/// Application events about paths
+1041#[derive(Debug, Clone, PartialEq, Eq)]
+1042#[non_exhaustive]
+1043pub enum PathEvent {
+1044    /// A new path has established connection with the peer.
+1045    #[non_exhaustive]
+1046    Established {
+1047        /// The path which can now be used for application data.
+1048        id: PathId,
+1049    },
+1050    /// A path was abandoned and is no longer usable.
+1051    ///
+1052    /// This event will always be followed by [`Self::Discarded`] after some time.
+1053    #[non_exhaustive]
+1054    Abandoned {
+1055        /// With path was abandoned.
+1056        id: PathId,
+1057        /// Reason why this path was abandoned.
+1058        reason: PathAbandonReason,
+1059    },
+1060    /// A path was discarded and all remaining state for it has been removed.
+1061    ///
+1062    /// This event is the last event for a path, and is always emitted after [`Self::Abandoned`].
+1063    #[non_exhaustive]
+1064    Discarded {
+1065        /// Which path had its state dropped
+1066        id: PathId,
+1067        /// The final path stats, they are no longer available via [`Connection::stats`]
+1068        ///
+1069        /// [`Connection::stats`]: super::Connection::stats
+1070        path_stats: Box<PathStats>,
+1071    },
+1072    /// The remote changed the status of the path
+1073    ///
+1074    /// The local status is not changed because of this event. It is up to the application
+1075    /// to update the local status, which is used for packet scheduling, when the remote
+1076    /// changes the status.
+1077    #[non_exhaustive]
+1078    RemoteStatus {
+1079        /// Path which has changed status
+1080        id: PathId,
+1081        /// The new status set by the remote
+1082        status: PathStatus,
+1083    },
+1084    /// Received an observation of our external address from the peer.
+1085    #[non_exhaustive]
+1086    ObservedAddr {
+1087        /// Path over which the observed address was reported, [`PathId::ZERO`] when multipath is
+1088        /// not negotiated
+1089        id: PathId,
+1090        /// The address observed by the remote over this path
+1091        addr: SocketAddr,
+1092    },
+1093}
+1094
+1095/// Reason for why a path was abandoned.
+1096#[derive(Debug, Clone, Eq, PartialEq)]
+1097pub enum PathAbandonReason {
+1098    /// The path was closed locally by the application.
+1099    ApplicationClosed {
+1100        /// The error code to be sent with the abandon frame.
+1101        error_code: VarInt,
+1102    },
+1103    /// We didn't receive a path response in time after opening this path.
+1104    ValidationFailed,
+1105    /// We didn't receive any data from the remote within the path's idle timeout.
+1106    TimedOut,
+1107    /// The path became unusable after a local network change.
+1108    UnusableAfterNetworkChange,
+1109    /// The remote closed the path.
+1110    RemoteAbandoned {
+1111        /// The error that was sent with the abandon frame.
+1112        error_code: VarInt,
+1113    },
+1114}
+1115
+1116impl PathAbandonReason {
+1117    /// Whether this abandon was initiated by the remote peer.
+1118    pub(crate) fn is_remote(&self) -> bool {
+1119        matches!(self, Self::RemoteAbandoned { .. })
+1120    }
+1121
+1122    /// Returns the error code to send with a PATH_ABANDON frame.
+1123    pub(crate) fn error_code(&self) -> TransportErrorCode {
+1124        match self {
+1125            Self::ApplicationClosed { error_code } => (*error_code).into(),
+1126            Self::ValidationFailed | Self::TimedOut | Self::UnusableAfterNetworkChange => {
+1127                TransportErrorCode::PATH_UNSTABLE_OR_POOR
+1128            }
+1129            Self::RemoteAbandoned { error_code } => (*error_code).into(),
+1130        }
+1131    }
+1132}
+1133
+1134/// Error from setting path status
+1135#[derive(Debug, Error, Clone, PartialEq, Eq)]
+1136pub enum SetPathStatusError {
+1137    /// Error indicating that a path has not been opened or has already been abandoned
+1138    #[error("closed path")]
+1139    ClosedPath,
+1140    /// Error indicating that this operation requires multipath to be negotiated whereas it hasn't been
+1141    #[error("multipath not negotiated")]
+1142    MultipathNotNegotiated,
+1143}
+1144
+1145/// Error indicating that a path has not been opened or has already been abandoned
+1146#[derive(Debug, Default, Error, Clone, PartialEq, Eq)]
+1147#[error("closed path")]
+1148pub struct ClosedPath {
+1149    pub(super) _private: (),
+1150}
+1151
+1152#[cfg(test)]
+1153mod tests {
+1154    use super::*;
+1155
+1156    #[test]
+1157    fn test_path_id_saturating_add() {
+1158        // add within range behaves normally
+1159        let large: PathId = u16::MAX.into();
+1160        let next = u32::from(u16::MAX) + 1;
+1161        assert_eq!(large.saturating_add(1u8), PathId::from(next));
+1162
+1163        // outside range saturates
+1164        assert_eq!(PathId::MAX.saturating_add(1u8), PathId::MAX)
+1165    }
+1166}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/qlog.rs.html b/pr/672/docs/src/noq_proto/connection/qlog.rs.html new file mode 100644 index 000000000..a4f47b22b --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/qlog.rs.html @@ -0,0 +1,1297 @@ +qlog.rs - source

noq_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::{EncryptionLevel, PathData, SentPacket, SpaceKind, timer::Timer},
+42    frame::EncodableFrame,
+43    packet::Header,
+44    transport_parameters::TransportParameters,
+45};
+46#[cfg(feature = "qlog")]
+47use crate::{
+48    QlogConfig, Side, TransportErrorCode,
+49    connection::timer::{ConnTimer, PathTimer},
+50    frame::{self, DataBlocked, StreamDataBlocked, StreamsBlocked},
+51};
+52
+53/// Shareable handle to a single qlog output stream
+54#[cfg(feature = "qlog")]
+55#[derive(Clone)]
+56pub(crate) struct QlogStream(Arc<Mutex<QlogStreamer>>);
+57
+58#[cfg(feature = "qlog")]
+59impl QlogStream {
+60    pub(crate) fn new(
+61        config: QlogConfig,
+62        initial_dst_cid: ConnectionId,
+63        side: Side,
+64        now: Instant,
+65    ) -> Result<Self, qlog::Error> {
+66        let vantage_point = VantagePoint {
+67            name: None,
+68            ty: match side {
+69                Side::Client => qlog::VantagePointType::Client,
+70                Side::Server => qlog::VantagePointType::Server,
+71            },
+72            flow: None,
+73        };
+74
+75        let common_fields = CommonFields {
+76            group_id: Some(initial_dst_cid.to_string()),
+77            ..Default::default()
+78        };
+79
+80        let trace = qlog::TraceSeq::new(
+81            config.title.clone(),
+82            config.description.clone(),
+83            Some(common_fields),
+84            Some(vantage_point),
+85            vec![],
+86        );
+87
+88        let start_time = config.start_time.unwrap_or(now);
+89
+90        let mut streamer = QlogStreamer::new(
+91            config.title,
+92            config.description,
+93            start_time,
+94            trace,
+95            qlog::events::EventImportance::Extra,
+96            config.writer,
+97        );
+98
+99        streamer.start_log()?;
+100        Ok(Self(Arc::new(Mutex::new(streamer))))
+101    }
+102
+103    fn emit_event(&self, event: EventData, now: Instant) {
+104        self.emit_event_with_tuple_id(event, now, None);
+105    }
+106
+107    fn emit_event_with_tuple_id(
+108        &self,
+109        event: EventData,
+110        now: Instant,
+111        network_path: Option<String>,
+112    ) {
+113        // Time will be overwritten by `add_event_with_instant`
+114        let mut event = Event::with_time(0.0, event);
+115        event.tuple = network_path;
+116        let mut qlog_streamer = self.0.lock().unwrap();
+117        if let Err(e) = qlog_streamer.add_event_with_instant(event, now) {
+118            warn!("could not emit qlog event: {e}");
+119        }
+120    }
+121}
+122
+123/// A [`QlogStream`] that may be either dynamically disabled or compiled out entirely
+124#[derive(Clone, Default)]
+125pub(crate) struct QlogSink {
+126    #[cfg(feature = "qlog")]
+127    stream: Option<QlogStream>,
+128}
+129
+130impl QlogSink {
+131    #[cfg(feature = "qlog")]
+132    pub(crate) fn new(
+133        config: QlogConfig,
+134        initial_dst_cid: ConnectionId,
+135        side: Side,
+136        now: Instant,
+137    ) -> Self {
+138        let stream = QlogStream::new(config, initial_dst_cid, side, now)
+139            .inspect_err(|err| warn!("failed to initialize qlog streamer: {err}"))
+140            .ok();
+141        Self { stream }
+142    }
+143
+144    pub(crate) fn emit_connection_started(
+145        &self,
+146        now: Instant,
+147        local_cid: ConnectionId,
+148        remote_cid: ConnectionId,
+149        remote: SocketAddr,
+150        local_ip: Option<IpAddr>,
+151        transport_params: &TransportParameters,
+152    ) {
+153        #[cfg(feature = "qlog")]
+154        {
+155            let Some(stream) = self.stream.as_ref() else {
+156                return;
+157            };
+158            stream.emit_event(
+159                EventData::ConnectionStarted(ConnectionStarted {
+160                    local: tuple_endpoint_info(local_ip, None, Some(local_cid)),
+161                    remote: tuple_endpoint_info(
+162                        Some(remote.ip()),
+163                        Some(remote.port()),
+164                        Some(remote_cid),
+165                    ),
+166                }),
+167                now,
+168            );
+169
+170            let params = transport_params.to_qlog(TransportInitiator::Local);
+171            let event = EventData::ParametersSet(params);
+172            stream.emit_event(event, now);
+173        }
+174    }
+175
+176    pub(super) fn emit_recovery_metrics(&self, path_id: PathId, path: &mut PathData, now: Instant) {
+177        #[cfg(feature = "qlog")]
+178        {
+179            let Some(stream) = self.stream.as_ref() else {
+180                return;
+181            };
+182
+183            let Some(metrics) = path.qlog_recovery_metrics(path_id) else {
+184                return;
+185            };
+186
+187            stream.emit_event(EventData::MetricsUpdated(metrics), now);
+188        }
+189    }
+190
+191    pub(super) fn emit_packet_lost(
+192        &self,
+193        pn: u64,
+194        info: &SentPacket,
+195        loss_delay: Duration,
+196        space: SpaceKind,
+197        now: Instant,
+198    ) {
+199        #[cfg(feature = "qlog")]
+200        {
+201            let Some(stream) = self.stream.as_ref() else {
+202                return;
+203            };
+204
+205            let event = PacketLost {
+206                header: Some(PacketHeader {
+207                    packet_number: Some(pn),
+208                    packet_type: packet_type(space, false),
+209                    length: Some(info.size),
+210                    ..Default::default()
+211                }),
+212                frames: None,
+213                trigger: Some(
+214                    match info.time_sent.saturating_duration_since(now) >= loss_delay {
+215                        true => PacketLostTrigger::TimeThreshold,
+216                        false => PacketLostTrigger::ReorderingThreshold,
+217                    },
+218                ),
+219                is_mtu_probe_packet: None,
+220            };
+221
+222            stream.emit_event(EventData::PacketLost(event), now);
+223        }
+224    }
+225
+226    pub(super) fn emit_peer_transport_params_restored(&self, conn: &Connection, now: Instant) {
+227        #[cfg(feature = "qlog")]
+228        {
+229            let Some(stream) = self.stream.as_ref() else {
+230                return;
+231            };
+232            let params = conn.peer_params.to_qlog_restored();
+233            let event = EventData::ParametersRestored(params);
+234            stream.emit_event(event, now);
+235        }
+236    }
+237
+238    pub(super) fn emit_peer_transport_params_received(&self, conn: &Connection, now: Instant) {
+239        #[cfg(feature = "qlog")]
+240        {
+241            let Some(stream) = self.stream.as_ref() else {
+242                return;
+243            };
+244            let params = conn.peer_params.to_qlog(TransportInitiator::Remote);
+245            let event = EventData::ParametersSet(params);
+246            stream.emit_event(event, now);
+247        }
+248    }
+249
+250    pub(super) fn emit_tuple_assigned(&self, path_id: PathId, tuple: FourTuple, now: Instant) {
+251        #[cfg(feature = "qlog")]
+252        {
+253            let Some(stream) = self.stream.as_ref() else {
+254                return;
+255            };
+256            let tuple_id = fmt_tuple_id(path_id.as_u32() as u64);
+257            let event = TupleAssigned {
+258                tuple_id,
+259                tuple_local: tuple
+260                    .local_ip
+261                    .map(|local_ip| tuple_endpoint_info(Some(local_ip), None, None)),
+262                tuple_remote: Some(tuple_endpoint_info(
+263                    Some(tuple.remote.ip()),
+264                    Some(tuple.remote.port()),
+265                    None,
+266                )),
+267            };
+268
+269            stream.emit_event(EventData::TupleAssigned(event), now);
+270        }
+271    }
+272
+273    pub(super) fn emit_packet_sent(&self, packet: QlogSentPacket, now: Instant) {
+274        #[cfg(feature = "qlog")]
+275        {
+276            let Some(stream) = self.stream.as_ref() else {
+277                return;
+278            };
+279            let tuple_id = packet.inner.header.path_id.map(fmt_tuple_id);
+280            stream.emit_event_with_tuple_id(EventData::PacketSent(packet.inner), now, tuple_id);
+281        }
+282    }
+283
+284    pub(super) fn emit_packet_received(&self, packet: QlogRecvPacket, now: Instant) {
+285        #[cfg(feature = "qlog")]
+286        {
+287            let Some(stream) = self.stream.as_ref() else {
+288                return;
+289            };
+290            let mut packet = packet;
+291            packet.emit_padding();
+292            let tuple_id = packet.inner.header.path_id.map(fmt_tuple_id);
+293            let event = packet.inner;
+294            stream.emit_event_with_tuple_id(EventData::PacketReceived(event), now, tuple_id);
+295        }
+296    }
+297
+298    /// Emits a timer event.
+299    ///
+300    /// This function is not public: Instead, create a [`QlogSinkWithTime`] via [`Self::with_time`] and use
+301    /// its `emit_timer_` methods.
+302    #[cfg(feature = "qlog")]
+303    fn emit_timer(&self, timer: Timer, op: TimerOp, now: Instant) {
+304        let Some(stream) = self.stream.as_ref() else {
+305            return;
+306        };
+307
+308        let timer_type: Option<TimerType> = match timer {
+309            Timer::Conn(conn_timer) => match conn_timer {
+310                ConnTimer::Idle => Some(QlogTimerType::IdleTimeout.into()),
+311                ConnTimer::Close => Some(TimerType::custom("close")),
+312                ConnTimer::KeyDiscard => Some(TimerType::custom("key_discard")),
+313                ConnTimer::KeepAlive => Some(TimerType::custom("keep_alive")),
+314                ConnTimer::PushNewCid => Some(TimerType::custom("push_new_cid")),
+315                ConnTimer::NoAvailablePath => Some(TimerType::custom("no_available_path")),
+316                ConnTimer::NatTraversalProbeRetry => {
+317                    Some(TimerType::custom("nat_traversal_probe_retry"))
+318                }
+319            },
+320            Timer::PerPath(_, path_timer) => match path_timer {
+321                PathTimer::LossDetection => Some(QlogTimerType::LossTimeout.into()),
+322                PathTimer::PathIdle => Some(TimerType::custom("path_idle")),
+323                PathTimer::PathValidationFailed => Some(QlogTimerType::PathValidation.into()),
+324                PathTimer::PathChallengeLost => Some(TimerType::custom("path_challenge_lost")),
+325                PathTimer::AbandonFromValidation => {
+326                    Some(TimerType::custom("abandon_from_validation"))
+327                }
+328                PathTimer::PathKeepAlive => Some(TimerType::custom("path_keep_alive")),
+329                PathTimer::Pacing => Some(TimerType::custom("pacing")),
+330                PathTimer::MaxAckDelay => Some(QlogTimerType::Ack.into()),
+331                PathTimer::PathDrained => Some(TimerType::custom("discard_path")),
+332            },
+333        };
+334
+335        let Some(timer_type) = timer_type else {
+336            return;
+337        };
+338
+339        let delta = match op {
+340            TimerOp::Set(instant) => instant
+341                .checked_duration_since(now)
+342                .map(|dur| dur.as_secs_f32() * 1000.),
+343            _ => None,
+344        };
+345        let path_id = match timer {
+346            Timer::Conn(_) => None,
+347            Timer::PerPath(path_id, _) => Some(path_id.as_u32() as u64),
+348        };
+349
+350        let event_type = match op {
+351            TimerOp::Set(_) => TimerEventType::Set,
+352            TimerOp::Expire => TimerEventType::Expired,
+353            TimerOp::Cancelled => TimerEventType::Cancelled,
+354        };
+355
+356        let event = TimerUpdated {
+357            path_id,
+358            timer_type: Some(timer_type),
+359            timer_id: None,
+360            packet_number_space: None,
+361            event_type,
+362            delta,
+363        };
+364        stream.emit_event(EventData::TimerUpdated(event), now);
+365    }
+366
+367    /// Returns a [`QlogSinkWithTime`] that passes along a `now` timestamp.
+368    ///
+369    /// This may be used if you want to pass a [`QlogSink`] downwards together with the current
+370    /// `now` timestamp, to not have to pass the latter separately as an additional argument just
+371    /// for qlog support.
+372    pub(super) fn with_time(&self, now: Instant) -> QlogSinkWithTime<'_> {
+373        #[cfg(feature = "qlog")]
+374        let s = QlogSinkWithTime { sink: self, now };
+375        #[cfg(not(feature = "qlog"))]
+376        let s = QlogSinkWithTime {
+377            _phantom: PhantomData,
+378        };
+379        s
+380    }
+381}
+382
+383/// A [`QlogSink`] with a `now` timestamp.
+384#[derive(Clone)]
+385pub(super) struct QlogSinkWithTime<'a> {
+386    #[cfg(feature = "qlog")]
+387    sink: &'a QlogSink,
+388    #[cfg(feature = "qlog")]
+389    now: Instant,
+390    #[cfg(not(feature = "qlog"))]
+391    _phantom: PhantomData<&'a ()>,
+392}
+393
+394impl<'a> QlogSinkWithTime<'a> {
+395    pub(super) fn emit_timer_stop(&self, timer: Timer) {
+396        #[cfg(feature = "qlog")]
+397        self.sink.emit_timer(timer, TimerOp::Cancelled, self.now)
+398    }
+399
+400    pub(super) fn emit_timer_set(&self, timer: Timer, expire_at: Instant) {
+401        #[cfg(feature = "qlog")]
+402        self.sink
+403            .emit_timer(timer, TimerOp::Set(expire_at), self.now)
+404    }
+405
+406    pub(super) fn emit_timer_expire(&self, timer: Timer) {
+407        #[cfg(feature = "qlog")]
+408        self.sink.emit_timer(timer, TimerOp::Expire, self.now)
+409    }
+410}
+411
+412#[cfg(feature = "qlog")]
+413enum TimerOp {
+414    Set(Instant),
+415    Expire,
+416    Cancelled,
+417}
+418
+419/// Info about a sent packet. Zero-sized struct if `qlog` feature is not enabled.
+420#[derive(Default)]
+421pub(crate) struct QlogSentPacket {
+422    #[cfg(feature = "qlog")]
+423    inner: PacketSent,
+424}
+425
+426impl QlogSentPacket {
+427    /// Sets data from the packet header.
+428    pub(crate) fn header(
+429        &mut self,
+430        header: &Header,
+431        pn: Option<u64>,
+432        encryption_level: EncryptionLevel,
+433        path_id: PathId,
+434    ) {
+435        #[cfg(feature = "qlog")]
+436        {
+437            self.inner.header.scid = header.src_cid().map(stringify_cid);
+438            self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
+439            self.inner.header.packet_number = pn;
+440            self.inner.header.packet_type = encryption_level.into();
+441            self.inner.header.path_id = Some(path_id.as_u32() as u64);
+442        }
+443    }
+444
+445    /// Adds a PADDING frame.
+446    ///
+447    /// This is a no-op if the `qlog` feature is not enabled.
+448    pub(crate) fn frame_padding(&mut self, count: usize) {
+449        #[cfg(feature = "qlog")]
+450        self.frame_raw(QuicFrame::Padding {
+451            raw: Some(RawInfo {
+452                length: Some(count as u64),
+453                payload_length: Some(count as u64),
+454                data: None,
+455            }),
+456        });
+457    }
+458
+459    /// Adds a frame by pushing a [`QuicFrame`].
+460    ///
+461    /// This function is only available if the `qlog` feature is enabled, because constructing a [`QuicFrame`] may involve
+462    /// calculations which shouldn't be performed if the `qlog` feature is disabled.
+463    #[cfg(feature = "qlog")]
+464    fn frame_raw(&mut self, frame: QuicFrame) {
+465        self.inner.frames.get_or_insert_default().push(frame);
+466    }
+467
+468    /// Finalizes the packet by setting the final packet length (after encryption).
+469    pub(super) fn finalize(&mut self, len: usize) {
+470        #[cfg(feature = "qlog")]
+471        {
+472            self.inner.header.length = Some(len as u16);
+473        }
+474    }
+475
+476    pub(crate) fn record<'a>(&mut self, frame: &EncodableFrame<'a>) {
+477        #[cfg(feature = "qlog")]
+478        self.frame_raw(frame.to_qlog());
+479    }
+480}
+481
+482/// Info about a received packet. Zero-sized struct if `qlog` feature is not enabled.
+483pub(crate) struct QlogRecvPacket {
+484    #[cfg(feature = "qlog")]
+485    inner: PacketReceived,
+486    #[cfg(feature = "qlog")]
+487    padding: usize,
+488}
+489
+490impl QlogRecvPacket {
+491    /// Creates a new [`QlogRecvPacket`]. Noop if `qlog` feature is not enabled.
+492    ///
+493    /// `len` is the packet's full length (before decryption).
+494    pub(crate) fn new(len: usize) -> Self {
+495        #[cfg(not(feature = "qlog"))]
+496        let this = Self {};
+497
+498        #[cfg(feature = "qlog")]
+499        let this = {
+500            let mut this = Self {
+501                inner: Default::default(),
+502                padding: 0,
+503            };
+504            this.inner.header.length = Some(len as u16);
+505            this
+506        };
+507
+508        this
+509    }
+510
+511    /// Adds info from the packet header.
+512    pub(crate) fn header(&mut self, header: &Header, pn: Option<u64>, path_id: PathId) {
+513        #[cfg(feature = "qlog")]
+514        {
+515            let is_0rtt = !header.is_1rtt();
+516            self.inner.header.scid = header.src_cid().map(stringify_cid);
+517            self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
+518            self.inner.header.packet_number = pn;
+519            self.inner.header.packet_type = packet_type(header.space(), is_0rtt);
+520            self.inner.header.path_id = Some(path_id.as_u32() as u64);
+521        }
+522    }
+523
+524    /// Adds a frame.
+525    pub(crate) fn frame(&mut self, frame: &Frame) {
+526        #[cfg(feature = "qlog")]
+527        {
+528            if matches!(frame, crate::Frame::Padding) {
+529                self.padding += 1;
+530            } else {
+531                self.emit_padding();
+532                self.inner
+533                    .frames
+534                    .get_or_insert_default()
+535                    .push(frame.to_qlog())
+536            }
+537        }
+538    }
+539
+540    #[cfg(feature = "qlog")]
+541    fn emit_padding(&mut self) {
+542        if self.padding > 0 {
+543            self.inner
+544                .frames
+545                .get_or_insert_default()
+546                .push(QuicFrame::Padding {
+547                    raw: Some(RawInfo {
+548                        length: Some(self.padding as u64),
+549                        payload_length: Some(self.padding as u64),
+550                        data: None,
+551                    }),
+552                });
+553            self.padding = 0;
+554        }
+555    }
+556}
+557
+558/* Frame conversions to qlog */
+559#[cfg(feature = "qlog")]
+560pub(crate) trait ToQlog {
+561    fn to_qlog(&self) -> QuicFrame;
+562}
+563
+564#[cfg(feature = "qlog")]
+565impl<'a> ToQlog for frame::AckEncoder<'a> {
+566    fn to_qlog(&self) -> QuicFrame {
+567        QuicFrame::Ack {
+568            ack_delay: Some(self.delay as f32),
+569            acked_ranges: Some(AckedRanges::Double(
+570                self.ranges
+571                    .iter()
+572                    .map(|range| (range.start, range.end))
+573                    .collect(),
+574            )),
+575            ect1: self.ecn.map(|e| e.ect1),
+576            ect0: self.ecn.map(|e| e.ect0),
+577            ce: self.ecn.map(|e| e.ce),
+578            raw: None,
+579        }
+580    }
+581}
+582
+583#[cfg(feature = "qlog")]
+584impl ToQlog for frame::AckFrequency {
+585    fn to_qlog(&self) -> QuicFrame {
+586        QuicFrame::AckFrequency {
+587            sequence_number: self.sequence.into_inner(),
+588            ack_eliciting_threshold: self.ack_eliciting_threshold.into_inner(),
+589            requested_max_ack_delay: self.request_max_ack_delay.into_inner(),
+590            reordering_threshold: self.reordering_threshold.into_inner(),
+591            raw: None,
+592        }
+593    }
+594}
+595
+596#[cfg(feature = "qlog")]
+597impl ToQlog for frame::AddAddress {
+598    fn to_qlog(&self) -> QuicFrame {
+599        QuicFrame::AddAddress {
+600            sequence_number: self.seq_no.into_inner(),
+601            ip_v4: match self.ip {
+602                IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
+603                IpAddr::V6(ipv6_addr) => None,
+604            },
+605            ip_v6: match self.ip {
+606                IpAddr::V4(ipv4_addr) => None,
+607                IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
+608            },
+609            port: self.port,
+610        }
+611    }
+612}
+613
+614#[cfg(feature = "qlog")]
+615impl ToQlog for frame::CloseEncoder<'_> {
+616    fn to_qlog(&self) -> QuicFrame {
+617        self.close.to_qlog()
+618    }
+619}
+620
+621#[cfg(feature = "qlog")]
+622impl ToQlog for frame::Close {
+623    fn to_qlog(&self) -> QuicFrame {
+624        match self {
+625            Self::Connection(f) => {
+626                let (error, error_code) = transport_error(f.error_code);
+627                let error = error.map(|transport_error| {
+628                    ConnectionClosedFrameError::TransportError(transport_error)
+629                });
+630                QuicFrame::ConnectionClose {
+631                    error_space: Some(ErrorSpace::TransportError),
+632                    error,
+633                    error_code,
+634                    reason: String::from_utf8(f.reason.to_vec()).ok(),
+635                    reason_bytes: None,
+636                    trigger_frame_type: None,
+637                }
+638            }
+639            Self::Application(f) => QuicFrame::ConnectionClose {
+640                error_space: Some(ErrorSpace::ApplicationError),
+641                error: None,
+642                error_code: Some(f.error_code.into_inner()),
+643                reason: String::from_utf8(f.reason.to_vec()).ok(),
+644                reason_bytes: None,
+645                trigger_frame_type: None,
+646            },
+647        }
+648    }
+649}
+650
+651#[cfg(feature = "qlog")]
+652impl ToQlog for frame::Crypto {
+653    fn to_qlog(&self) -> QuicFrame {
+654        QuicFrame::Crypto {
+655            offset: self.offset,
+656            raw: Some(RawInfo {
+657                length: Some(self.data.len() as u64),
+658                ..Default::default()
+659            }),
+660        }
+661    }
+662}
+663
+664#[cfg(feature = "qlog")]
+665impl ToQlog for frame::Datagram {
+666    fn to_qlog(&self) -> QuicFrame {
+667        QuicFrame::Datagram {
+668            raw: Some(RawInfo {
+669                length: Some(self.data.len() as u64),
+670                ..Default::default()
+671            }),
+672        }
+673    }
+674}
+675
+676#[cfg(feature = "qlog")]
+677impl ToQlog for frame::HandshakeDone {
+678    fn to_qlog(&self) -> QuicFrame {
+679        QuicFrame::HandshakeDone { raw: None }
+680    }
+681}
+682
+683#[cfg(feature = "qlog")]
+684impl ToQlog for frame::ImmediateAck {
+685    fn to_qlog(&self) -> QuicFrame {
+686        QuicFrame::ImmediateAck { raw: None }
+687    }
+688}
+689
+690#[cfg(feature = "qlog")]
+691impl ToQlog for frame::MaxData {
+692    fn to_qlog(&self) -> QuicFrame {
+693        QuicFrame::MaxData {
+694            maximum: self.0.into(),
+695            raw: None,
+696        }
+697    }
+698}
+699
+700#[cfg(feature = "qlog")]
+701impl ToQlog for frame::MaxPathId {
+702    fn to_qlog(&self) -> QuicFrame {
+703        QuicFrame::MaxPathId {
+704            maximum_path_id: self.0.as_u32().into(),
+705            raw: None,
+706        }
+707    }
+708}
+709
+710#[cfg(feature = "qlog")]
+711impl ToQlog for frame::MaxStreamData {
+712    fn to_qlog(&self) -> QuicFrame {
+713        QuicFrame::MaxStreamData {
+714            stream_id: self.id.into(),
+715            maximum: self.offset,
+716            raw: None,
+717        }
+718    }
+719}
+720
+721#[cfg(feature = "qlog")]
+722impl ToQlog for frame::MaxStreams {
+723    fn to_qlog(&self) -> QuicFrame {
+724        QuicFrame::MaxStreams {
+725            maximum: self.count,
+726            stream_type: self.dir.into(),
+727            raw: None,
+728        }
+729    }
+730}
+731
+732#[cfg(feature = "qlog")]
+733impl ToQlog for frame::StreamsBlocked {
+734    fn to_qlog(&self) -> QuicFrame {
+735        QuicFrame::StreamsBlocked {
+736            stream_type: self.dir.into(),
+737            limit: self.limit,
+738            raw: None,
+739        }
+740    }
+741}
+742
+743#[cfg(feature = "qlog")]
+744impl ToQlog for frame::NewConnectionId {
+745    fn to_qlog(&self) -> QuicFrame {
+746        match self.path_id {
+747            None => QuicFrame::NewConnectionId {
+748                sequence_number: self.sequence,
+749                retire_prior_to: self.retire_prior_to,
+750                connection_id_length: Some(self.id.len() as u8),
+751                connection_id: self.id.to_string(),
+752                stateless_reset_token: Some(self.reset_token.to_string()),
+753                raw: None,
+754            },
+755            Some(path_id) => QuicFrame::PathNewConnectionId {
+756                path_id: path_id.0 as u64,
+757                sequence_number: self.sequence,
+758                retire_prior_to: self.retire_prior_to,
+759                connection_id_length: Some(self.id.len() as u8),
+760                connection_id: self.id.to_string(),
+761                stateless_reset_token: Some(self.reset_token.to_string()),
+762                raw: None,
+763            },
+764        }
+765    }
+766}
+767
+768#[cfg(feature = "qlog")]
+769impl ToQlog for frame::NewToken {
+770    fn to_qlog(&self) -> QuicFrame {
+771        QuicFrame::NewToken {
+772            token: qlog::Token {
+773                ty: Some(TokenType::Retry),
+774                raw: Some(RawInfo {
+775                    data: HexSlice::maybe_string(Some(&self.token)),
+776                    length: Some(self.token.len() as u64),
+777                    payload_length: None,
+778                }),
+779                details: None,
+780            },
+781            raw: None,
+782        }
+783    }
+784}
+785
+786#[cfg(feature = "qlog")]
+787impl ToQlog for frame::ObservedAddr {
+788    fn to_qlog(&self) -> QuicFrame {
+789        QuicFrame::ObservedAddress {
+790            sequence_number: self.seq_no.into_inner(),
+791            ip_v4: match self.ip {
+792                IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
+793                IpAddr::V6(ipv6_addr) => None,
+794            },
+795            ip_v6: match self.ip {
+796                IpAddr::V4(ipv4_addr) => None,
+797                IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
+798            },
+799            port: self.port,
+800            raw: None,
+801        }
+802    }
+803}
+804
+805#[cfg(feature = "qlog")]
+806impl ToQlog for frame::PathAbandon {
+807    fn to_qlog(&self) -> QuicFrame {
+808        QuicFrame::PathAbandon {
+809            path_id: self.path_id.as_u32().into(),
+810            error_code: self.error_code.into(),
+811            raw: None,
+812        }
+813    }
+814}
+815
+816#[cfg(feature = "qlog")]
+817impl ToQlog for frame::PathAckEncoder<'_> {
+818    fn to_qlog(&self) -> QuicFrame {
+819        QuicFrame::PathAck {
+820            path_id: self.path_id.as_u32() as u64,
+821            ack_delay: Some(self.delay as f32),
+822            acked_ranges: Some(AckedRanges::Double(
+823                self.ranges
+824                    .iter()
+825                    .map(|range| (range.start, range.end))
+826                    .collect(),
+827            )),
+828            ect1: self.ecn.map(|e| e.ect1),
+829            ect0: self.ecn.map(|e| e.ect0),
+830            ce: self.ecn.map(|e| e.ce),
+831            raw: None,
+832        }
+833    }
+834}
+835
+836#[cfg(feature = "qlog")]
+837impl ToQlog for frame::PathChallenge {
+838    #[cfg(feature = "qlog")]
+839    fn to_qlog(&self) -> QuicFrame {
+840        QuicFrame::PathChallenge {
+841            data: Some(self.to_string()),
+842            raw: None,
+843        }
+844    }
+845}
+846
+847#[cfg(feature = "qlog")]
+848impl ToQlog for frame::PathCidsBlocked {
+849    fn to_qlog(&self) -> QuicFrame {
+850        QuicFrame::PathCidsBlocked {
+851            path_id: self.path_id.as_u32().into(),
+852            next_sequence_number: self.next_seq.into(),
+853            raw: None,
+854        }
+855    }
+856}
+857
+858#[cfg(feature = "qlog")]
+859impl ToQlog for frame::PathResponse {
+860    fn to_qlog(&self) -> QuicFrame {
+861        QuicFrame::PathResponse {
+862            data: Some(self.to_string()),
+863            raw: None,
+864        }
+865    }
+866}
+867
+868#[cfg(feature = "qlog")]
+869impl ToQlog for frame::ReachOut {
+870    fn to_qlog(&self) -> QuicFrame {
+871        QuicFrame::ReachOut {
+872            round: self.round.into_inner(),
+873            ip_v4: match self.ip {
+874                IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
+875                IpAddr::V6(ipv6_addr) => None,
+876            },
+877            ip_v6: match self.ip {
+878                IpAddr::V4(ipv4_addr) => None,
+879                IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
+880            },
+881            port: self.port,
+882        }
+883    }
+884}
+885
+886#[cfg(feature = "qlog")]
+887impl ToQlog for frame::Ping {
+888    fn to_qlog(&self) -> QuicFrame {
+889        QuicFrame::Ping { raw: None }
+890    }
+891}
+892
+893#[cfg(feature = "qlog")]
+894impl ToQlog for frame::PathStatusAvailable {
+895    fn to_qlog(&self) -> QuicFrame {
+896        QuicFrame::PathStatusAvailable {
+897            path_id: self.path_id.as_u32().into(),
+898            path_status_sequence_number: self.status_seq_no.into(),
+899            raw: None,
+900        }
+901    }
+902}
+903
+904#[cfg(feature = "qlog")]
+905impl ToQlog for frame::PathStatusBackup {
+906    fn to_qlog(&self) -> QuicFrame {
+907        QuicFrame::PathStatusBackup {
+908            path_id: self.path_id.as_u32().into(),
+909            path_status_sequence_number: self.status_seq_no.into(),
+910            raw: None,
+911        }
+912    }
+913}
+914
+915#[cfg(feature = "qlog")]
+916impl ToQlog for frame::PathsBlocked {
+917    fn to_qlog(&self) -> QuicFrame {
+918        QuicFrame::PathsBlocked {
+919            maximum_path_id: self.0.as_u32().into(),
+920            raw: None,
+921        }
+922    }
+923}
+924
+925#[cfg(feature = "qlog")]
+926impl ToQlog for frame::ResetStream {
+927    fn to_qlog(&self) -> QuicFrame {
+928        QuicFrame::ResetStream {
+929            stream_id: self.id.into(),
+930            error_code: Some(self.error_code.into_inner()),
+931            final_size: self.final_offset.into(),
+932            error: ApplicationError::Unknown,
+933            raw: None,
+934        }
+935    }
+936}
+937
+938#[cfg(feature = "qlog")]
+939impl ToQlog for frame::StopSending {
+940    fn to_qlog(&self) -> QuicFrame {
+941        QuicFrame::StopSending {
+942            stream_id: self.id.into(),
+943            error_code: Some(self.error_code.into_inner()),
+944            error: ApplicationError::Unknown,
+945            raw: None,
+946        }
+947    }
+948}
+949
+950#[cfg(feature = "qlog")]
+951impl ToQlog for frame::RetireConnectionId {
+952    fn to_qlog(&self) -> QuicFrame {
+953        match self.path_id {
+954            None => QuicFrame::RetireConnectionId {
+955                sequence_number: self.sequence,
+956                raw: None,
+957            },
+958            Some(path_id) => QuicFrame::PathRetireConnectionId {
+959                path_id: path_id.0 as u64,
+960                sequence_number: self.sequence,
+961                raw: None,
+962            },
+963        }
+964    }
+965}
+966
+967#[cfg(feature = "qlog")]
+968impl ToQlog for frame::RemoveAddress {
+969    fn to_qlog(&self) -> QuicFrame {
+970        QuicFrame::RemoveAddress {
+971            sequence_number: self.seq_no.into_inner(),
+972        }
+973    }
+974}
+975
+976#[cfg(feature = "qlog")]
+977impl ToQlog for frame::StreamMetaEncoder {
+978    fn to_qlog(&self) -> QuicFrame {
+979        let meta = &self.meta;
+980        QuicFrame::Stream {
+981            stream_id: meta.id.into(),
+982            offset: Some(meta.offsets.start),
+983            fin: Some(meta.fin),
+984            raw: Some(RawInfo {
+985                length: Some(meta.offsets.end - meta.offsets.start),
+986                ..Default::default()
+987            }),
+988        }
+989    }
+990}
+991
+992#[cfg(feature = "qlog")]
+993impl Frame {
+994    /// Converts a [`crate::Frame`] into a [`QuicFrame`].
+995    pub(crate) fn to_qlog(&self) -> QuicFrame {
+996        match self {
+997            Self::Padding => QuicFrame::Padding {
+998                raw: Some(RawInfo {
+999                    length: None,
+1000                    payload_length: Some(1),
+1001                    data: None,
+1002                }),
+1003            },
+1004            Self::Ping => frame::Ping.to_qlog(),
+1005            Self::Ack(f) => QuicFrame::Ack {
+1006                ack_delay: Some(f.delay as f32),
+1007                acked_ranges: Some(AckedRanges::Double(
+1008                    f.iter().map(|range| (range.start, range.end)).collect(),
+1009                )),
+1010                ect1: f.ecn.as_ref().map(|e| e.ect1),
+1011                ect0: f.ecn.as_ref().map(|e| e.ect0),
+1012                ce: f.ecn.as_ref().map(|e| e.ce),
+1013                raw: None,
+1014            },
+1015            Self::ResetStream(f) => f.to_qlog(),
+1016            Self::StopSending(f) => f.to_qlog(),
+1017            Self::Crypto(f) => f.to_qlog(),
+1018            Self::NewToken(f) => f.to_qlog(),
+1019            Self::Stream(s) => QuicFrame::Stream {
+1020                stream_id: s.id.into(),
+1021                offset: Some(s.offset),
+1022                fin: Some(s.fin),
+1023                raw: Some(RawInfo {
+1024                    length: Some(s.data.len() as u64),
+1025                    ..Default::default()
+1026                }),
+1027            },
+1028            Self::MaxData(v) => v.to_qlog(),
+1029            Self::MaxStreamData(f) => f.to_qlog(),
+1030            Self::MaxStreams(f) => f.to_qlog(),
+1031            Self::DataBlocked(DataBlocked(offset)) => QuicFrame::DataBlocked {
+1032                limit: *offset,
+1033                raw: None,
+1034            },
+1035            Self::StreamDataBlocked(StreamDataBlocked { id, offset }) => {
+1036                QuicFrame::StreamDataBlocked {
+1037                    stream_id: (*id).into(),
+1038                    limit: *offset,
+1039                    raw: None,
+1040                }
+1041            }
+1042            Self::StreamsBlocked(StreamsBlocked { dir, limit }) => QuicFrame::StreamsBlocked {
+1043                stream_type: (*dir).into(),
+1044                limit: *limit,
+1045                raw: None,
+1046            },
+1047            Self::NewConnectionId(f) => f.to_qlog(),
+1048            Self::RetireConnectionId(f) => f.to_qlog(),
+1049            Self::PathChallenge(f) => f.to_qlog(),
+1050            Self::PathResponse(f) => f.to_qlog(),
+1051            Self::Close(close) => close.to_qlog(),
+1052            Self::Datagram(d) => d.to_qlog(),
+1053            Self::HandshakeDone => frame::HandshakeDone.to_qlog(),
+1054            Self::PathAck(ack) => QuicFrame::PathAck {
+1055                path_id: ack.path_id.as_u32().into(),
+1056                ack_delay: Some(ack.delay as f32),
+1057                ect1: ack.ecn.as_ref().map(|e| e.ect1),
+1058                ect0: ack.ecn.as_ref().map(|e| e.ect0),
+1059                ce: ack.ecn.as_ref().map(|e| e.ce),
+1060                raw: None,
+1061                acked_ranges: Some(AckedRanges::Double(
+1062                    ack.ranges
+1063                        .iter()
+1064                        .map(|range| (range.start, range.end))
+1065                        .collect(),
+1066                )),
+1067            },
+1068            Self::PathAbandon(frame) => frame.to_qlog(),
+1069            Self::PathStatusAvailable(frame) => frame.to_qlog(),
+1070            Self::PathStatusBackup(frame) => frame.to_qlog(),
+1071            Self::PathsBlocked(frame) => frame.to_qlog(),
+1072            Self::PathCidsBlocked(frame) => frame.to_qlog(),
+1073            Self::MaxPathId(f) => f.to_qlog(),
+1074            Self::AckFrequency(f) => f.to_qlog(),
+1075            Self::ImmediateAck => frame::ImmediateAck.to_qlog(),
+1076            Self::ObservedAddr(f) => f.to_qlog(),
+1077            Self::AddAddress(f) => f.to_qlog(),
+1078            Self::ReachOut(f) => f.to_qlog(),
+1079            Self::RemoveAddress(f) => f.to_qlog(),
+1080        }
+1081    }
+1082}
+1083
+1084#[cfg(feature = "qlog")]
+1085impl From<crate::Dir> for StreamType {
+1086    fn from(value: crate::Dir) -> Self {
+1087        match value {
+1088            crate::Dir::Bi => Self::Bidirectional,
+1089            crate::Dir::Uni => Self::Unidirectional,
+1090        }
+1091    }
+1092}
+1093
+1094#[cfg(feature = "qlog")]
+1095fn packet_type(space: SpaceKind, is_0rtt: bool) -> PacketType {
+1096    match space {
+1097        SpaceKind::Initial => PacketType::Initial,
+1098        SpaceKind::Handshake => PacketType::Handshake,
+1099        SpaceKind::Data if is_0rtt => PacketType::ZeroRtt,
+1100        SpaceKind::Data => PacketType::OneRtt,
+1101    }
+1102}
+1103
+1104#[cfg(feature = "qlog")]
+1105impl From<EncryptionLevel> for PacketType {
+1106    fn from(encryption_level: EncryptionLevel) -> Self {
+1107        match encryption_level {
+1108            EncryptionLevel::Initial => Self::Initial,
+1109            EncryptionLevel::Handshake => Self::Handshake,
+1110            EncryptionLevel::ZeroRtt => Self::ZeroRtt,
+1111            EncryptionLevel::OneRtt => Self::OneRtt,
+1112        }
+1113    }
+1114}
+1115
+1116#[cfg(feature = "qlog")]
+1117fn stringify_cid(cid: ConnectionId) -> String {
+1118    format!("{cid}")
+1119}
+1120
+1121#[cfg(feature = "qlog")]
+1122fn tuple_endpoint_info(
+1123    ip: Option<IpAddr>,
+1124    port: Option<u16>,
+1125    cid: Option<ConnectionId>,
+1126) -> TupleEndpointInfo {
+1127    let (ip_v4, port_v4, ip_v6, port_v6) = match ip {
+1128        Some(addr) => match addr {
+1129            IpAddr::V4(ipv4_addr) => (Some(ipv4_addr.to_string()), port, None, None),
+1130            IpAddr::V6(ipv6_addr) => (None, None, Some(ipv6_addr.to_string()), port),
+1131        },
+1132        None => (None, None, None, None),
+1133    };
+1134    TupleEndpointInfo {
+1135        ip_v4,
+1136        port_v4,
+1137        ip_v6,
+1138        port_v6,
+1139        connection_ids: cid.map(|cid| vec![cid.to_string()]),
+1140    }
+1141}
+1142
+1143#[cfg(feature = "qlog")]
+1144fn transport_error(code: TransportErrorCode) -> (Option<quic::TransportError>, Option<u64>) {
+1145    let transport_error = match code {
+1146        TransportErrorCode::NO_ERROR => Some(quic::TransportError::NoError),
+1147        TransportErrorCode::INTERNAL_ERROR => Some(quic::TransportError::InternalError),
+1148        TransportErrorCode::CONNECTION_REFUSED => Some(quic::TransportError::ConnectionRefused),
+1149        TransportErrorCode::FLOW_CONTROL_ERROR => Some(quic::TransportError::FlowControlError),
+1150        TransportErrorCode::STREAM_LIMIT_ERROR => Some(quic::TransportError::StreamLimitError),
+1151        TransportErrorCode::STREAM_STATE_ERROR => Some(quic::TransportError::StreamStateError),
+1152        TransportErrorCode::FINAL_SIZE_ERROR => Some(quic::TransportError::FinalSizeError),
+1153        TransportErrorCode::FRAME_ENCODING_ERROR => Some(quic::TransportError::FrameEncodingError),
+1154        TransportErrorCode::TRANSPORT_PARAMETER_ERROR => {
+1155            Some(quic::TransportError::TransportParameterError)
+1156        }
+1157        TransportErrorCode::CONNECTION_ID_LIMIT_ERROR => {
+1158            Some(quic::TransportError::ConnectionIdLimitError)
+1159        }
+1160        TransportErrorCode::PROTOCOL_VIOLATION => Some(quic::TransportError::ProtocolViolation),
+1161        TransportErrorCode::INVALID_TOKEN => Some(quic::TransportError::InvalidToken),
+1162        TransportErrorCode::APPLICATION_ERROR => Some(quic::TransportError::ApplicationError),
+1163        TransportErrorCode::CRYPTO_BUFFER_EXCEEDED => {
+1164            Some(quic::TransportError::CryptoBufferExceeded)
+1165        }
+1166        TransportErrorCode::KEY_UPDATE_ERROR => Some(quic::TransportError::KeyUpdateError),
+1167        TransportErrorCode::AEAD_LIMIT_REACHED => Some(quic::TransportError::AeadLimitReached),
+1168        TransportErrorCode::NO_VIABLE_PATH => Some(quic::TransportError::NoViablePath),
+1169        // multipath
+1170        TransportErrorCode::APPLICATION_ABANDON_PATH => {
+1171            Some(quic::TransportError::ApplicationAbandonPath)
+1172        }
+1173        TransportErrorCode::PATH_RESOURCE_LIMIT_REACHED => {
+1174            Some(quic::TransportError::PathResourceLimitReached)
+1175        }
+1176        TransportErrorCode::PATH_UNSTABLE_OR_POOR => Some(quic::TransportError::PathUnstableOrPoor),
+1177        TransportErrorCode::NO_CID_AVAILABLE_FOR_PATH => {
+1178            Some(quic::TransportError::NoCidsAvailableForPath)
+1179        }
+1180        _ => None,
+1181    };
+1182    let code = match transport_error {
+1183        None => Some(code.into()),
+1184        Some(_) => None,
+1185    };
+1186    (transport_error, code)
+1187}
+1188
+1189#[cfg(feature = "qlog")]
+1190fn fmt_tuple_id(path_id: u64) -> String {
+1191    format!("p{path_id}")
+1192}
+1193
+1194#[cfg(feature = "qlog")]
+1195impl TransportParameters {
+1196    fn to_qlog(self, initiator: TransportInitiator) -> ParametersSet {
+1197        ParametersSet {
+1198            initiator: Some(initiator),
+1199            resumption_allowed: None,
+1200            early_data_enabled: None,
+1201            tls_cipher: None,
+1202            original_destination_connection_id: self
+1203                .original_dst_cid
+1204                .as_ref()
+1205                .map(ToString::to_string),
+1206            initial_source_connection_id: self.initial_src_cid.as_ref().map(ToString::to_string),
+1207            retry_source_connection_id: self.retry_src_cid.as_ref().map(ToString::to_string),
+1208            stateless_reset_token: self.stateless_reset_token.as_ref().map(ToString::to_string),
+1209            disable_active_migration: Some(self.disable_active_migration),
+1210            max_idle_timeout: Some(self.max_idle_timeout.into()),
+1211            max_udp_payload_size: Some(self.max_udp_payload_size.into()),
+1212            ack_delay_exponent: Some(self.ack_delay_exponent.into()),
+1213            max_ack_delay: Some(self.max_ack_delay.into()),
+1214            active_connection_id_limit: Some(self.active_connection_id_limit.into()),
+1215            initial_max_data: Some(self.initial_max_data.into()),
+1216            initial_max_stream_data_bidi_local: Some(
+1217                self.initial_max_stream_data_bidi_local.into(),
+1218            ),
+1219            initial_max_stream_data_bidi_remote: Some(
+1220                self.initial_max_stream_data_bidi_remote.into(),
+1221            ),
+1222            initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
+1223            initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
+1224            initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
+1225            preferred_address: self.preferred_address.as_ref().map(Into::into),
+1226            min_ack_delay: self.min_ack_delay.map(Into::into),
+1227            address_discovery: self.address_discovery_role.to_qlog(),
+1228            initial_max_path_id: self.initial_max_path_id.map(|p| p.as_u32() as u64),
+1229            max_remote_nat_traversal_addresses: self
+1230                .max_remote_nat_traversal_addresses
+1231                .map(|v| u64::from(v.get())),
+1232            max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
+1233            grease_quic_bit: Some(self.grease_quic_bit),
+1234            unknown_parameters: Default::default(),
+1235        }
+1236    }
+1237
+1238    fn to_qlog_restored(self) -> ParametersRestored {
+1239        ParametersRestored {
+1240            disable_active_migration: Some(self.disable_active_migration),
+1241            max_idle_timeout: Some(self.max_idle_timeout.into()),
+1242            max_udp_payload_size: Some(self.max_udp_payload_size.into()),
+1243            active_connection_id_limit: Some(self.active_connection_id_limit.into()),
+1244            initial_max_data: Some(self.initial_max_data.into()),
+1245            initial_max_stream_data_bidi_local: Some(
+1246                self.initial_max_stream_data_bidi_local.into(),
+1247            ),
+1248            initial_max_stream_data_bidi_remote: Some(
+1249                self.initial_max_stream_data_bidi_remote.into(),
+1250            ),
+1251            initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
+1252            initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
+1253            initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
+1254            max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
+1255            grease_quic_bit: Some(self.grease_quic_bit),
+1256        }
+1257    }
+1258}
+1259
+1260#[cfg(feature = "qlog")]
+1261impl From<&crate::transport_parameters::PreferredAddress> for PreferredAddress {
+1262    fn from(value: &crate::transport_parameters::PreferredAddress) -> Self {
+1263        let port_v4 = value.address_v4.map(|addr| addr.port()).unwrap_or_default();
+1264        let port_v6 = value.address_v6.map(|addr| addr.port()).unwrap_or_default();
+1265        let ip_v4 = value
+1266            .address_v4
+1267            .map(|addr| addr.ip().to_string())
+1268            .unwrap_or_default();
+1269        let ip_v6 = value
+1270            .address_v6
+1271            .map(|addr| addr.ip().to_string())
+1272            .unwrap_or_default();
+1273        let connection_id = value.connection_id.to_string();
+1274        let stateless_reset_token = value.stateless_reset_token.to_string();
+1275
+1276        Self {
+1277            ip_v4,
+1278            ip_v6,
+1279            port_v4,
+1280            port_v6,
+1281            connection_id,
+1282            stateless_reset_token,
+1283        }
+1284    }
+1285}
+1286
+1287#[cfg(feature = "qlog")]
+1288impl crate::address_discovery::Role {
+1289    fn to_qlog(self) -> Option<AddressDiscoveryRole> {
+1290        match self {
+1291            Self::SendOnly => Some(AddressDiscoveryRole::SendOnly),
+1292            Self::ReceiveOnly => Some(AddressDiscoveryRole::ReceiveOnly),
+1293            Self::Both => Some(AddressDiscoveryRole::Both),
+1294            Self::Disabled => None,
+1295        }
+1296    }
+1297}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/send_buffer.rs.html b/pr/672/docs/src/noq_proto/connection/send_buffer.rs.html new file mode 100644 index 000000000..c3b5ca93e --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/send_buffer.rs.html @@ -0,0 +1,762 @@ +send_buffer.rs - source

noq_proto/connection/
send_buffer.rs

1use std::{collections::VecDeque, ops::Range};
+2
+3use bytes::{Buf, BufMut, Bytes, BytesMut};
+4
+5use crate::{VarInt, connection::streams::BytesOrSlice, range_set::ArrayRangeSet};
+6
+7/// Buffer of outgoing retransmittable stream data
+8#[derive(Default, Debug)]
+9pub(super) struct SendBuffer {
+10    /// Data queued by the application that has to be retained for resends.
+11    ///
+12    /// Only data up to the highest contiguous acknowledged offset can be discarded.
+13    /// We could discard acknowledged in this buffer, but it would require a more
+14    /// complex data structure. Instead, we track acknowledged ranges in `acks`.
+15    ///
+16    /// Data keeps track of the base offset of the buffered data.
+17    data: SendBufferData,
+18    /// The first offset that hasn't been sent even once
+19    ///
+20    /// Always lies in `data.range()`
+21    unsent: u64,
+22    /// Acknowledged ranges which couldn't be discarded yet as they don't include the earliest
+23    /// offset in `unacked`
+24    ///
+25    /// All ranges must be within `data.range().start..(data.range().end - unsent)`, since data
+26    /// that has never been sent can't be acknowledged.
+27    // TODO: Recover storage from these by compacting (#700)
+28    acks: ArrayRangeSet,
+29    /// Previously transmitted ranges deemed lost and marked for retransmission
+30    ///
+31    /// All ranges must be within `data.range().start..(data.range().end - unsent)`, since data
+32    /// that has never been sent can't be retransmitted.
+33    ///
+34    /// This should usually not overlap with `acks`, but this is not strictly enforced.
+35    retransmits: ArrayRangeSet,
+36}
+37
+38/// Maximum number of bytes to combine into a single segment
+39///
+40/// Any segment larger than this will be stored as-is, possibly triggering a flush of the buffer.
+41const MAX_COMBINE: usize = 1452;
+42
+43/// This is where the data of the send buffer lives. It supports appending at the end,
+44/// removing from the front, and retrieving data by range.
+45#[derive(Default, Debug)]
+46struct SendBufferData {
+47    /// Start offset of the buffered data
+48    offset: u64,
+49    /// Total size of [`Self::segments`] and [`Self::last_segment`]
+50    len: usize,
+51    /// Buffered data segments
+52    segments: VecDeque<Bytes>,
+53    /// Last segment, possibly empty
+54    last_segment: BytesMut,
+55}
+56
+57impl SendBufferData {
+58    /// Total size of buffered data
+59    fn len(&self) -> usize {
+60        self.len
+61    }
+62
+63    /// Range of buffered data
+64    #[inline(always)]
+65    fn range(&self) -> Range<u64> {
+66        self.offset..self.offset + self.len as u64
+67    }
+68
+69    /// Append data to the end of the buffer
+70    fn append<'a>(&'a mut self, data: impl BytesOrSlice<'a>) {
+71        self.len += data.len();
+72        if data.len() > MAX_COMBINE {
+73            // use in place
+74            if !self.last_segment.is_empty() {
+75                self.segments.push_back(self.last_segment.split().freeze());
+76            }
+77            self.segments.push_back(data.into_bytes());
+78        } else {
+79            // copy
+80            let rest = if self.last_segment.len() + data.len() > MAX_COMBINE
+81                && !self.last_segment.is_empty()
+82            {
+83                // fill up last_segment up to MAX_COMBINE and flush
+84                let capacity = MAX_COMBINE.saturating_sub(self.last_segment.len());
+85                let (curr, rest) = data.as_ref().split_at(capacity);
+86                self.last_segment.put_slice(curr);
+87                self.segments.push_back(self.last_segment.split().freeze());
+88                rest
+89            } else {
+90                data.as_ref()
+91            };
+92            // copy the rest into the now empty last_segment
+93            self.last_segment.extend_from_slice(rest);
+94        }
+95    }
+96
+97    /// Discard data from the front of the buffer
+98    ///
+99    /// Calling this with n > len() is allowed and will simply clear the buffer.
+100    fn pop_front(&mut self, n: usize) {
+101        let mut n = n.min(self.len);
+102        self.len -= n;
+103        self.offset += n as u64;
+104        while n > 0 {
+105            // segments is empty, which leaves only last_segment
+106            let Some(front) = self.segments.front_mut() else {
+107                break;
+108            };
+109            if front.len() <= n {
+110                // Remove the whole front segment
+111                n -= front.len();
+112                self.segments.pop_front();
+113            } else {
+114                // Advance within the front segment
+115                front.advance(n);
+116                n = 0;
+117            }
+118        }
+119        // the rest has to be in the last segment
+120        self.last_segment.advance(n);
+121        // shrink segments if we have a lot of unused capacity
+122        if self.segments.len() * 4 < self.segments.capacity() {
+123            self.segments.shrink_to_fit();
+124        }
+125    }
+126
+127    /// Iterator over all segments in order
+128    ///
+129    /// Concatenates `segments` and `last_segment` so they can be handled uniformly
+130    fn segments_iter(&self) -> impl Iterator<Item = &[u8]> {
+131        self.segments
+132            .iter()
+133            .map(|x| x.as_ref())
+134            .chain(std::iter::once(self.last_segment.as_ref()))
+135    }
+136
+137    /// Returns data which is associated with a range
+138    ///
+139    /// Requesting a range outside of the buffered data will panic.
+140    #[cfg(any(test, feature = "bench"))]
+141    fn get(&self, offsets: Range<u64>) -> &[u8] {
+142        assert!(
+143            offsets.start >= self.range().start && offsets.end <= self.range().end,
+144            "Requested range is outside of buffered data"
+145        );
+146        // translate to segment-relative offsets and usize
+147        let offsets = Range {
+148            start: (offsets.start - self.offset) as usize,
+149            end: (offsets.end - self.offset) as usize,
+150        };
+151        let mut segment_offset = 0;
+152        for segment in self.segments_iter() {
+153            if offsets.start >= segment_offset && offsets.start < segment_offset + segment.len() {
+154                let start = offsets.start - segment_offset;
+155                let end = offsets.end - segment_offset;
+156
+157                return &segment[start..end.min(segment.len())];
+158            }
+159            segment_offset += segment.len();
+160        }
+161
+162        unreachable!("impossible if segments and range are consistent");
+163    }
+164
+165    fn get_into(&self, offsets: Range<u64>, buf: &mut impl BufMut) {
+166        assert!(
+167            offsets.start >= self.range().start && offsets.end <= self.range().end,
+168            "Requested range is outside of buffered data"
+169        );
+170        // translate to segment-relative offsets and usize
+171        let offsets = Range {
+172            start: (offsets.start - self.offset) as usize,
+173            end: (offsets.end - self.offset) as usize,
+174        };
+175        let mut segment_offset = 0;
+176        for segment in self.segments_iter() {
+177            // intersect segment range with requested range
+178            let start = segment_offset.max(offsets.start);
+179            let end = (segment_offset + segment.len()).min(offsets.end);
+180            if start < end {
+181                // slice range intersects with requested range
+182                buf.put_slice(&segment[start - segment_offset..end - segment_offset]);
+183            }
+184            segment_offset += segment.len();
+185            if segment_offset >= offsets.end {
+186                // we are beyond the requested range
+187                break;
+188            }
+189        }
+190    }
+191
+192    #[cfg(test)]
+193    fn to_vec(&self) -> Vec<u8> {
+194        let mut result = Vec::with_capacity(self.len);
+195        for segment in self.segments_iter() {
+196            result.extend_from_slice(segment);
+197        }
+198        result
+199    }
+200}
+201
+202impl SendBuffer {
+203    /// Construct an empty buffer at the initial offset
+204    pub(super) fn new() -> Self {
+205        Self::default()
+206    }
+207
+208    /// Append application data to the end of the stream
+209    pub(super) fn write<'a>(&'a mut self, data: impl BytesOrSlice<'a>) {
+210        self.data.append(data);
+211    }
+212
+213    /// Discard a range of acknowledged stream data
+214    pub(super) fn ack(&mut self, mut range: Range<u64>) {
+215        // Clamp the range to data which is still tracked
+216        let base_offset = self.fully_acked_offset();
+217        range.start = base_offset.max(range.start);
+218        range.end = base_offset.max(range.end);
+219
+220        self.acks.insert(range);
+221
+222        while self.acks.min() == Some(self.fully_acked_offset()) {
+223            let prefix = self.acks.pop_min().unwrap();
+224            let to_advance = (prefix.end - prefix.start) as usize;
+225            self.data.pop_front(to_advance);
+226        }
+227
+228        // Remove retransmit ranges which have been acknowledged
+229        //
+230        // We have to do this since we have just dropped the data, and asking
+231        // for non-present data would be an error.
+232        self.retransmits.remove(0..self.fully_acked_offset());
+233    }
+234
+235    /// Compute the next range to transmit on this stream and update state to account for that
+236    /// transmission.
+237    ///
+238    /// `max_len` here includes the space which is available to transmit the
+239    /// offset and length of the data to send. The caller has to guarantee that
+240    /// there is at least enough space available to write maximum-sized metadata
+241    /// (8 byte offset + 8 byte length).
+242    ///
+243    /// The method returns a tuple:
+244    /// - The first return value indicates the range of data to send
+245    /// - The second return value indicates whether the length needs to be encoded
+246    ///   in the STREAM frames metadata (`true`), or whether it can be omitted
+247    ///   since the selected range will fill the whole packet.
+248    pub(super) fn poll_transmit(&mut self, mut max_len: usize) -> (Range<u64>, bool) {
+249        debug_assert!(max_len >= 8 + 8);
+250        let mut encode_length = false;
+251
+252        if let Some(range) = self.retransmits.pop_min() {
+253            // Retransmit sent data
+254
+255            // When the offset is known, we know how many bytes are required to encode it.
+256            // Offset 0 requires no space
+257            if range.start != 0 {
+258                max_len -= VarInt::size(unsafe { VarInt::from_u64_unchecked(range.start) });
+259            }
+260            if range.end - range.start < max_len as u64 {
+261                encode_length = true;
+262                max_len -= 8;
+263            }
+264
+265            let end = range.end.min((max_len as u64).saturating_add(range.start));
+266            if end != range.end {
+267                self.retransmits.insert(end..range.end);
+268            }
+269            return (range.start..end, encode_length);
+270        }
+271
+272        // Transmit new data
+273
+274        // When the offset is known, we know how many bytes are required to encode it.
+275        // Offset 0 requires no space
+276        if self.unsent != 0 {
+277            max_len -= VarInt::size(unsafe { VarInt::from_u64_unchecked(self.unsent) });
+278        }
+279        if self.offset() - self.unsent < max_len as u64 {
+280            encode_length = true;
+281            max_len -= 8;
+282        }
+283
+284        let end = self
+285            .offset()
+286            .min((max_len as u64).saturating_add(self.unsent));
+287        let result = self.unsent..end;
+288        self.unsent = end;
+289        (result, encode_length)
+290    }
+291
+292    /// Returns data which is associated with a range
+293    ///
+294    /// This function can return a subset of the range, if the data is stored
+295    /// in noncontiguous fashion in the send buffer. In this case callers
+296    /// should call the function again with an incremented start offset to
+297    /// retrieve more data.
+298    #[cfg(any(test, feature = "bench"))]
+299    pub(super) fn get(&self, offsets: Range<u64>) -> &[u8] {
+300        self.data.get(offsets)
+301    }
+302
+303    pub(super) fn get_into(&self, offsets: Range<u64>, buf: &mut impl BufMut) {
+304        self.data.get_into(offsets, buf)
+305    }
+306
+307    /// Queue a range of sent but unacknowledged data to be retransmitted
+308    pub(super) fn retransmit(&mut self, mut range: Range<u64>) {
+309        debug_assert!(range.end <= self.unsent, "unsent data can't be lost");
+310        // don't allow retransmitting data that has already been fully acknowledged,
+311        // since we don't have it anymore.
+312        //
+313        // Note that we do allow retransmitting data that has been acknowledged
+314        // for simplicity. Not doing so would require clipping the range against
+315        // all acknowledged ranges.
+316        range.start = range.start.max(self.fully_acked_offset());
+317        self.retransmits.insert(range);
+318    }
+319
+320    pub(super) fn retransmit_all_for_0rtt(&mut self) {
+321        // check that we still got all data - we didn't get any acks.
+322        debug_assert_eq!(self.fully_acked_offset(), 0);
+323        self.unsent = 0;
+324    }
+325
+326    /// Offset up to which all data has been acknowledged
+327    fn fully_acked_offset(&self) -> u64 {
+328        self.data.range().start
+329    }
+330
+331    /// First stream offset unwritten by the application, i.e. the offset that the next write will
+332    /// begin at
+333    pub(super) fn offset(&self) -> u64 {
+334        self.data.range().end
+335    }
+336
+337    /// Whether all sent data has been acknowledged
+338    pub(super) fn is_fully_acked(&self) -> bool {
+339        self.data.len() == 0
+340    }
+341
+342    /// Whether there's data to send
+343    ///
+344    /// There may be sent unacknowledged data even when this is false.
+345    pub(super) fn has_unsent_data(&self) -> bool {
+346        self.unsent != self.offset() || !self.retransmits.is_empty()
+347    }
+348
+349    /// Compute the amount of data that hasn't been acknowledged
+350    pub(super) fn unacked(&self) -> u64 {
+351        self.data.len() as u64 - self.acks.iter().map(|x| x.end - x.start).sum::<u64>()
+352    }
+353}
+354
+355#[cfg(test)]
+356mod tests {
+357    use super::*;
+358
+359    #[test]
+360    fn fragment_with_length() {
+361        let mut buf = SendBuffer::new();
+362        const MSG: &[u8] = b"Hello, world!";
+363        buf.write(MSG);
+364        // 0 byte offset => 19 bytes left => 13 byte data isn't enough
+365        // with 8 bytes reserved for length 11 payload bytes will fit
+366        assert_eq!(buf.poll_transmit(19), (0..11, true));
+367        assert_eq!(
+368            buf.poll_transmit(MSG.len() + 16 - 11),
+369            (11..MSG.len() as u64, true)
+370        );
+371        assert_eq!(
+372            buf.poll_transmit(58),
+373            (MSG.len() as u64..MSG.len() as u64, true)
+374        );
+375    }
+376
+377    #[test]
+378    fn fragment_without_length() {
+379        let mut buf = SendBuffer::new();
+380        const MSG: &[u8] = b"Hello, world with some extra data!";
+381        buf.write(MSG);
+382        // 0 byte offset => 19 bytes left => can be filled by 34 bytes payload
+383        assert_eq!(buf.poll_transmit(19), (0..19, false));
+384        assert_eq!(
+385            buf.poll_transmit(MSG.len() - 19 + 1),
+386            (19..MSG.len() as u64, false)
+387        );
+388        assert_eq!(
+389            buf.poll_transmit(58),
+390            (MSG.len() as u64..MSG.len() as u64, true)
+391        );
+392    }
+393
+394    #[test]
+395    fn reserves_encoded_offset() {
+396        let mut buf = SendBuffer::new();
+397
+398        // Pretend we have more than 1 GB of data in the buffer
+399        let chunk: Bytes = Bytes::from_static(&[0; 1024 * 1024]);
+400        for _ in 0..1025 {
+401            buf.write(chunk.clone());
+402        }
+403
+404        const SIZE1: u64 = 64;
+405        const SIZE2: u64 = 16 * 1024;
+406        const SIZE3: u64 = 1024 * 1024 * 1024;
+407
+408        // Offset 0 requires no space
+409        assert_eq!(buf.poll_transmit(16), (0..16, false));
+410        buf.retransmit(0..16);
+411        assert_eq!(buf.poll_transmit(16), (0..16, false));
+412        let mut transmitted = 16u64;
+413
+414        // Offset 16 requires 1 byte
+415        assert_eq!(
+416            buf.poll_transmit((SIZE1 - transmitted + 1) as usize),
+417            (transmitted..SIZE1, false)
+418        );
+419        buf.retransmit(transmitted..SIZE1);
+420        assert_eq!(
+421            buf.poll_transmit((SIZE1 - transmitted + 1) as usize),
+422            (transmitted..SIZE1, false)
+423        );
+424        transmitted = SIZE1;
+425
+426        // Offset 64 requires 2 bytes
+427        assert_eq!(
+428            buf.poll_transmit((SIZE2 - transmitted + 2) as usize),
+429            (transmitted..SIZE2, false)
+430        );
+431        buf.retransmit(transmitted..SIZE2);
+432        assert_eq!(
+433            buf.poll_transmit((SIZE2 - transmitted + 2) as usize),
+434            (transmitted..SIZE2, false)
+435        );
+436        transmitted = SIZE2;
+437
+438        // Offset 16384 requires requires 4 bytes
+439        assert_eq!(
+440            buf.poll_transmit((SIZE3 - transmitted + 4) as usize),
+441            (transmitted..SIZE3, false)
+442        );
+443        buf.retransmit(transmitted..SIZE3);
+444        assert_eq!(
+445            buf.poll_transmit((SIZE3 - transmitted + 4) as usize),
+446            (transmitted..SIZE3, false)
+447        );
+448        transmitted = SIZE3;
+449
+450        // Offset 1GB requires 8 bytes
+451        assert_eq!(
+452            buf.poll_transmit(chunk.len() + 8),
+453            (transmitted..transmitted + chunk.len() as u64, false)
+454        );
+455        buf.retransmit(transmitted..transmitted + chunk.len() as u64);
+456        assert_eq!(
+457            buf.poll_transmit(chunk.len() + 8),
+458            (transmitted..transmitted + chunk.len() as u64, false)
+459        );
+460    }
+461
+462    /// tests that large segments are copied as-is in the SendBuffer
+463    #[test]
+464    fn multiple_large_segments() {
+465        // this must be bigger than MAX_COMBINE so we don't get writes coalesced.
+466        const N: usize = 2000;
+467        const K: u64 = N as u64;
+468        fn dup(data: &[u8]) -> Bytes {
+469            let mut buf = BytesMut::with_capacity(data.len() * N);
+470            for c in data {
+471                for _ in 0..N {
+472                    buf.put_u8(*c);
+473                }
+474            }
+475            buf.freeze()
+476        }
+477
+478        fn same(a: &[u8], b: &[u8]) -> bool {
+479            // surprisingly, eq also checks the fat pointer metadata aka length
+480            std::ptr::eq(a.as_ptr(), b.as_ptr())
+481        }
+482
+483        let mut buf = SendBuffer::new();
+484        let msg: Bytes = dup(b"Hello, world!");
+485        let msg_len: u64 = msg.len() as u64;
+486
+487        let seg1: Bytes = dup(b"He");
+488        buf.write(seg1.clone());
+489        let seg2: Bytes = dup(b"llo,");
+490        buf.write(seg2.clone());
+491        let seg3: Bytes = dup(b" w");
+492        buf.write(seg3.clone());
+493        let seg4: Bytes = dup(b"o");
+494        buf.write(seg4.clone());
+495        let seg5: Bytes = dup(b"rld!");
+496        buf.write(seg5.clone());
+497        assert_eq!(aggregate_unacked(&buf), msg);
+498        // Check that the segments were stored as-is
+499        assert!(same(buf.get(0..5 * K), &seg1));
+500        assert!(same(buf.get(2 * K..8 * K), &seg2));
+501        assert!(same(buf.get(6 * K..8 * K), &seg3));
+502        assert!(same(buf.get(8 * 2000..msg_len), &seg4));
+503        assert!(same(buf.get(9 * 2000..msg_len), &seg5));
+504        // Now drain the segments
+505        buf.ack(0..K);
+506        assert_eq!(aggregate_unacked(&buf), &msg[N..]);
+507        buf.ack(0..3 * K);
+508        assert_eq!(aggregate_unacked(&buf), &msg[3 * N..]);
+509        buf.ack(3 * K..5 * K);
+510        assert_eq!(aggregate_unacked(&buf), &msg[5 * N..]);
+511        // ack with gap, doesn't free anything
+512        buf.ack(7 * K..9 * K);
+513        assert_eq!(aggregate_unacked(&buf), &msg[5 * N..]);
+514        // fill the gap, free up to 9 K
+515        buf.ack(4 * K..7 * K);
+516        assert_eq!(aggregate_unacked(&buf), &msg[9 * N..]);
+517        // ack all
+518        buf.ack(0..msg_len);
+519        assert_eq!(aggregate_unacked(&buf), &[] as &[u8]);
+520    }
+521
+522    #[test]
+523    fn retransmit() {
+524        let mut buf = SendBuffer::new();
+525        const MSG: &[u8] = b"Hello, world with extra data!";
+526        buf.write(MSG);
+527        // Transmit two frames
+528        assert_eq!(buf.poll_transmit(16), (0..16, false));
+529        assert_eq!(buf.poll_transmit(16), (16..23, true));
+530        // Lose the first, but not the second
+531        buf.retransmit(0..16);
+532        // Ensure we only retransmit the lost frame, then continue sending fresh data
+533        assert_eq!(buf.poll_transmit(16), (0..16, false));
+534        assert_eq!(buf.poll_transmit(16), (23..MSG.len() as u64, true));
+535        // Lose the second frame
+536        buf.retransmit(16..23);
+537        assert_eq!(buf.poll_transmit(16), (16..23, true));
+538    }
+539
+540    #[test]
+541    fn ack() {
+542        let mut buf = SendBuffer::new();
+543        const MSG: &[u8] = b"Hello, world!";
+544        buf.write(MSG);
+545        assert_eq!(buf.poll_transmit(16), (0..8, true));
+546        buf.ack(0..8);
+547        assert_eq!(aggregate_unacked(&buf), &MSG[8..]);
+548    }
+549
+550    #[test]
+551    fn reordered_ack() {
+552        let mut buf = SendBuffer::new();
+553        const MSG: &[u8] = b"Hello, world with extra data!";
+554        buf.write(MSG);
+555        assert_eq!(buf.poll_transmit(16), (0..16, false));
+556        assert_eq!(buf.poll_transmit(16), (16..23, true));
+557        buf.ack(16..23);
+558        assert_eq!(aggregate_unacked(&buf), MSG);
+559        buf.ack(0..16);
+560        assert_eq!(aggregate_unacked(&buf), &MSG[23..]);
+561        assert!(buf.acks.is_empty());
+562    }
+563
+564    fn aggregate_unacked(buf: &SendBuffer) -> Vec<u8> {
+565        buf.data.to_vec()
+566    }
+567
+568    #[test]
+569    #[should_panic(expected = "Requested range is outside of buffered data")]
+570    fn send_buffer_get_out_of_range() {
+571        let data = SendBufferData::default();
+572        data.get(0..1);
+573    }
+574
+575    #[test]
+576    #[should_panic(expected = "Requested range is outside of buffered data")]
+577    fn send_buffer_get_into_out_of_range() {
+578        let data = SendBufferData::default();
+579        let mut buf = Vec::new();
+580        data.get_into(0..1, &mut buf);
+581    }
+582}
+583
+584#[cfg(all(test, not(target_family = "wasm")))]
+585mod proptests {
+586    use super::*;
+587
+588    use proptest::prelude::*;
+589    use test_strategy::{Arbitrary, proptest};
+590    use crate::tests::subscribe;
+591    use tracing::trace;
+592
+593    #[derive(Debug, Clone, Arbitrary)]
+594    enum Op {
+595        // write the given bytes
+596        Write(#[strategy(proptest::collection::vec(any::<u8>(), 0..1024))] Vec<u8>),
+597        // ack a random range
+598        Ack(Range<u64>),
+599        // retransmit a random range
+600        Retransmit(Range<u64>),
+601        // poll_transmit with the given max len
+602        PollTransmit(#[strategy(16usize..1024)] usize),
+603    }
+604
+605    /// Map a range into a target range
+606    ///
+607    /// If the target range is empty, it will be returned as is.
+608    /// For a non-empty target range, 0 in the input range will be mapped to
+609    /// the start of the target range, and the input range will wrap around
+610    /// the target range as needed.
+611    fn map_range(input: Range<u64>, target: Range<u64>) -> Range<u64> {
+612        if target.is_empty() {
+613            return target;
+614        }
+615        let size = target.end - target.start;
+616        let a = target.start + (input.start % size);
+617        let b = target.start + (input.end % size);
+618        a.min(b)..a.max(b)
+619    }
+620
+621    #[proptest]
+622    fn send_buffer_matches_reference(
+623        #[strategy(proptest::collection::vec(any::<Op>(), 1..100))] ops: Vec<Op>,
+624    ) {
+625        let _guard = subscribe();
+626        let mut sb = SendBuffer::new();
+627        // all data written to the send buffer
+628        let mut buf = Vec::new();
+629        // max offset that has been returned by poll_transmit
+630        let mut max_send_offset = 0u64;
+631        // max offset up to which data has been fully acked
+632        let mut max_full_send_offset = 0u64;
+633        trace!("");
+634        for op in ops {
+635            match op {
+636                Op::Write(data) => {
+637                    trace!("Op::Write({})", data.len());
+638                    buf.extend_from_slice(&data);
+639                    sb.write(Bytes::from(data));
+640                }
+641                Op::Ack(range) => {
+642                    // we can only get acks for data that has been sent
+643                    let range = map_range(range, 0..max_send_offset);
+644                    // update fully acked range
+645                    if range.contains(&max_full_send_offset) {
+646                        max_full_send_offset = range.end;
+647                    }
+648                    trace!("Op::Ack({:?})", range);
+649                    sb.ack(range);
+650                }
+651                Op::Retransmit(range) => {
+652                    // we can only get retransmits for data that has been sent
+653                    let range = map_range(range, 0..max_send_offset);
+654                    trace!("Op::Retransmit({:?})", range);
+655                    sb.retransmit(range);
+656                }
+657                Op::PollTransmit(max_len) => {
+658                    trace!("Op::PollTransmit({})", max_len);
+659                    let (range, _partial) = sb.poll_transmit(max_len);
+660                    max_send_offset = max_send_offset.max(range.end);
+661                    assert!(
+662                        range.start >= max_full_send_offset,
+663                        "poll_transmit returned already fully acked data: range={:?}, max_full_send_offset={}",
+664                        range,
+665                        max_full_send_offset
+666                    );
+667
+668                    let mut t1 = Vec::new();
+669                    sb.get_into(range.clone(), &mut t1);
+670
+671                    let mut t2 = Vec::new();
+672                    t2.extend_from_slice(&buf[range.start as usize..range.end as usize]);
+673
+674                    assert_eq!(t1, t2, "Data mismatch for range {:?}", range);
+675                }
+676            }
+677        }
+678        // Drain all remaining data
+679        trace!("Op::Retransmit({:?})", 0..max_send_offset);
+680        sb.retransmit(0..max_send_offset);
+681        loop {
+682            trace!("Op::PollTransmit({})", 1024);
+683            let (range, _partial) = sb.poll_transmit(1024);
+684            if range.is_empty() {
+685                break;
+686            }
+687            trace!("Op::Ack({:?})", range);
+688            sb.ack(range);
+689        }
+690        assert!(
+691            sb.is_fully_acked(),
+692            "SendBuffer not fully acked at end of ops"
+693        );
+694    }
+695}
+696
+697#[cfg(feature = "bench")]
+698pub mod send_buffer_benches {
+699    //! Bench fns for SendBuffer
+700    //!
+701    //! These are defined here and re-exported via `bench_exports` in lib.rs,
+702    //! so we can access the private `SendBuffer` struct.
+703    use bytes::Bytes;
+704    use criterion::Criterion;
+705
+706    use super::SendBuffer;
+707
+708    /// Pathological case: many segments, get from end
+709    pub fn get_into_many_segments(criterion: &mut Criterion) {
+710        let mut group = criterion.benchmark_group("get_into_many_segments");
+711        let mut buf = SendBuffer::new();
+712
+713        const SEGMENTS: u64 = 10000;
+714        const SEGMENT_SIZE: u64 = 10;
+715        const PACKET_SIZE: u64 = 1200;
+716        const BYTES: u64 = SEGMENTS * SEGMENT_SIZE;
+717
+718        // 10000 segments of 10 bytes each = 100KB total (same data size)
+719        for i in 0..SEGMENTS {
+720            buf.write(Bytes::from(vec![i as u8; SEGMENT_SIZE as usize]));
+721        }
+722
+723        let mut tgt = Vec::with_capacity(PACKET_SIZE as usize);
+724        group.bench_function("get_into", |b| {
+725            b.iter(|| {
+726                // Get from end (very slow - scans through all 1000 segments)
+727                tgt.clear();
+728                buf.get_into(BYTES - PACKET_SIZE..BYTES, std::hint::black_box(&mut tgt));
+729            });
+730        });
+731    }
+732
+733    /// Get segments in the old way, using a loop of get calls
+734    pub fn get_loop_many_segments(criterion: &mut Criterion) {
+735        let mut group = criterion.benchmark_group("get_loop_many_segments");
+736        let mut buf = SendBuffer::new();
+737
+738        const SEGMENTS: u64 = 10000;
+739        const SEGMENT_SIZE: u64 = 10;
+740        const PACKET_SIZE: u64 = 1200;
+741        const BYTES: u64 = SEGMENTS * SEGMENT_SIZE;
+742
+743        // 10000 segments of 10 bytes each = 100KB total (same data size)
+744        for i in 0..SEGMENTS {
+745            buf.write(Bytes::from(vec![i as u8; SEGMENT_SIZE as usize]));
+746        }
+747
+748        let mut tgt = Vec::with_capacity(PACKET_SIZE as usize);
+749        group.bench_function("get_loop", |b| {
+750            b.iter(|| {
+751                // Get from end (very slow - scans through all 1000 segments)
+752                tgt.clear();
+753                let mut range = BYTES - PACKET_SIZE..BYTES;
+754                while range.start < range.end {
+755                    let slice = std::hint::black_box(buf.get(range.clone()));
+756                    range.start += slice.len() as u64;
+757                    tgt.extend_from_slice(slice);
+758                }
+759            });
+760        });
+761    }
+762}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/spaces.rs.html b/pr/672/docs/src/noq_proto/connection/spaces.rs.html new file mode 100644 index 000000000..3f3a3fa55 --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/spaces.rs.html @@ -0,0 +1,1595 @@ +spaces.rs - source

noq_proto/connection/
spaces.rs

1use std::{
+2    cmp,
+3    collections::{BTreeMap, BTreeSet, VecDeque},
+4    mem,
+5    ops::{Bound, Index, IndexMut},
+6};
+7
+8use rand::{CryptoRng, RngExt};
+9use rustc_hash::{FxHashMap, FxHashSet};
+10use sorted_index_buffer::SortedIndexBuffer;
+11use tracing::trace;
+12
+13use super::PathId;
+14use crate::{
+15    Dir, Duration, FourTuple, Instant, StreamId, TransportError, TransportErrorCode, VarInt,
+16    connection::StreamsState,
+17    frame::{self, AddAddress, RemoveAddress},
+18    packet::SpaceId,
+19    range_set::ArrayRangeSet,
+20    shared::IssuedCid,
+21};
+22
+23pub(super) struct PacketSpace {
+24    /// Data to send
+25    pub(super) pending: Retransmits,
+26
+27    /// Multipath packet number spaces
+28    ///
+29    /// Each [`PathId`] has it's own [`PacketNumberSpace`].  Only the [`SpaceId::Data`] can
+30    /// have multiple packet number spaces, the other spaces only have a number space for
+31    /// `PathId::ZERO`, which is populated at creation.
+32    pub(super) number_spaces: BTreeMap<PathId, PacketNumberSpace>,
+33}
+34
+35impl PacketSpace {
+36    pub(super) fn new(now: Instant, space: SpaceId, rng: &mut (impl CryptoRng + ?Sized)) -> Self {
+37        let number_space_0 = PacketNumberSpace::new(now, space, rng);
+38        Self {
+39            pending: Retransmits::default(),
+40            number_spaces: BTreeMap::from([(PathId::ZERO, number_space_0)]),
+41        }
+42    }
+43
+44    #[cfg(test)]
+45    pub(super) fn new_deterministic(now: Instant, space: SpaceId) -> Self {
+46        let number_space_0 = PacketNumberSpace::new_deterministic(now, space);
+47        Self {
+48            pending: Retransmits::default(),
+49            number_spaces: BTreeMap::from([(PathId::ZERO, number_space_0)]),
+50        }
+51    }
+52
+53    /// Returns the [`PacketNumberSpace`] for a path
+54    ///
+55    /// When multipath is disabled use [`PathId::ZERO`].
+56    pub(super) fn path_space(&self, path_id: PathId) -> Option<&PacketNumberSpace> {
+57        self.number_spaces.get(&path_id)
+58    }
+59
+60    /// Returns a mutable reference to the [`PacketNumberSpace`] for a path
+61    ///
+62    /// When multipath is disabled use [`PathId::ZERO`].
+63    pub(super) fn path_space_mut(&mut self, path_id: PathId) -> Option<&mut PacketNumberSpace> {
+64        self.number_spaces.get_mut(&path_id)
+65    }
+66
+67    /// Returns the [`PacketNumberSpace`] for a path
+68    ///
+69    /// When multipath is disabled use `PathId::ZERO`.
+70    // TODO(flub): Note that this only exists as `&mut self` because it creates a new
+71    //    [`PacketNumberSpace`] if one is not yet available for a path.  This forces a few
+72    //    more `&mut` references to users than strictly needed.  An alternative would be to
+73    //    return an Option but that would need to be handled for all callers.  This could be
+74    //    worth exploring once we have all the main multipath bits fitted.
+75    pub(super) fn for_path(&mut self, path: PathId) -> &mut PacketNumberSpace {
+76        self.number_spaces
+77            .get_mut(&path)
+78            .unwrap_or_else(|| panic!("PacketNumberSpace missing for {path}"))
+79    }
+80
+81    pub(super) fn iter_paths_mut(&mut self) -> impl Iterator<Item = &mut PacketNumberSpace> {
+82        self.number_spaces.values_mut()
+83    }
+84
+85    /// Queue data for a tail loss probe (or anti-amplification deadlock prevention) packet
+86    ///
+87    /// Probes are sent similarly to normal packets when an expected ACK has not arrived. We never
+88    /// deem a packet lost until we receive an ACK that should have included it, but if a trailing
+89    /// run of packets (or their ACKs) are lost, this might not happen in a timely fashion. We send
+90    /// probe packets to force an ACK, and exempt them from congestion control to prevent a deadlock
+91    /// when the congestion window is filled with lost tail packets.
+92    ///
+93    /// We prefer to send new data, to make the most efficient use of bandwidth. If there's no data
+94    /// waiting to be sent, then we retransmit in-flight data to reduce odds of loss. If there's no
+95    /// in-flight data either, we're probably a client guarding against a handshake
+96    /// anti-amplification deadlock and we just make something up.
+97    pub(super) fn queue_tail_loss_probe(
+98        &mut self,
+99        path_id: PathId,
+100        request_immediate_ack: bool,
+101        streams: &StreamsState,
+102    ) {
+103        if request_immediate_ack {
+104            // The probe should be ACKed without delay (should only be used in the Data space and
+105            // when the peer supports the acknowledgement frequency extension)
+106            self.for_path(path_id).immediate_ack_pending = true;
+107        }
+108
+109        // We prefer to send new data to make most efficient use of bandwidth.
+110        if !self.pending.is_empty(streams) {
+111            // There's real data to send here, no need to make something up
+112            return;
+113        }
+114
+115        // Retransmit data from the oldest in-flight data from any path
+116        for packet in self
+117            .number_spaces
+118            .values_mut()
+119            .flat_map(|s| s.sent_packets.values_mut())
+120        {
+121            if !packet.retransmits.is_empty(streams) {
+122                // Remove retransmitted data from the old packet so we don't end up retransmitting
+123                // it *again* even if the copy we're sending now gets acknowledged.
+124                self.pending |= mem::take(&mut packet.retransmits);
+125                return;
+126            }
+127        }
+128
+129        // Nothing new to send and nothing to retransmit, so fall back on a ping. This should only
+130        // happen in rare cases during the handshake when the server becomes blocked by
+131        // anti-amplification.
+132        if !self.for_path(path_id).immediate_ack_pending {
+133            self.for_path(path_id).ping_pending = true;
+134        }
+135    }
+136
+137    /// Whether there is anything to send in this space
+138    ///
+139    /// For the data space [`Connection::can_send_1rtt`] also needs to be consulted. Prefer
+140    /// to use [`Connection::space_can_send`] which handles this.
+141    ///
+142    /// [`Connection::can_send_1rtt`]: super::Connection::can_send_1rtt
+143    /// [`Connection::space_can_send`]: super::Connection::space_can_send
+144    pub(super) fn can_send(&self, path_id: PathId, streams: &StreamsState) -> SendableFrames {
+145        let acks = self
+146            .number_spaces
+147            .values()
+148            .any(|pns| pns.pending_acks.can_send());
+149        let space_specific = self
+150            .number_spaces
+151            .get(&path_id)
+152            .is_some_and(|s| s.ping_pending || s.immediate_ack_pending);
+153        let other = !self.pending.is_empty(streams);
+154        SendableFrames {
+155            acks,
+156            close: false,
+157            space_specific,
+158            other,
+159        }
+160    }
+161}
+162
+163impl Index<SpaceId> for [PacketSpace; 3] {
+164    type Output = PacketSpace;
+165    fn index(&self, space: SpaceId) -> &PacketSpace {
+166        &self.as_ref()[space as usize]
+167    }
+168}
+169
+170impl IndexMut<SpaceId> for [PacketSpace; 3] {
+171    fn index_mut(&mut self, space: SpaceId) -> &mut PacketSpace {
+172        &mut self.as_mut()[space as usize]
+173    }
+174}
+175
+176/// The three QUIC packet number space kinds
+177///
+178/// Unlike [`SpaceId`], this always has exactly three variants — it represents the
+179/// encryption level / space kind, not a specific packet number space identity.
+180#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+181pub(crate) enum SpaceKind {
+182    /// Initial packets (client and server).
+183    Initial = 0,
+184    /// Handshake packets.
+185    Handshake = 1,
+186    /// Data (1-RTT and 0-RTT)
+187    Data = 2,
+188}
+189
+190impl SpaceKind {
+191    /// Returns the encryption level for this space kind.
+192    pub(crate) fn encryption_level(self) -> super::EncryptionLevel {
+193        match self {
+194            Self::Initial => super::EncryptionLevel::Initial,
+195            Self::Handshake => super::EncryptionLevel::Handshake,
+196            Self::Data => super::EncryptionLevel::OneRtt,
+197        }
+198    }
+199}
+200
+201impl Index<SpaceKind> for [PacketSpace; 3] {
+202    type Output = PacketSpace;
+203    fn index(&self, space: SpaceKind) -> &PacketSpace {
+204        &self.as_ref()[space as usize]
+205    }
+206}
+207
+208impl IndexMut<SpaceKind> for [PacketSpace; 3] {
+209    fn index_mut(&mut self, space: SpaceKind) -> &mut PacketSpace {
+210        &mut self.as_mut()[space as usize]
+211    }
+212}
+213
+214/// The state of a single packet number space.
+215///
+216/// In RFC9000 there are 3 packet number spaces: Initial, Handshake and Data. In QUIC
+217/// Multipath there are multiple packet number spaces for Data, each identified by a
+218/// [`PathId`].
+219///
+220/// This contains the state for a packet number space which is not specific to the 4-tuple
+221/// this space is currently using. The 4-tuple specific state, like congestion controller,
+222/// pacing, ECN, MTU etc, is stored in [`PathData`].
+223///
+224/// Note that the `Initial`, `Handshake` and `Data(PathId(0))` space all share the same
+225/// [`PathData`].
+226///
+227/// You should access this via [`PacketSpace::for_path`].
+228///
+229/// [`PathData`]: super::paths::PathData
+230pub(super) struct PacketNumberSpace {
+231    /// Highest received packet number, if any
+232    pub(super) largest_received_packet_number: Option<u64>,
+233    /// The packet number of the next packet that will be sent, if any. In the Data space, the
+234    /// packet number stored here is sometimes skipped by [`PacketNumberFilter`] logic.
+235    pub(super) next_packet_number: u64,
+236    /// The largest packet number the remote peer acknowledged in an ACK frame.
+237    pub(super) largest_acked_packet_pn: Option<u64>,
+238    pub(super) largest_acked_packet_send_time: Instant,
+239    /// The highest-numbered ACK-eliciting packet we've sent
+240    pub(super) largest_ack_eliciting_sent: u64,
+241    /// Number of packets in `sent_packets` with numbers above `largest_ack_eliciting_sent`
+242    pub(super) unacked_non_ack_eliciting_tail: u64,
+243    /// Transmitted but not acked
+244    // We use a BTreeMap here so we can efficiently query by range on ACK and for loss detection
+245    pub(super) sent_packets: SortedIndexBuffer<SentPacket>,
+246    /// Packets that were deemed lost
+247    // Older packets are regularly removed in `Connection::drain_lost_packets`.
+248    pub(super) lost_packets: SortedIndexBuffer<LostPacket>,
+249    /// Number of explicit congestion notification codepoints seen on incoming packets
+250    pub(super) ecn_counters: frame::EcnCounts,
+251    /// Recent ECN counters sent by the peer in ACK frames
+252    ///
+253    /// Updated (and inspected) whenever we receive an ACK with a new highest acked packet
+254    /// number. Stored per-space to simplify verification, which would otherwise have difficulty
+255    /// distinguishing between ECN bleaching and counts having been updated by a near-simultaneous
+256    /// ACK already processed in another space.
+257    pub(super) ecn_feedback: frame::EcnCounts,
+258    /// A PING frame needs to be sent on this path
+259    pub(super) ping_pending: bool,
+260    /// An IMMEDIATE_ACK (draft-ietf-quic-ack-frequency) frame needs to be sent on this path
+261    pub(super) immediate_ack_pending: bool,
+262    /// Packet deduplicator
+263    pub(super) dedup: Dedup,
+264    /// Packet numbers to acknowledge
+265    pub(super) pending_acks: PendingAcks,
+266
+267    //
+268    // Loss Detection
+269    //
+270    /// The time the most recently sent retransmittable packet was sent.
+271    pub(super) time_of_last_ack_eliciting_packet: Option<Instant>,
+272    /// Earliest time when we might declare a packet lost.
+273    ///
+274    /// The time at which the earliest sent packet in this space will be considered lost
+275    /// based on exceeding the reordering window in time. Only set for packets numbered
+276    /// prior to a packet that has been acknowledged.
+277    pub(super) loss_time: Option<Instant>,
+278    /// Number of tail loss probes to send
+279    pub(super) loss_probes: u32,
+280
+281    /// Packet numbers to skip, only used in the data package space.
+282    pn_filter: Option<PacketNumberFilter>,
+283}
+284
+285impl PacketNumberSpace {
+286    pub(super) fn new(now: Instant, space: SpaceId, rng: &mut (impl CryptoRng + ?Sized)) -> Self {
+287        let pn_filter = match space {
+288            SpaceId::Initial | SpaceId::Handshake => None,
+289            SpaceId::Data => Some(PacketNumberFilter::new(rng)),
+290        };
+291        Self {
+292            largest_received_packet_number: None,
+293            next_packet_number: 0,
+294            largest_acked_packet_pn: None,
+295            largest_acked_packet_send_time: now,
+296            largest_ack_eliciting_sent: 0,
+297            unacked_non_ack_eliciting_tail: 0,
+298            sent_packets: SortedIndexBuffer::new(),
+299            lost_packets: SortedIndexBuffer::new(),
+300            ecn_counters: frame::EcnCounts::ZERO,
+301            ecn_feedback: frame::EcnCounts::ZERO,
+302            ping_pending: false,
+303            immediate_ack_pending: false,
+304            dedup: Default::default(),
+305            pending_acks: PendingAcks::new(),
+306            time_of_last_ack_eliciting_packet: None,
+307            loss_time: None,
+308            loss_probes: 0,
+309            pn_filter,
+310        }
+311    }
+312
+313    #[cfg(test)]
+314    fn new_deterministic(now: Instant, space: SpaceId) -> Self {
+315        let pn_filter = match space {
+316            SpaceId::Initial | SpaceId::Handshake => None,
+317            SpaceId::Data => Some(PacketNumberFilter::disabled()),
+318        };
+319        Self {
+320            largest_received_packet_number: None,
+321            next_packet_number: 0,
+322            largest_acked_packet_pn: None,
+323            largest_acked_packet_send_time: now,
+324            largest_ack_eliciting_sent: 0,
+325            unacked_non_ack_eliciting_tail: 0,
+326            sent_packets: SortedIndexBuffer::new(),
+327            lost_packets: SortedIndexBuffer::new(),
+328            ecn_counters: frame::EcnCounts::ZERO,
+329            ecn_feedback: frame::EcnCounts::ZERO,
+330            ping_pending: false,
+331            immediate_ack_pending: false,
+332            dedup: Default::default(),
+333            pending_acks: PendingAcks::new(),
+334            time_of_last_ack_eliciting_packet: None,
+335            loss_time: None,
+336            loss_probes: 0,
+337            pn_filter,
+338        }
+339    }
+340
+341    /// Get the next outgoing packet number in this space
+342    ///
+343    /// In the Data space, the connection's [`PacketNumberFilter`] must be used rather than calling
+344    /// this directly.
+345    pub(super) fn get_tx_number(&mut self, rng: &mut (impl CryptoRng + ?Sized)) -> u64 {
+346        // TODO: Handle packet number overflow gracefully
+347        assert!(self.next_packet_number < 2u64.pow(62));
+348        let mut pn = self.next_packet_number;
+349        self.next_packet_number += 1;
+350
+351        // Skip this number if the filter says so, only enabled in the data space
+352        if let Some(ref mut filter) = self.pn_filter
+353            && filter.skip_pn(pn, rng)
+354        {
+355            pn = self.next_packet_number;
+356            self.next_packet_number += 1;
+357        }
+358        pn
+359    }
+360
+361    pub(super) fn peek_tx_number(&mut self) -> u64 {
+362        let pn = self.next_packet_number;
+363        if let Some(ref filter) = self.pn_filter
+364            && pn == filter.next_skipped_packet_number
+365        {
+366            return pn + 1;
+367        }
+368        pn
+369    }
+370
+371    /// Checks whether a skipped packet number was ACKed.
+372    pub(super) fn check_ack(&self, range: std::ops::Range<u64>) -> Result<(), TransportError> {
+373        if let Some(ref filter) = self.pn_filter
+374            && filter
+375                .prev_skipped_packet_number
+376                .is_some_and(|pn| range.contains(&pn))
+377        {
+378            return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked"));
+379        }
+380        Ok(())
+381    }
+382
+383    /// Verifies sanity of an ECN block and returns whether congestion was encountered.
+384    pub(super) fn detect_ecn(
+385        &mut self,
+386        newly_acked: u64,
+387        ecn: frame::EcnCounts,
+388    ) -> Result<bool, &'static str> {
+389        let ect0_increase = ecn
+390            .ect0
+391            .checked_sub(self.ecn_feedback.ect0)
+392            .ok_or("peer ECT(0) count regression")?;
+393        let ect1_increase = ecn
+394            .ect1
+395            .checked_sub(self.ecn_feedback.ect1)
+396            .ok_or("peer ECT(1) count regression")?;
+397        let ce_increase = ecn
+398            .ce
+399            .checked_sub(self.ecn_feedback.ce)
+400            .ok_or("peer CE count regression")?;
+401        let total_increase = ect0_increase + ect1_increase + ce_increase;
+402        if total_increase < newly_acked {
+403            return Err("ECN bleaching");
+404        }
+405        if (ect0_increase + ce_increase) < newly_acked || ect1_increase != 0 {
+406            return Err("ECN corruption");
+407        }
+408        // If total_increase > newly_acked (which happens when ACKs are lost), this is required by
+409        // the draft so that long-term drift does not occur. If =, then the only question is whether
+410        // to count CE packets as CE or ECT0. Recording them as CE is more consistent and keeps the
+411        // congestion check obvious.
+412        self.ecn_feedback = ecn;
+413        Ok(ce_increase != 0)
+414    }
+415
+416    /// Stop tracking sent packet `number`, and return what we knew about it
+417    pub(super) fn take(&mut self, number: u64) -> Option<SentPacket> {
+418        let packet = self.sent_packets.remove(number)?;
+419        if !packet.ack_eliciting && number > self.largest_ack_eliciting_sent {
+420            self.unacked_non_ack_eliciting_tail =
+421                self.unacked_non_ack_eliciting_tail.checked_sub(1).unwrap();
+422        }
+423        Some(packet)
+424    }
+425
+426    /// May return a packet that should be forgotten
+427    pub(super) fn sent(&mut self, number: u64, packet: SentPacket) -> Option<SentPacket> {
+428        // Retain state for at most this many non-ACK-eliciting packets sent after the most recently
+429        // sent ACK-eliciting packet. We're never guaranteed to receive an ACK for those, and we
+430        // can't judge them as lost without an ACK, so to limit memory in applications which receive
+431        // packets but don't send ACK-eliciting data for long periods use we must eventually start
+432        // forgetting about them, although it might also be reasonable to just kill the connection
+433        // due to weird peer behavior.
+434        const MAX_UNACKED_NON_ACK_ELICTING_TAIL: u64 = 1_000;
+435
+436        let mut forgotten = None;
+437        if packet.ack_eliciting {
+438            self.unacked_non_ack_eliciting_tail = 0;
+439            self.largest_ack_eliciting_sent = number;
+440        } else if self.unacked_non_ack_eliciting_tail > MAX_UNACKED_NON_ACK_ELICTING_TAIL {
+441            let oldest_after_ack_eliciting = self
+442                .sent_packets
+443                .keys_range((
+444                    Bound::Excluded(self.largest_ack_eliciting_sent),
+445                    Bound::Unbounded,
+446                ))
+447                .next()
+448                .unwrap();
+449            // Per https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types,
+450            // non-ACK-eliciting packets must only contain PADDING, ACK, and CONNECTION_CLOSE
+451            // frames, which require no special handling on ACK or loss beyond removal from
+452            // in-flight counters if padded.
+453            let packet = self
+454                .sent_packets
+455                .remove(oldest_after_ack_eliciting)
+456                .unwrap();
+457            debug_assert!(!packet.ack_eliciting);
+458            forgotten = Some(packet);
+459        } else {
+460            self.unacked_non_ack_eliciting_tail += 1;
+461        }
+462
+463        self.sent_packets.insert(number, packet);
+464        forgotten
+465    }
+466
+467    /// Whether any congestion-controlled packets in this space are not yet acknowledged or lost
+468    pub(super) fn has_in_flight(&self) -> bool {
+469        // The number of non-congestion-controlled (i.e. size == 0) packets in flight at a time
+470        // should be small, since otherwise congestion control wouldn't be effective. Therefore,
+471        // this shouldn't need to visit many packets before finishing one way or another.
+472        self.sent_packets.values().any(|x| x.size != 0)
+473    }
+474}
+475
+476/// Represents one or more packets subject to retransmission
+477#[derive(Debug, Clone)]
+478pub(super) struct SentPacket {
+479    /// [`PathData::generation`](super::PathData::generation) of the path on which this packet was sent
+480    pub(super) path_generation: u64,
+481    /// The time the packet was sent.
+482    pub(super) time_sent: Instant,
+483    /// The number of bytes sent in the packet, not including UDP or IP overhead, but including QUIC
+484    /// framing overhead. Zero if this packet is not counted towards congestion control, i.e. not an
+485    /// "in flight" packet.
+486    pub(super) size: u16,
+487    /// Whether an acknowledgement is expected directly in response to this packet.
+488    pub(super) ack_eliciting: bool,
+489    /// The largest packet number acknowledged by this packet
+490    pub(super) largest_acked: FxHashMap<PathId, u64>,
+491    /// Data which needs to be retransmitted in case the packet is lost.
+492    /// The data is boxed to minimize `SentPacket` size for the typical case of
+493    /// packets only containing ACKs and STREAM frames.
+494    pub(super) retransmits: ThinRetransmits,
+495    /// Metadata for stream frames in a packet
+496    ///
+497    /// The actual application data is stored with the stream state.
+498    pub(super) stream_frames: frame::StreamMetaVec,
+499}
+500
+501/// Represents one or more packets that are deemed lost.
+502#[derive(Debug)]
+503pub(super) struct LostPacket {
+504    /// The time the packet was sent.
+505    pub(super) time_sent: Instant,
+506}
+507
+508/// Retransmittable data queue
+509#[allow(unreachable_pub)] // fuzzing only
+510#[derive(Debug, Default, Clone)]
+511pub struct Retransmits {
+512    pub(super) max_data: bool,
+513    pub(super) max_stream_id: [bool; 2],
+514    pub(super) streams_blocked: [bool; 2],
+515    pub(super) reset_stream: Vec<(StreamId, VarInt)>,
+516    pub(super) stop_sending: Vec<frame::StopSending>,
+517    pub(super) max_stream_data: FxHashSet<StreamId>,
+518    pub(super) crypto: VecDeque<frame::Crypto>,
+519    pub(super) new_cids: PendingNewCids,
+520    pub(super) retire_cids: Vec<(PathId, u64)>,
+521    pub(super) ack_frequency: bool,
+522    pub(super) handshake_done: bool,
+523    pub(super) observed_addr: bool,
+524    /// Whether we should inform the peer we will allow higher [`PathId`]s.
+525    pub(super) max_path_id: bool,
+526    /// Whether we should inform the peer that their max [`PathId`] is blocking our attempt to open
+527    /// new paths.
+528    // TODO(@divma): we need logic to prevent sending this more than once after being ack-d once
+529    pub(super) paths_blocked: bool,
+530    /// For each enqueued NEW_TOKEN frame, a copy of the path's remote address
+531    ///
+532    /// There are 2 reasons this is unusual:
+533    ///
+534    /// - If the path changes, NEW_TOKEN frames bound for the old path are not retransmitted on the
+535    ///   new path. That is why this field stores the remote address: so that ones for old paths
+536    ///   can be filtered out.
+537    /// - If a token is lost, a new randomly generated token is re-transmitted, rather than the
+538    ///   original. This is so that if both transmissions are received, the client won't risk
+539    ///   sending the same token twice. That is why this field does _not_ store any actual token.
+540    ///
+541    /// It is true that a QUIC endpoint will only want to effectively have NEW_TOKEN frames
+542    /// enqueued for its current path at a given point in time. Based on that, we could conceivably
+543    /// change this from a vector to an `Option<(FourTuple, usize)>` or just a `usize` or
+544    /// something. However, due to the architecture of noq, it is considerably simpler to not do
+545    /// that; consider what such a change would mean for implementing `BitOrAssign` on Self.
+546    pub(super) new_tokens: Vec<FourTuple>,
+547    /// Paths which need to be abandoned
+548    pub(super) path_abandon: BTreeMap<PathId, TransportErrorCode>,
+549    /// If a [`frame::PathStatusAvailable`] and [`frame::PathStatusBackup`] need to be sent for a path
+550    pub(super) path_status: BTreeSet<PathId>,
+551    /// If a PATH_CIDS_BLOCKED frame needs to be sent for a path
+552    pub(super) path_cids_blocked: BTreeSet<PathId>,
+553
+554    // Nat traversal data
+555    /// Addresses to report in `ADD_ADDRESS` frames
+556    pub(super) add_address: BTreeSet<AddAddress>,
+557    /// Address IDs to remove in `REMOVE_ADDRESS` frames
+558    pub(super) remove_address: BTreeSet<RemoveAddress>,
+559    /// Round and local addresses to advertise in `REACH_OUT` frames
+560    pub(super) reach_out: PendingReachOutFrames,
+561}
+562
+563impl Retransmits {
+564    pub(super) fn is_empty(&self, streams: &StreamsState) -> bool {
+565        let Self {
+566            max_data,
+567            max_stream_id,
+568            streams_blocked,
+569            reset_stream,
+570            stop_sending,
+571            max_stream_data,
+572            crypto,
+573            new_cids,
+574            retire_cids,
+575            ack_frequency,
+576            handshake_done,
+577            observed_addr,
+578            max_path_id,
+579            paths_blocked,
+580            new_tokens,
+581            path_abandon,
+582            path_status,
+583            path_cids_blocked,
+584            add_address,
+585            remove_address,
+586            reach_out,
+587        } = &self;
+588        !max_data
+589            && !max_stream_id.iter().any(|x| *x)
+590            && !streams_blocked.iter().any(|x| *x)
+591            && reset_stream.is_empty()
+592            && stop_sending.is_empty()
+593            && max_stream_data
+594                .iter()
+595                .all(|&id| !streams.can_send_flow_control(id))
+596            && crypto.is_empty()
+597            && new_cids.is_empty()
+598            && retire_cids.is_empty()
+599            && !ack_frequency
+600            && !handshake_done
+601            && !observed_addr
+602            && !max_path_id
+603            && !paths_blocked
+604            && new_tokens.is_empty()
+605            && path_abandon.is_empty()
+606            && path_status.is_empty()
+607            && path_cids_blocked.is_empty()
+608            && add_address.is_empty()
+609            && remove_address.is_empty()
+610            && reach_out.is_empty()
+611    }
+612}
+613
+614impl ::std::ops::BitOrAssign for Retransmits {
+615    fn bitor_assign(&mut self, rhs: Self) {
+616        let Self {
+617            max_data,
+618            max_stream_id,
+619            streams_blocked,
+620            reset_stream,
+621            stop_sending,
+622            max_stream_data,
+623            crypto,
+624            new_cids,
+625            retire_cids,
+626            ack_frequency,
+627            handshake_done,
+628            observed_addr,
+629            max_path_id,
+630            paths_blocked,
+631            new_tokens,
+632            mut path_abandon,
+633            mut path_status,
+634            mut path_cids_blocked,
+635            add_address,
+636            remove_address,
+637            mut reach_out,
+638        } = rhs;
+639
+640        // We reduce in-stream head-of-line blocking by queueing retransmits before other data for
+641        // STREAM and CRYPTO frames.
+642        self.max_data |= max_data;
+643        for dir in Dir::iter() {
+644            self.max_stream_id[dir as usize] |= max_stream_id[dir as usize];
+645            self.streams_blocked[dir as usize] |= streams_blocked[dir as usize];
+646        }
+647        self.reset_stream.extend_from_slice(&reset_stream);
+648        self.stop_sending.extend_from_slice(&stop_sending);
+649        self.max_stream_data.extend(&max_stream_data);
+650        for crypto in crypto.into_iter().rev() {
+651            self.crypto.push_front(crypto);
+652        }
+653        self.new_cids.extend(&new_cids);
+654        self.retire_cids.extend(retire_cids);
+655        self.ack_frequency |= ack_frequency;
+656        self.handshake_done |= handshake_done;
+657        self.observed_addr |= observed_addr;
+658        self.max_path_id |= max_path_id;
+659        self.paths_blocked |= paths_blocked;
+660        self.new_tokens.extend_from_slice(&new_tokens);
+661        self.path_abandon.append(&mut path_abandon);
+662        self.path_status.append(&mut path_status);
+663        self.path_cids_blocked.append(&mut path_cids_blocked);
+664        self.add_address.extend(add_address.iter().copied());
+665        self.remove_address.extend(remove_address.iter().copied());
+666        self.reach_out.append(&mut reach_out);
+667    }
+668}
+669
+670impl ::std::ops::BitOrAssign<ThinRetransmits> for Retransmits {
+671    fn bitor_assign(&mut self, rhs: ThinRetransmits) {
+672        let ThinRetransmits { retransmits } = rhs;
+673        if let Some(retransmits) = retransmits {
+674            self.bitor_assign(*retransmits)
+675        }
+676    }
+677}
+678
+679impl ::std::iter::FromIterator<Self> for Retransmits {
+680    fn from_iter<T>(iter: T) -> Self
+681    where
+682        T: IntoIterator<Item = Self>,
+683    {
+684        let mut result = Self::default();
+685        for packet in iter {
+686            result |= packet;
+687        }
+688        result
+689    }
+690}
+691
+692/// The queue of new CIDs to be transmitted to the peer.
+693///
+694/// This queue is always sorted, so that popping off the last item is always the lowest
+695/// sequence number of the lowest path ID. Which is the CID you want to be issued next.
+696///
+697/// This is but a newtype over a `Vec` to enforce the sorted invariant.
+698#[derive(Clone, Debug, Default)]
+699pub(super) struct PendingNewCids {
+700    /// The CIDs themselves.
+701    cids: Vec<IssuedCid>,
+702    /// Whether [`Self::cids`] is sorted or not.
+703    sorted: bool,
+704}
+705
+706impl PendingNewCids {
+707    /// Inserts an issued CID into the queue.
+708    pub(super) fn push(&mut self, cid: IssuedCid) {
+709        self.cids.push(cid);
+710        self.sorted = false;
+711    }
+712
+713    /// Pops the next issued CID to transmit from the queue.
+714    pub(super) fn pop(&mut self) -> Option<IssuedCid> {
+715        if !std::mem::replace(&mut self.sorted, true) {
+716            self.cids
+717                .sort_by_key(|cid| cmp::Reverse((cid.path_id, cid.sequence)));
+718        }
+719        self.cids.pop()
+720    }
+721
+722    pub(super) fn is_empty(&self) -> bool {
+723        self.cids.is_empty()
+724    }
+725
+726    pub(super) fn extend(&mut self, other: &Self) {
+727        self.cids.extend(&other.cids);
+728        self.sorted = false;
+729    }
+730
+731    pub(super) fn retain<F>(&mut self, f: F)
+732    where
+733        F: FnMut(&IssuedCid) -> bool,
+734    {
+735        self.cids.retain(f);
+736    }
+737}
+738
+739/// Logically a Vec of REACH_OUT frames queued for transmit.
+740///
+741/// This keeps track of the highest round ID and automatically drops frames with a lower
+742/// round ID.
+743///
+744/// The API is directly modelled on [`Vec`].
+745#[derive(Debug, Default, Clone)]
+746pub(crate) struct PendingReachOutFrames {
+747    /// The round ID of the REACH_OUT frames currently pending.
+748    round: VarInt,
+749    /// The REACH_OUT frames, always all having the same round ID.
+750    frames: Vec<frame::ReachOut>,
+751}
+752
+753impl PendingReachOutFrames {
+754    pub(crate) fn len(&self) -> usize {
+755        self.frames.len()
+756    }
+757
+758    pub(crate) fn is_empty(&self) -> bool {
+759        self.frames.is_empty()
+760    }
+761
+762    pub(crate) fn push(&mut self, frame: frame::ReachOut) {
+763        if frame.round < self.round {
+764            return;
+765        } else if frame.round > self.round {
+766            self.round = frame.round;
+767            self.frames.clear();
+768        }
+769        self.frames.push(frame);
+770    }
+771
+772    pub(crate) fn append(&mut self, other: &mut Self) {
+773        if other.round < self.round {
+774            other.frames.clear();
+775            return;
+776        } else if other.round > self.round {
+777            self.round = other.round;
+778            self.frames.clear();
+779        }
+780        self.frames.append(&mut other.frames);
+781    }
+782
+783    pub(crate) fn pop_if(
+784        &mut self,
+785        predicate: impl FnOnce(&mut frame::ReachOut) -> bool,
+786    ) -> Option<frame::ReachOut> {
+787        self.frames.pop_if(predicate)
+788    }
+789}
+790
+791impl FromIterator<frame::ReachOut> for PendingReachOutFrames {
+792    fn from_iter<T: IntoIterator<Item = frame::ReachOut>>(iter: T) -> Self {
+793        let iter = iter.into_iter();
+794        let size_hint = iter.size_hint();
+795        let mut this = Self {
+796            round: Default::default(),
+797            frames: Vec::with_capacity(size_hint.1.unwrap_or(size_hint.0)),
+798        };
+799        for frame in iter {
+800            this.push(frame);
+801        }
+802        this
+803    }
+804}
+805
+806/// A variant of `Retransmits` which only allocates storage when required
+807#[derive(Debug, Default, Clone)]
+808pub(super) struct ThinRetransmits {
+809    retransmits: Option<Box<Retransmits>>,
+810}
+811
+812impl ThinRetransmits {
+813    /// Returns `true` if no retransmits are necessary
+814    pub(super) fn is_empty(&self, streams: &StreamsState) -> bool {
+815        match &self.retransmits {
+816            Some(retransmits) => retransmits.is_empty(streams),
+817            None => true,
+818        }
+819    }
+820
+821    /// Returns a reference to the retransmits stored in this box
+822    pub(super) fn get(&self) -> Option<&Retransmits> {
+823        self.retransmits.as_deref()
+824    }
+825
+826    /// Returns a mutable reference to the retransmits stored in this box
+827    pub(super) fn get_mut(&mut self) -> Option<&mut Retransmits> {
+828        self.retransmits.as_deref_mut()
+829    }
+830
+831    /// Returns a mutable reference to the stored retransmits
+832    ///
+833    /// This function will allocate a backing storage if required.
+834    pub(super) fn get_or_create(&mut self) -> &mut Retransmits {
+835        if self.retransmits.is_none() {
+836            self.retransmits = Some(Box::default());
+837        }
+838        self.retransmits.as_deref_mut().unwrap()
+839    }
+840}
+841
+842/// RFC4303-style sliding window packet number deduplicator.
+843///
+844/// A contiguous bitfield, where each bit corresponds to a packet number and the rightmost bit is
+845/// always set. A set bit represents a packet that has been successfully authenticated. Bits left of
+846/// the window are assumed to be set.
+847///
+848/// ```text
+849/// ...xxxxxxxxx 1 0
+850///     ^        ^ ^
+851/// window highest next
+852/// ```
+853#[derive(Debug, Default)]
+854pub(super) struct Dedup {
+855    window: Window,
+856    /// Lowest packet number higher than all yet authenticated.
+857    next: u64,
+858}
+859
+860/// Inner bitfield type.
+861///
+862/// Because QUIC never reuses packet numbers, this only needs to be large enough to deal with
+863/// packets that are reordered but still delivered in a timely manner.
+864type Window = u128;
+865
+866/// Number of packets tracked by `Dedup`.
+867const WINDOW_SIZE: u64 = 1 + mem::size_of::<Window>() as u64 * 8;
+868
+869impl Dedup {
+870    /// Construct an empty window positioned at the start.
+871    #[cfg(test)]
+872    pub(super) fn new() -> Self {
+873        Self { window: 0, next: 0 }
+874    }
+875
+876    /// Highest packet number authenticated.
+877    fn highest(&self) -> u64 {
+878        self.next - 1
+879    }
+880
+881    /// Record a newly authenticated packet number.
+882    ///
+883    /// Returns whether the packet might be a duplicate.
+884    pub(super) fn insert(&mut self, packet: u64) -> bool {
+885        if let Some(diff) = packet.checked_sub(self.next) {
+886            // Right of window
+887            self.window = ((self.window << 1) | 1)
+888                .checked_shl(cmp::min(diff, u64::from(u32::MAX)) as u32)
+889                .unwrap_or(0);
+890            self.next = packet + 1;
+891            false
+892        } else if self.highest() - packet < WINDOW_SIZE {
+893            // Within window
+894            if let Some(bit) = (self.highest() - packet).checked_sub(1) {
+895                // < highest
+896                let mask = 1 << bit;
+897                let duplicate = self.window & mask != 0;
+898                self.window |= mask;
+899                duplicate
+900            } else {
+901                // == highest
+902                true
+903            }
+904        } else {
+905            // Left of window
+906            true
+907        }
+908    }
+909
+910    /// Returns the packet number of the smallest packet missing between the provided interval
+911    ///
+912    /// If there are no missing packets, returns `None`
+913    fn smallest_missing_in_interval(&self, lower_bound: u64, upper_bound: u64) -> Option<u64> {
+914        debug_assert!(lower_bound <= upper_bound);
+915        debug_assert!(upper_bound <= self.highest());
+916        const BITFIELD_SIZE: u64 = (mem::size_of::<Window>() * 8) as u64;
+917
+918        // Since we already know the packets at the boundaries have been received, we only need to
+919        // check those in between them (this removes the necessity of extra logic to deal with the
+920        // highest packet, which is stored outside the bitfield)
+921        let lower_bound = lower_bound + 1;
+922        let upper_bound = upper_bound.saturating_sub(1);
+923
+924        // Note: the offsets are counted from the right
+925        // The highest packet is not included in the bitfield, so we subtract 1 to account for that
+926        let start_offset = (self.highest() - upper_bound).max(1) - 1;
+927        if start_offset >= BITFIELD_SIZE {
+928            // The start offset is outside of the window. All packets outside of the window are
+929            // considered to be received.
+930            return None;
+931        }
+932
+933        let end_offset_exclusive = self.highest().saturating_sub(lower_bound);
+934
+935        // The range is clamped at the edge of the window, because any earlier packets are
+936        // considered to be received
+937        let range_len = end_offset_exclusive
+938            .saturating_sub(start_offset)
+939            .min(BITFIELD_SIZE);
+940        if range_len == 0 {
+941            return None;
+942        }
+943
+944        // Ensure the shift is within bounds (we already know start_offset < BITFIELD_SIZE,
+945        // because of the early return)
+946        let mask = if range_len == BITFIELD_SIZE {
+947            u128::MAX
+948        } else {
+949            ((1u128 << range_len) - 1) << start_offset
+950        };
+951        let gaps = !self.window & mask;
+952
+953        let smallest_missing_offset = 128 - gaps.leading_zeros() as u64;
+954        let smallest_missing_packet = self.highest() - smallest_missing_offset;
+955
+956        if smallest_missing_packet <= upper_bound {
+957            Some(smallest_missing_packet)
+958        } else {
+959            None
+960        }
+961    }
+962
+963    /// Returns true if there are any missing packets between the provided interval
+964    ///
+965    /// The provided packet numbers must have been received before calling this function
+966    fn missing_in_interval(&self, lower_bound: u64, upper_bound: u64) -> bool {
+967        self.smallest_missing_in_interval(lower_bound, upper_bound)
+968            .is_some()
+969    }
+970}
+971
+972/// Indicates which data is available for sending.
+973///
+974/// This applies to a particular space ID that was queried and all refers to on-path data.
+975#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+976pub(super) struct SendableFrames {
+977    /// Whether there are ACK frames to send, these are not ack-eliciting.
+978    pub(super) acks: bool,
+979    /// Whether there is a CONNECTION_CLOSE to send, this is not ack-eliciting.
+980    pub(super) close: bool,
+981    /// Whether there are any frames that must be sent on this specific space.
+982    ///
+983    /// A space here in the sense of a QUIC Multipath packet number space: `Initial`,
+984    /// `Handshake` and all `Data(PathId)` spaces.
+985    ///
+986    /// These are ack-eliciting. Some frames are scheduled per path, e.g. PING,
+987    /// IMMEDIATE_ACK, PATH_CHALLENGE or PATH_RESPONSE.
+988    pub(super) space_specific: bool,
+989    /// Whether there are any other frames to send, these are ack-eliciting.
+990    pub(super) other: bool,
+991}
+992
+993impl SendableFrames {
+994    /// Returns that no data is available for sending
+995    pub(super) fn empty() -> Self {
+996        Self {
+997            acks: false,
+998            close: false,
+999            space_specific: false,
+1000            other: false,
+1001        }
+1002    }
+1003
+1004    /// Whether an ack-eliciting packet will be sent.
+1005    pub(super) fn is_ack_eliciting(&self) -> bool {
+1006        let Self {
+1007            acks: _,
+1008            close,
+1009            space_specific,
+1010            other,
+1011        } = *self;
+1012        if close {
+1013            // No ack-eliciting frames are included with a CONNECTION_CLOSE, only acks.
+1014            return false;
+1015        }
+1016        space_specific || other
+1017    }
+1018
+1019    /// Whether no data is sendable.
+1020    pub(super) fn is_empty(&self) -> bool {
+1021        let Self {
+1022            acks,
+1023            close,
+1024            space_specific,
+1025            other,
+1026        } = *self;
+1027        !acks && !close && !space_specific && !other
+1028    }
+1029}
+1030
+1031impl ::std::ops::BitOrAssign for SendableFrames {
+1032    fn bitor_assign(&mut self, rhs: Self) {
+1033        let Self {
+1034            acks,
+1035            close,
+1036            space_specific,
+1037            other,
+1038        } = rhs;
+1039
+1040        self.acks |= acks;
+1041        self.close |= close;
+1042        self.space_specific |= space_specific;
+1043        self.other |= other;
+1044    }
+1045}
+1046
+1047#[derive(Debug)]
+1048pub(super) struct PendingAcks {
+1049    /// Whether we should send an ACK immediately, even if that means sending an ACK-only packet
+1050    ///
+1051    /// When `immediate_ack_required` is false, the normal behavior is to send ACK frames only when
+1052    /// there is other data to send, or when the `MaxAckDelay` timer expires.
+1053    immediate_ack_required: bool,
+1054    /// The number of ack-eliciting packets received since the last ACK frame was sent
+1055    ///
+1056    /// Once the count _exceeds_ `ack_eliciting_threshold`, an immediate ACK is required
+1057    ack_eliciting_since_last_ack_sent: u64,
+1058    non_ack_eliciting_since_last_ack_sent: u64,
+1059    ack_eliciting_threshold: u64,
+1060    /// The reordering threshold, controlling how we respond to out-of-order ack-eliciting packets
+1061    ///
+1062    /// Different values enable different behavior:
+1063    ///
+1064    /// * `0`: no special action is taken
+1065    /// * `1`: an ACK is immediately sent if it is out-of-order according to RFC 9000
+1066    /// * `>1`: an ACK is immediately sent if it is out-of-order according to the ACK frequency draft
+1067    reordering_threshold: u64,
+1068    /// The earliest ack-eliciting packet since the last ACK was sent, used to calculate the moment
+1069    /// upon which `max_ack_delay` elapses
+1070    earliest_ack_eliciting_since_last_ack_sent: Option<Instant>,
+1071    /// Packet number ranges for which to still send acknowledgements.
+1072    ///
+1073    /// These are packet number ranges of ack-eliciting packets the peer has sent and which
+1074    /// need to be acknowledged.  Packet numbers are only removed from here once the peer has
+1075    /// acknowledged the ACKs for them.
+1076    ranges: ArrayRangeSet,
+1077    /// The largest packet number received and the time it was received
+1078    ///
+1079    /// Used to calculate ACK delay in [`PendingAcks::ack_delay`].
+1080    largest_packet: Option<(u64, Instant)>,
+1081    /// The ack-eliciting packet we have received with the largest packet number
+1082    largest_ack_eliciting_packet: Option<u64>,
+1083    /// The largest acknowledged packet number sent in an ACK frame
+1084    largest_acked: Option<u64>,
+1085}
+1086
+1087impl PendingAcks {
+1088    fn new() -> Self {
+1089        Self {
+1090            immediate_ack_required: false,
+1091            ack_eliciting_since_last_ack_sent: 0,
+1092            non_ack_eliciting_since_last_ack_sent: 0,
+1093            ack_eliciting_threshold: 1,
+1094            reordering_threshold: 1,
+1095            earliest_ack_eliciting_since_last_ack_sent: None,
+1096            ranges: Default::default(),
+1097            largest_packet: Default::default(),
+1098            largest_ack_eliciting_packet: Default::default(),
+1099            largest_acked: Default::default(),
+1100        }
+1101    }
+1102
+1103    pub(super) fn set_ack_frequency_params(&mut self, frame: &frame::AckFrequency) {
+1104        self.ack_eliciting_threshold = frame.ack_eliciting_threshold.into_inner();
+1105        self.reordering_threshold = frame.reordering_threshold.into_inner();
+1106    }
+1107
+1108    pub(super) fn set_immediate_ack_required(&mut self) {
+1109        self.immediate_ack_required = true;
+1110    }
+1111
+1112    pub(super) fn on_max_ack_delay_timeout(&mut self) {
+1113        self.immediate_ack_required = self.ack_eliciting_since_last_ack_sent > 0;
+1114    }
+1115
+1116    pub(super) fn max_ack_delay_timeout(&self, max_ack_delay: Duration) -> Option<Instant> {
+1117        self.earliest_ack_eliciting_since_last_ack_sent
+1118            .map(|earliest_unacked| earliest_unacked + max_ack_delay)
+1119    }
+1120
+1121    /// Whether any ACK frames SHOULD be sent
+1122    ///
+1123    /// This is used in the top-level [`Connection::space_can_send`], so determines if a
+1124    /// packet will be built. It is often possible to construct new ACK ranges to send
+1125    /// before this returns `true`. This results in more ACK frames being sent, and
+1126    /// processing those at the receiver costs CPU for very little improvements.
+1127    ///
+1128    /// [`Connection::space_can_send`]: super::Connection::space_can_send
+1129    pub(super) fn can_send(&self) -> bool {
+1130        self.immediate_ack_required && !self.ranges.is_empty()
+1131    }
+1132
+1133    /// Returns the delay since the packet with the largest packet number was received
+1134    pub(super) fn ack_delay(&self, now: Instant) -> Duration {
+1135        self.largest_packet
+1136            .map_or_else(Duration::default, |(_, received)| now - received)
+1137    }
+1138
+1139    /// Handle receipt of a new packet
+1140    ///
+1141    /// Returns true if the max ack delay timer should be armed
+1142    pub(super) fn packet_received(
+1143        &mut self,
+1144        now: Instant,
+1145        packet_number: u64,
+1146        ack_eliciting: bool,
+1147        dedup: &Dedup,
+1148    ) -> bool {
+1149        if !ack_eliciting {
+1150            self.non_ack_eliciting_since_last_ack_sent += 1;
+1151            return false;
+1152        }
+1153
+1154        let prev_largest_ack_eliciting = self.largest_ack_eliciting_packet.unwrap_or(0);
+1155
+1156        // Track largest ack-eliciting packet
+1157        self.largest_ack_eliciting_packet = self
+1158            .largest_ack_eliciting_packet
+1159            .map(|pn| pn.max(packet_number))
+1160            .or(Some(packet_number));
+1161
+1162        // Handle ack_eliciting_threshold
+1163        self.ack_eliciting_since_last_ack_sent += 1;
+1164        self.immediate_ack_required |=
+1165            self.ack_eliciting_since_last_ack_sent > self.ack_eliciting_threshold;
+1166
+1167        // Handle out-of-order packets
+1168        self.immediate_ack_required |=
+1169            self.is_out_of_order(packet_number, prev_largest_ack_eliciting, dedup);
+1170
+1171        // Arm max_ack_delay timer if necessary
+1172        if self.earliest_ack_eliciting_since_last_ack_sent.is_none() && !self.can_send() {
+1173            self.earliest_ack_eliciting_since_last_ack_sent = Some(now);
+1174            return true;
+1175        }
+1176
+1177        false
+1178    }
+1179
+1180    fn is_out_of_order(
+1181        &self,
+1182        packet_number: u64,
+1183        prev_largest_ack_eliciting: u64,
+1184        dedup: &Dedup,
+1185    ) -> bool {
+1186        match self.reordering_threshold {
+1187            0 => false,
+1188            1 => {
+1189                // From https://www.rfc-editor.org/rfc/rfc9000#section-13.2.1-7
+1190                packet_number < prev_largest_ack_eliciting
+1191                    || dedup.missing_in_interval(prev_largest_ack_eliciting, packet_number)
+1192            }
+1193            _ => {
+1194                // From acknowledgement frequency draft, section 6.1: send an ACK immediately if
+1195                // doing so would cause the sender to detect a new packet loss
+1196                let Some((largest_acked, largest_unacked)) =
+1197                    self.largest_acked.zip(self.largest_ack_eliciting_packet)
+1198                else {
+1199                    return false;
+1200                };
+1201                if self.reordering_threshold > largest_acked {
+1202                    return false;
+1203                }
+1204                // The largest packet number that could be declared lost without a new ACK being
+1205                // sent
+1206                let largest_reported = largest_acked - self.reordering_threshold + 1;
+1207                let Some(smallest_missing_unreported) =
+1208                    dedup.smallest_missing_in_interval(largest_reported, largest_unacked)
+1209                else {
+1210                    return false;
+1211                };
+1212                largest_unacked - smallest_missing_unreported >= self.reordering_threshold
+1213            }
+1214        }
+1215    }
+1216
+1217    /// Should be called whenever ACKs have been sent
+1218    ///
+1219    /// This will suppress sending further ACKs until additional ACK eliciting frames arrive
+1220    pub(super) fn acks_sent(&mut self) {
+1221        // It is possible (though unlikely) that the ACKs we just sent do not cover all the
+1222        // ACK-eliciting packets we have received (e.g. if there is not enough room in the packet to
+1223        // fit all the ranges). To keep things simple, however, we assume they do. If there are
+1224        // indeed some ACKs that weren't covered, the packets might be ACKed later anyway, because
+1225        // they are still contained in `self.ranges`. If we somehow fail to send the ACKs at a later
+1226        // moment, the peer will assume the packets got lost and will retransmit their frames in a
+1227        // new packet, which is suboptimal, because we already received them. Our assumption here is
+1228        // that simplicity results in code that is more performant, even in the presence of
+1229        // occasional redundant retransmits.
+1230        self.immediate_ack_required = false;
+1231        self.ack_eliciting_since_last_ack_sent = 0;
+1232        self.non_ack_eliciting_since_last_ack_sent = 0;
+1233        self.earliest_ack_eliciting_since_last_ack_sent = None;
+1234        self.largest_acked = self.largest_ack_eliciting_packet;
+1235    }
+1236
+1237    /// Insert one packet that needs to be acknowledged
+1238    pub(super) fn insert_one(&mut self, packet: u64, now: Instant) {
+1239        self.ranges.insert_one(packet);
+1240
+1241        if self.largest_packet.is_none_or(|(pn, _)| packet > pn) {
+1242            self.largest_packet = Some((packet, now));
+1243        }
+1244
+1245        if self.ranges.len() > MAX_ACK_BLOCKS {
+1246            self.ranges.pop_min();
+1247        }
+1248    }
+1249
+1250    /// Remove ACKs of packets numbered at or below `max` from the set of pending ACKs
+1251    pub(super) fn subtract_below(&mut self, max: u64) {
+1252        self.ranges.remove(0..(max + 1));
+1253    }
+1254
+1255    /// Returns the set of currently pending ACK ranges
+1256    pub(super) fn ranges(&self) -> &ArrayRangeSet {
+1257        &self.ranges
+1258    }
+1259
+1260    /// Queue an ACK if a significant number of non-ACK-eliciting packets have not yet been
+1261    /// acknowledged
+1262    ///
+1263    /// Should be called immediately before a non-probing packet is composed, when we've already
+1264    /// committed to sending a packet regardless.
+1265    pub(super) fn maybe_ack_non_eliciting(&mut self) {
+1266        // If we're going to send a packet anyway, and we've received a significant number of
+1267        // non-ACK-eliciting packets, then include an ACK to help the peer perform timely loss
+1268        // detection even if they're not sending any ACK-eliciting packets themselves. Exact
+1269        // threshold chosen somewhat arbitrarily.
+1270        const LAZY_ACK_THRESHOLD: u64 = 10;
+1271        if self.non_ack_eliciting_since_last_ack_sent > LAZY_ACK_THRESHOLD {
+1272            self.immediate_ack_required = true;
+1273        }
+1274    }
+1275}
+1276
+1277/// Helper for mitigating [optimistic ACK attacks]
+1278///
+1279/// A malicious peer could prompt the local application to begin a large data transfer, and then
+1280/// send ACKs without first waiting for data to be received. This could defeat congestion control,
+1281/// allowing the connection to consume disproportionate resources. We therefore occasionally skip
+1282/// packet numbers, and classify any ACK referencing a skipped packet number as a transport error.
+1283///
+1284/// Skipped packet numbers occur only in the application data space (where costly transfers might
+1285/// take place) and are distributed exponentially to reflect the reduced likelihood and impact of
+1286/// bad behavior from a peer that has been well-behaved for an extended period.
+1287///
+1288/// ACKs for packet numbers that have not yet been allocated are also a transport error, but an
+1289/// attacker with knowledge of the congestion control algorithm in use could time falsified ACKs to
+1290/// arrive after the packets they reference are sent.
+1291///
+1292/// [optimistic ACK attacks]: https://www.rfc-editor.org/rfc/rfc9000.html#name-optimistic-ack-attack
+1293pub(super) struct PacketNumberFilter {
+1294    /// Next outgoing packet number to skip
+1295    next_skipped_packet_number: u64,
+1296    /// Most recently skipped packet number
+1297    prev_skipped_packet_number: Option<u64>,
+1298    /// Next packet number to skip is randomly selected from 2^n..2^n+1
+1299    exponent: u32,
+1300}
+1301
+1302impl PacketNumberFilter {
+1303    pub(super) fn new(rng: &mut (impl CryptoRng + ?Sized)) -> Self {
+1304        // First skipped PN is in 0..64
+1305        let exponent = 6;
+1306        Self {
+1307            next_skipped_packet_number: rng.random_range(0..2u64.saturating_pow(exponent)),
+1308            prev_skipped_packet_number: None,
+1309            exponent,
+1310        }
+1311    }
+1312
+1313    #[cfg(test)]
+1314    pub(super) fn disabled() -> Self {
+1315        Self {
+1316            next_skipped_packet_number: u64::MAX,
+1317            prev_skipped_packet_number: None,
+1318            exponent: u32::MAX,
+1319        }
+1320    }
+1321
+1322    /// Whether to use the provided packet number (false) or to skip it (true)
+1323    pub(super) fn skip_pn(&mut self, n: u64, rng: &mut (impl CryptoRng + ?Sized)) -> bool {
+1324        if n != self.next_skipped_packet_number {
+1325            return false;
+1326        }
+1327
+1328        trace!("skipping pn {n}");
+1329        // Skip this packet number, and choose the next one to skip
+1330        self.prev_skipped_packet_number = Some(self.next_skipped_packet_number);
+1331        let next_exponent = self.exponent.saturating_add(1);
+1332        self.next_skipped_packet_number = rng
+1333            .random_range(2u64.saturating_pow(self.exponent)..2u64.saturating_pow(next_exponent));
+1334        self.exponent = next_exponent;
+1335        true
+1336    }
+1337}
+1338
+1339/// Ensures we can always fit all our ACKs in a single minimum-MTU packet with room to spare
+1340const MAX_ACK_BLOCKS: usize = 64;
+1341
+1342#[cfg(test)]
+1343mod test {
+1344    use rand::Rng;
+1345    use rand::seq::SliceRandom;
+1346
+1347    use crate::token::ResetToken;
+1348    use crate::{ConnectionIdGenerator, RandomConnectionIdGenerator};
+1349
+1350    use super::*;
+1351
+1352    #[test]
+1353    fn sanity() {
+1354        let mut dedup = Dedup::new();
+1355        assert!(!dedup.insert(0));
+1356        assert_eq!(dedup.next, 1);
+1357        assert_eq!(dedup.window, 0b1);
+1358        assert!(dedup.insert(0));
+1359        assert_eq!(dedup.next, 1);
+1360        assert_eq!(dedup.window, 0b1);
+1361        assert!(!dedup.insert(1));
+1362        assert_eq!(dedup.next, 2);
+1363        assert_eq!(dedup.window, 0b11);
+1364        assert!(!dedup.insert(2));
+1365        assert_eq!(dedup.next, 3);
+1366        assert_eq!(dedup.window, 0b111);
+1367        assert!(!dedup.insert(4));
+1368        assert_eq!(dedup.next, 5);
+1369        assert_eq!(dedup.window, 0b11110);
+1370        assert!(!dedup.insert(7));
+1371        assert_eq!(dedup.next, 8);
+1372        assert_eq!(dedup.window, 0b1111_0100);
+1373        assert!(dedup.insert(4));
+1374        assert!(!dedup.insert(3));
+1375        assert_eq!(dedup.next, 8);
+1376        assert_eq!(dedup.window, 0b1111_1100);
+1377        assert!(!dedup.insert(6));
+1378        assert_eq!(dedup.next, 8);
+1379        assert_eq!(dedup.window, 0b1111_1101);
+1380        assert!(!dedup.insert(5));
+1381        assert_eq!(dedup.next, 8);
+1382        assert_eq!(dedup.window, 0b1111_1111);
+1383    }
+1384
+1385    #[test]
+1386    fn happypath() {
+1387        let mut dedup = Dedup::new();
+1388        for i in 0..(2 * WINDOW_SIZE) {
+1389            assert!(!dedup.insert(i));
+1390            for j in 0..=i {
+1391                assert!(dedup.insert(j));
+1392            }
+1393        }
+1394    }
+1395
+1396    #[test]
+1397    fn jump() {
+1398        let mut dedup = Dedup::new();
+1399        dedup.insert(2 * WINDOW_SIZE);
+1400        assert!(dedup.insert(WINDOW_SIZE));
+1401        assert_eq!(dedup.next, 2 * WINDOW_SIZE + 1);
+1402        assert_eq!(dedup.window, 0);
+1403        assert!(!dedup.insert(WINDOW_SIZE + 1));
+1404        assert_eq!(dedup.next, 2 * WINDOW_SIZE + 1);
+1405        assert_eq!(dedup.window, 1 << (WINDOW_SIZE - 2));
+1406    }
+1407
+1408    #[test]
+1409    fn dedup_has_missing() {
+1410        let mut dedup = Dedup::new();
+1411
+1412        dedup.insert(0);
+1413        assert!(!dedup.missing_in_interval(0, 0));
+1414
+1415        dedup.insert(1);
+1416        assert!(!dedup.missing_in_interval(0, 1));
+1417
+1418        dedup.insert(3);
+1419        assert!(dedup.missing_in_interval(1, 3));
+1420
+1421        dedup.insert(4);
+1422        assert!(!dedup.missing_in_interval(3, 4));
+1423        assert!(dedup.missing_in_interval(0, 4));
+1424
+1425        dedup.insert(2);
+1426        assert!(!dedup.missing_in_interval(0, 4));
+1427    }
+1428
+1429    #[test]
+1430    fn dedup_outside_of_window_has_missing() {
+1431        let mut dedup = Dedup::new();
+1432
+1433        for i in 0..140 {
+1434            dedup.insert(i);
+1435        }
+1436
+1437        // 0 and 4 are outside of the window
+1438        assert!(!dedup.missing_in_interval(0, 4));
+1439        dedup.insert(160);
+1440        assert!(!dedup.missing_in_interval(0, 4));
+1441        assert!(!dedup.missing_in_interval(0, 140));
+1442        assert!(dedup.missing_in_interval(0, 160));
+1443    }
+1444
+1445    #[test]
+1446    fn dedup_smallest_missing() {
+1447        let mut dedup = Dedup::new();
+1448
+1449        dedup.insert(0);
+1450        assert_eq!(dedup.smallest_missing_in_interval(0, 0), None);
+1451
+1452        dedup.insert(1);
+1453        assert_eq!(dedup.smallest_missing_in_interval(0, 1), None);
+1454
+1455        dedup.insert(5);
+1456        dedup.insert(7);
+1457        assert_eq!(dedup.smallest_missing_in_interval(0, 7), Some(2));
+1458        assert_eq!(dedup.smallest_missing_in_interval(5, 7), Some(6));
+1459
+1460        dedup.insert(2);
+1461        assert_eq!(dedup.smallest_missing_in_interval(1, 7), Some(3));
+1462
+1463        dedup.insert(170);
+1464        dedup.insert(172);
+1465        dedup.insert(300);
+1466        assert_eq!(dedup.smallest_missing_in_interval(170, 172), None);
+1467
+1468        dedup.insert(500);
+1469        assert_eq!(dedup.smallest_missing_in_interval(0, 500), Some(372));
+1470        assert_eq!(dedup.smallest_missing_in_interval(0, 373), Some(372));
+1471        assert_eq!(dedup.smallest_missing_in_interval(0, 372), None);
+1472    }
+1473
+1474    #[test]
+1475    fn pending_acks_first_packet_is_not_considered_reordered() {
+1476        let mut acks = PendingAcks::new();
+1477        let mut dedup = Dedup::new();
+1478        dedup.insert(0);
+1479        acks.packet_received(Instant::now(), 0, true, &dedup);
+1480        assert!(!acks.immediate_ack_required);
+1481    }
+1482
+1483    #[test]
+1484    fn pending_acks_after_immediate_ack_set() {
+1485        let mut acks = PendingAcks::new();
+1486        let mut dedup = Dedup::new();
+1487
+1488        // Receive ack-eliciting packet
+1489        dedup.insert(0);
+1490        let now = Instant::now();
+1491        acks.insert_one(0, now);
+1492        acks.packet_received(now, 0, true, &dedup);
+1493
+1494        // Sanity check
+1495        assert!(!acks.ranges.is_empty());
+1496        assert!(!acks.can_send());
+1497
+1498        // Can send ACK after max_ack_delay exceeded
+1499        acks.set_immediate_ack_required();
+1500        assert!(acks.can_send());
+1501    }
+1502
+1503    #[test]
+1504    fn pending_acks_ack_delay() {
+1505        let mut acks = PendingAcks::new();
+1506        let mut dedup = Dedup::new();
+1507
+1508        let t1 = Instant::now();
+1509        let t2 = t1 + Duration::from_millis(2);
+1510        let t3 = t2 + Duration::from_millis(5);
+1511        assert_eq!(acks.ack_delay(t1), Duration::from_millis(0));
+1512        assert_eq!(acks.ack_delay(t2), Duration::from_millis(0));
+1513        assert_eq!(acks.ack_delay(t3), Duration::from_millis(0));
+1514
+1515        // In-order packet
+1516        dedup.insert(0);
+1517        acks.insert_one(0, t1);
+1518        acks.packet_received(t1, 0, true, &dedup);
+1519        assert_eq!(acks.ack_delay(t1), Duration::from_millis(0));
+1520        assert_eq!(acks.ack_delay(t2), Duration::from_millis(2));
+1521        assert_eq!(acks.ack_delay(t3), Duration::from_millis(7));
+1522
+1523        // Out of order (higher than expected)
+1524        dedup.insert(3);
+1525        acks.insert_one(3, t2);
+1526        acks.packet_received(t2, 3, true, &dedup);
+1527        assert_eq!(acks.ack_delay(t2), Duration::from_millis(0));
+1528        assert_eq!(acks.ack_delay(t3), Duration::from_millis(5));
+1529
+1530        // Out of order (lower than expected, so previous instant is kept)
+1531        dedup.insert(2);
+1532        acks.insert_one(2, t3);
+1533        acks.packet_received(t3, 2, true, &dedup);
+1534        assert_eq!(acks.ack_delay(t3), Duration::from_millis(5));
+1535    }
+1536
+1537    #[test]
+1538    fn sent_packet_size() {
+1539        // The tracking state of sent packets should be minimal, and not grow
+1540        // over time.
+1541        assert!(std::mem::size_of::<SentPacket>() <= 128);
+1542    }
+1543
+1544    #[test]
+1545    fn pending_new_cids() {
+1546        #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+1547        use aws_lc_rs::hmac;
+1548        #[cfg(feature = "ring")]
+1549        use ring::hmac;
+1550
+1551        let mut cid_generator = RandomConnectionIdGenerator::new(8);
+1552        let mut reset_key = [0; 64];
+1553        rand::rng().fill_bytes(&mut reset_key);
+1554        let hmac = hmac::Key::new(hmac::HMAC_SHA256, &reset_key);
+1555
+1556        let cid_a = cid_generator.generate_cid();
+1557        let a = IssuedCid {
+1558            path_id: PathId::ZERO,
+1559            sequence: 1,
+1560            id: cid_a,
+1561            reset_token: ResetToken::new(&hmac, cid_a),
+1562        };
+1563        let cid_b = cid_generator.generate_cid();
+1564        let b = IssuedCid {
+1565            path_id: PathId::ZERO,
+1566            sequence: 2,
+1567            id: cid_b,
+1568            reset_token: ResetToken::new(&hmac, cid_b),
+1569        };
+1570        let cid_c = cid_generator.generate_cid();
+1571        let c = IssuedCid {
+1572            path_id: PathId(1),
+1573            sequence: 1,
+1574            id: cid_c,
+1575            reset_token: ResetToken::new(&hmac, cid_c),
+1576        };
+1577
+1578        let mut pending_cids = PendingNewCids::default();
+1579
+1580        for _ in 0..9 {
+1581            // Push CIDs in a random order
+1582            let mut input = vec![a, b, c];
+1583            input.shuffle(&mut rand::rng());
+1584            for cid in input {
+1585                pending_cids.push(cid);
+1586            }
+1587
+1588            // Pop order is always the same
+1589            assert_eq!(pending_cids.pop().map(|i| i.id), Some(a.id));
+1590            assert_eq!(pending_cids.pop().map(|i| i.id), Some(b.id));
+1591            assert_eq!(pending_cids.pop().map(|i| i.id), Some(c.id));
+1592            assert!(pending_cids.pop().is_none());
+1593        }
+1594    }
+1595}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/state.rs.html b/pr/672/docs/src/noq_proto/connection/state.rs.html new file mode 100644 index 000000000..5129e6fca --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/state.rs.html @@ -0,0 +1,440 @@ +state.rs - source

noq_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    ///
+70    /// Returns whether we were in the draining state before.
+71    pub(super) fn move_to_drained(&mut self, error: Option<ConnectionError>) -> bool {
+72        let (error, is_local, was_draining) = if let Some(error) = error {
+73            (
+74                Some(error),
+75                false,
+76                matches!(self.inner, InnerState::Draining { .. }),
+77            )
+78        } else {
+79            let (error, was_draining) = match &mut self.inner {
+80                InnerState::Draining { error, .. } => (error.take(), true),
+81                InnerState::Drained { .. } => panic!("invalid state transition drained -> drained"),
+82                InnerState::Closed { error_read, .. } if *error_read => (None, false),
+83                InnerState::Closed { remote_reason, .. } => {
+84                    let error = match remote_reason.clone().into() {
+85                        ConnectionError::ConnectionClosed(close) => {
+86                            if close.error_code == TransportErrorCode::PROTOCOL_VIOLATION {
+87                                ConnectionError::TransportError(TransportError::new(
+88                                    close.error_code,
+89                                    std::string::String::from_utf8_lossy(&close.reason[..])
+90                                        .to_string(),
+91                                ))
+92                            } else {
+93                                ConnectionError::ConnectionClosed(close)
+94                            }
+95                        }
+96                        e => e,
+97                    };
+98                    (Some(error), false)
+99                }
+100                InnerState::Handshake(_) | InnerState::Established => (None, false),
+101            };
+102            (error, self.is_local_close(), was_draining)
+103        };
+104        self.inner = InnerState::Drained { error, is_local };
+105        trace!("connection state: drained");
+106        was_draining
+107    }
+108
+109    /// Moves to a draining state.
+110    ///
+111    /// Panics if the state is already draining or drained.
+112    pub(super) fn move_to_draining(&mut self, error: Option<ConnectionError>) {
+113        assert!(
+114            matches!(
+115                self.inner,
+116                InnerState::Handshake(_) | InnerState::Established | InnerState::Closed { .. }
+117            ),
+118            "invalid state transition {:?} -> draining",
+119            self.as_type()
+120        );
+121        let is_local = self.is_local_close();
+122
+123        // If no error is provided and we were in the `Closed` state with an unread non-local
+124        // error, preserve that error so `take_error` can still surface it. Otherwise the
+125        // `ConnectionLost` event would never be emitted, breaking the invariant that a
+126        // drained noq-proto connection will always produce a `ConnectionLost` event.
+127        let error = error.or_else(|| {
+128            if let InnerState::Closed {
+129                ref remote_reason,
+130                error_read: false,
+131                is_local: false,
+132            } = self.inner
+133            {
+134                Some(remote_reason.clone().into())
+135            } else {
+136                None
+137            }
+138        });
+139
+140        self.inner = InnerState::Draining { error, is_local };
+141        trace!("connection state: draining");
+142    }
+143
+144    fn is_local_close(&self) -> bool {
+145        match self.inner {
+146            InnerState::Handshake(_) => false,
+147            InnerState::Established => false,
+148            InnerState::Closed { is_local, .. } => is_local,
+149            InnerState::Draining { is_local, .. } => is_local,
+150            InnerState::Drained { is_local, .. } => is_local,
+151        }
+152    }
+153
+154    /// Enters the Closing connection state, due to changes in the [`Connection`] state.
+155    ///
+156    /// This is the closing state from
+157    /// <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1> due to the local side
+158    /// having initiated immediate close.
+159    ///
+160    /// Crucially, this is to be used when internal state changes result in initiating an
+161    /// immediate close. The resulting error will be surfaced as a [`ConnectionLost`] event
+162    /// in [`Connection::poll`].
+163    ///
+164    /// # Panics
+165    ///
+166    /// Panics if the state is later than established.
+167    ///
+168    /// [`Connection`]: super::Connection
+169    /// [`ConnectionLost`]: crate::Event::ConnectionLost
+170    /// [`Connection::poll`]: super::Connection::poll
+171    pub(super) fn move_to_closed<R: Into<CloseReason>>(&mut self, reason: R) {
+172        assert!(
+173            matches!(
+174                self.inner,
+175                InnerState::Handshake(_) | InnerState::Established | InnerState::Closed { .. }
+176            ),
+177            "invalid state transition {:?} -> closed",
+178            self.as_type()
+179        );
+180        let remote_reason = reason.into();
+181        let is_local = false;
+182        trace!(?remote_reason, ?is_local, "connection state: closed");
+183        self.inner = InnerState::Closed {
+184            error_read: false,
+185            remote_reason,
+186            is_local,
+187        };
+188    }
+189
+190    /// Enters the Closing connection state, initiated by explicit API calls.
+191    ///
+192    /// This is the closing state from
+193    /// <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1> due to the local side
+194    /// having initiated immediate close.
+195    ///
+196    /// Crucially, this is to be used when immediate close is entered due to an API
+197    /// being called. It means the close will NOT surface as a [`ConnectionLost`] event in
+198    /// [`Connection::poll`].
+199    ///
+200    /// See [`Self::move_to_closed`] for when the internal state changes resulted in
+201    /// initiating an immediate close.
+202    ///
+203    /// # Panics
+204    ///
+205    /// Panics if the state is later than established.
+206    ///
+207    /// [`ConnectionLost`]: crate::Event::ConnectionLost
+208    /// [`Connection::poll`]: super::Connection::poll
+209    pub(super) fn move_to_closed_local<R: Into<CloseReason>>(&mut self, reason: R) {
+210        assert!(
+211            matches!(
+212                self.inner,
+213                InnerState::Handshake(_) | InnerState::Established | InnerState::Closed { .. }
+214            ),
+215            "invalid state transition {:?} -> closed (local)",
+216            self.as_type()
+217        );
+218        let remote_reason = reason.into();
+219        let is_local = true;
+220        trace!(?remote_reason, ?is_local, "connection state: closed");
+221        self.inner = InnerState::Closed {
+222            error_read: false,
+223            remote_reason,
+224            is_local,
+225        };
+226    }
+227
+228    pub(super) fn is_handshake(&self) -> bool {
+229        matches!(self.inner, InnerState::Handshake(_))
+230    }
+231
+232    pub(super) fn is_established(&self) -> bool {
+233        matches!(self.inner, InnerState::Established)
+234    }
+235
+236    pub(super) fn is_closed(&self) -> bool {
+237        matches!(
+238            self.inner,
+239            InnerState::Closed { .. } | InnerState::Draining { .. } | InnerState::Drained { .. }
+240        )
+241    }
+242
+243    pub(super) fn is_drained(&self) -> bool {
+244        matches!(self.inner, InnerState::Drained { .. })
+245    }
+246
+247    pub(super) fn take_error(&mut self) -> Option<ConnectionError> {
+248        match &mut self.inner {
+249            InnerState::Draining { error, is_local } => {
+250                if !*is_local {
+251                    error.take()
+252                } else {
+253                    None
+254                }
+255            }
+256            InnerState::Drained { error, is_local } => {
+257                if !*is_local {
+258                    error.take()
+259                } else {
+260                    None
+261                }
+262            }
+263            InnerState::Closed {
+264                remote_reason,
+265                is_local: local_reason,
+266                error_read,
+267            } => {
+268                if *error_read {
+269                    None
+270                } else {
+271                    *error_read = true;
+272                    if *local_reason {
+273                        None
+274                    } else {
+275                        Some(remote_reason.clone().into())
+276                    }
+277                }
+278            }
+279            InnerState::Handshake(_) | InnerState::Established => None,
+280        }
+281    }
+282
+283    pub(super) fn as_type(&self) -> StateType {
+284        match self.inner {
+285            InnerState::Handshake(_) => StateType::Handshake,
+286            InnerState::Established => StateType::Established,
+287            InnerState::Closed { .. } => StateType::Closed,
+288            InnerState::Draining { .. } => StateType::Draining,
+289            InnerState::Drained { .. } => StateType::Drained,
+290        }
+291    }
+292}
+293
+294/// The state a [`Connection`] can be in.
+295///
+296/// [`Connection`]: super::Connection
+297#[derive(Debug, Clone)]
+298pub(super) enum StateType {
+299    /// Before the handshake is *confirmed*.
+300    Handshake,
+301    /// Once the handshake is *confirmed*.
+302    Established,
+303    /// The connection is closed, waiting for remote to confirm close.
+304    ///
+305    /// Specifically <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1>.
+306    ///
+307    /// So the side that initiates an immediate close will stay in this state while it is
+308    /// waiting for the remote to also send a CONNECTION_CLOSE. The side that receives a
+309    /// connection close will skip straight to [`StateType::Draining`].
+310    Closed,
+311    /// The connection is draining, giving time to gracefully discard any in-flight packets.
+312    ///
+313    /// Specifically <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.2>.
+314    ///
+315    /// See [`StateType::Closed`] above for more details.
+316    Draining,
+317    /// The connection is drained, waiting for the application to drop us.
+318    ///
+319    /// This is a terminal state in which the connection does nothing and can never do
+320    /// anything again. Waiting for the application to drop the [`Connection`] struct.
+321    ///
+322    /// [`Connection`]: super::Connection
+323    Drained,
+324}
+325
+326#[derive(Debug, Clone)]
+327pub(super) enum CloseReason {
+328    TransportError(TransportError),
+329    Connection(ConnectionClose),
+330    Application(ApplicationClose),
+331}
+332
+333impl From<TransportError> for CloseReason {
+334    fn from(x: TransportError) -> Self {
+335        Self::TransportError(x)
+336    }
+337}
+338impl From<ConnectionClose> for CloseReason {
+339    fn from(x: ConnectionClose) -> Self {
+340        Self::Connection(x)
+341    }
+342}
+343impl From<ApplicationClose> for CloseReason {
+344    fn from(x: ApplicationClose) -> Self {
+345        Self::Application(x)
+346    }
+347}
+348
+349impl From<Close> for CloseReason {
+350    fn from(value: Close) -> Self {
+351        match value {
+352            Close::Application(reason) => Self::Application(reason),
+353            Close::Connection(reason) => Self::Connection(reason),
+354        }
+355    }
+356}
+357
+358impl From<CloseReason> for ConnectionError {
+359    fn from(value: CloseReason) -> Self {
+360        match value {
+361            CloseReason::TransportError(err) => Self::TransportError(err),
+362            CloseReason::Connection(reason) => Self::ConnectionClosed(reason),
+363            CloseReason::Application(reason) => Self::ApplicationClosed(reason),
+364        }
+365    }
+366}
+367
+368impl From<CloseReason> for Close {
+369    fn from(value: CloseReason) -> Self {
+370        match value {
+371            CloseReason::TransportError(err) => Self::Connection(err.into()),
+372            CloseReason::Connection(reason) => Self::Connection(reason),
+373            CloseReason::Application(reason) => Self::Application(reason),
+374        }
+375    }
+376}
+377
+378#[derive(Debug, Clone)]
+379enum InnerState {
+380    /// See [`StateType::Handshake`].
+381    Handshake(Handshake),
+382    /// See [`StateType::Established`].
+383    Established,
+384    /// See [`StateType::Closed`].
+385    Closed {
+386        /// The reason the remote closed the connection, or the reason we are sending to the remote.
+387        remote_reason: CloseReason,
+388        /// Set to true if we closed the connection locally.
+389        is_local: bool,
+390        /// Did we read this as error already?
+391        error_read: bool,
+392    },
+393    /// See [`StateType::Draining`].
+394    Draining {
+395        /// Why the connection was lost, if it has been.
+396        error: Option<ConnectionError>,
+397        /// Set to true if we closed the connection locally.
+398        is_local: bool,
+399    },
+400    /// See [`StateType::Drained`].
+401    /// Waiting for application to call close so we can dispose of the resources.
+402    Drained {
+403        /// Why the connection was lost, if it has been.
+404        error: Option<ConnectionError>,
+405        /// Set to true if we closed the connection locally.
+406        is_local: bool,
+407    },
+408}
+409
+410#[allow(unreachable_pub)] // fuzzing only
+411#[derive(Debug, Clone)]
+412pub struct Handshake {
+413    /// Whether the remote CID has been set by the peer yet.
+414    ///
+415    /// Always set for servers.
+416    pub(super) remote_cid_set: bool,
+417    /// Stateless retry token received in the first Initial by a server.
+418    ///
+419    /// Must be present in every Initial. Always empty for clients.
+420    pub(super) expected_token: Bytes,
+421    /// First cryptographic message.
+422    ///
+423    /// Only set for clients.
+424    pub(super) client_hello: Option<Bytes>,
+425    /// Whether the server address is allowed to migrate.
+426    ///
+427    /// We allow the server to migrate during the handshake as long as we have not
+428    /// received an authenticated handshake packet: it can send a response from a
+429    /// different address than we sent the initial to.  This allows us to send the
+430    /// initial packet over multiple paths - by means of an IPv6 ULA address that copies
+431    /// the packets sent to it to multiple destinations - and accept one response.
+432    ///
+433    /// This is only ever set to true if for a client which hasn't yet received an
+434    /// authenticated handshake packet.  It is set back to false in
+435    /// [`super::Connection::on_packet_authenticated`].
+436    ///
+437    /// THIS IS NOT RFC 9000 COMPLIANT!  A server is not allowed to migrate addresses,
+438    /// other than using the preferred-address transport parameter.
+439    pub(super) allow_server_migration: bool,
+440}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/stats.rs.html b/pr/672/docs/src/noq_proto/connection/stats.rs.html new file mode 100644 index 000000000..365b026db --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/stats.rs.html @@ -0,0 +1,367 @@ +stats.rs - source

noq_proto/connection/
stats.rs

1//! Connection statistics
+2
+3use rustc_hash::FxHashMap;
+4
+5use crate::Duration;
+6use crate::FrameType;
+7
+8use super::PathId;
+9
+10/// Statistics about UDP datagrams transmitted or received on a connection.
+11///
+12/// All QUIC packets are carried by UDP datagrams. Hence, these statistics cover all traffic
+13/// on a connection.
+14#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, derive_more::Add, derive_more::AddAssign)]
+15#[non_exhaustive]
+16pub struct UdpStats {
+17    /// The number of UDP datagrams observed.
+18    pub datagrams: u64,
+19    /// The total amount of bytes which have been transferred inside UDP datagrams.
+20    pub bytes: u64,
+21    /// The number of I/O operations executed.
+22    ///
+23    /// Can be less than `datagrams` when GSO, GRO, and/or batched system calls are in use.
+24    pub ios: u64,
+25}
+26
+27impl UdpStats {
+28    pub(crate) fn on_sent(&mut self, datagrams: u64, bytes: usize) {
+29        self.datagrams += datagrams;
+30        self.bytes += bytes as u64;
+31        self.ios += 1;
+32    }
+33}
+34
+35/// Number of frames transmitted or received of each frame type.
+36#[derive(Default, Copy, Clone, PartialEq, Eq, derive_more::Add, derive_more::AddAssign)]
+37#[non_exhaustive]
+38#[allow(missing_docs)]
+39pub struct FrameStats {
+40    pub acks: u64,
+41    pub path_acks: u64,
+42    pub ack_frequency: u64,
+43    pub crypto: u64,
+44    pub connection_close: u64,
+45    pub data_blocked: u64,
+46    pub datagram: u64,
+47    pub handshake_done: u8,
+48    pub immediate_ack: u64,
+49    pub max_data: u64,
+50    pub max_stream_data: u64,
+51    pub max_streams_bidi: u64,
+52    pub max_streams_uni: u64,
+53    pub new_connection_id: u64,
+54    pub path_new_connection_id: u64,
+55    pub new_token: u64,
+56    pub path_challenge: u64,
+57    pub path_response: u64,
+58    pub ping: u64,
+59    pub reset_stream: u64,
+60    pub retire_connection_id: u64,
+61    pub path_retire_connection_id: u64,
+62    pub stream_data_blocked: u64,
+63    pub streams_blocked_bidi: u64,
+64    pub streams_blocked_uni: u64,
+65    pub stop_sending: u64,
+66    pub stream: u64,
+67    pub observed_addr: u64,
+68    pub path_abandon: u64,
+69    pub path_status_available: u64,
+70    pub path_status_backup: u64,
+71    pub max_path_id: u64,
+72    pub paths_blocked: u64,
+73    pub path_cids_blocked: u64,
+74    pub add_address: u64,
+75    pub reach_out: u64,
+76    pub remove_address: u64,
+77}
+78
+79impl FrameStats {
+80    pub(crate) fn record(&mut self, frame_type: FrameType) {
+81        use FrameType::*;
+82        // Increments the field. Added for readability
+83        macro_rules! inc {
+84            ($field_name: ident) => {{ self.$field_name = self.$field_name.saturating_add(1) }};
+85        }
+86        match frame_type {
+87            Padding => {}
+88            Ping => inc!(ping),
+89            Ack | AckEcn => inc!(acks),
+90            PathAck | PathAckEcn => inc!(path_acks),
+91            ResetStream => inc!(reset_stream),
+92            StopSending => inc!(stop_sending),
+93            Crypto => inc!(crypto),
+94            Datagram(_) => inc!(datagram),
+95            NewToken => inc!(new_token),
+96            MaxData => inc!(max_data),
+97            MaxStreamData => inc!(max_stream_data),
+98            MaxStreamsBidi => inc!(max_streams_bidi),
+99            MaxStreamsUni => inc!(max_streams_uni),
+100            DataBlocked => inc!(data_blocked),
+101            Stream(_) => inc!(stream),
+102            StreamDataBlocked => inc!(stream_data_blocked),
+103            StreamsBlockedUni => inc!(streams_blocked_uni),
+104            StreamsBlockedBidi => inc!(streams_blocked_bidi),
+105            NewConnectionId => inc!(new_connection_id),
+106            PathNewConnectionId => inc!(path_new_connection_id),
+107            RetireConnectionId => inc!(retire_connection_id),
+108            PathRetireConnectionId => inc!(path_retire_connection_id),
+109            PathChallenge => inc!(path_challenge),
+110            PathResponse => inc!(path_response),
+111            ConnectionClose | ApplicationClose => inc!(connection_close),
+112            AckFrequency => inc!(ack_frequency),
+113            ImmediateAck => inc!(immediate_ack),
+114            HandshakeDone => inc!(handshake_done),
+115            ObservedIpv4Addr | ObservedIpv6Addr => inc!(observed_addr),
+116            PathAbandon => inc!(path_abandon),
+117            PathStatusAvailable => inc!(path_status_available),
+118            PathStatusBackup => inc!(path_status_backup),
+119            MaxPathId => inc!(max_path_id),
+120            PathsBlocked => inc!(paths_blocked),
+121            PathCidsBlocked => inc!(path_cids_blocked),
+122            AddIpv4Address | AddIpv6Address => inc!(add_address),
+123            ReachOutAtIpv4 | ReachOutAtIpv6 => inc!(reach_out),
+124            RemoveAddress => inc!(remove_address),
+125        };
+126    }
+127}
+128
+129impl std::fmt::Debug for FrameStats {
+130    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+131        let Self {
+132            acks,
+133            path_acks,
+134            ack_frequency,
+135            crypto,
+136            connection_close,
+137            data_blocked,
+138            datagram,
+139            handshake_done,
+140            immediate_ack,
+141            max_data,
+142            max_stream_data,
+143            max_streams_bidi,
+144            max_streams_uni,
+145            new_connection_id,
+146            path_new_connection_id,
+147            new_token,
+148            path_challenge,
+149            path_response,
+150            ping,
+151            reset_stream,
+152            retire_connection_id,
+153            path_retire_connection_id,
+154            stream_data_blocked,
+155            streams_blocked_bidi,
+156            streams_blocked_uni,
+157            stop_sending,
+158            stream,
+159            observed_addr,
+160            path_abandon,
+161            path_status_available,
+162            path_status_backup,
+163            max_path_id,
+164            paths_blocked,
+165            path_cids_blocked,
+166            add_address,
+167            reach_out,
+168            remove_address,
+169        } = self;
+170        f.debug_struct("FrameStats")
+171            .field("ACK", acks)
+172            .field("ACK_FREQUENCY", ack_frequency)
+173            .field("CONNECTION_CLOSE", connection_close)
+174            .field("CRYPTO", crypto)
+175            .field("DATA_BLOCKED", data_blocked)
+176            .field("DATAGRAM", datagram)
+177            .field("HANDSHAKE_DONE", handshake_done)
+178            .field("IMMEDIATE_ACK", immediate_ack)
+179            .field("MAX_DATA", max_data)
+180            .field("MAX_PATH_ID", max_path_id)
+181            .field("MAX_STREAM_DATA", max_stream_data)
+182            .field("MAX_STREAMS_BIDI", max_streams_bidi)
+183            .field("MAX_STREAMS_UNI", max_streams_uni)
+184            .field("NEW_CONNECTION_ID", new_connection_id)
+185            .field("NEW_TOKEN", new_token)
+186            .field("PATHS_BLOCKED", paths_blocked)
+187            .field("PATH_ABANDON", path_abandon)
+188            .field("PATH_ACK", path_acks)
+189            .field("PATH_STATUS_AVAILABLE", path_status_available)
+190            .field("PATH_STATUS_BACKUP", path_status_backup)
+191            .field("PATH_CHALLENGE", path_challenge)
+192            .field("PATH_CIDS_BLOCKED", path_cids_blocked)
+193            .field("PATH_NEW_CONNECTION_ID", path_new_connection_id)
+194            .field("PATH_RESPONSE", path_response)
+195            .field("PATH_RETIRE_CONNECTION_ID", path_retire_connection_id)
+196            .field("PING", ping)
+197            .field("RESET_STREAM", reset_stream)
+198            .field("RETIRE_CONNECTION_ID", retire_connection_id)
+199            .field("STREAM_DATA_BLOCKED", stream_data_blocked)
+200            .field("STREAMS_BLOCKED_BIDI", streams_blocked_bidi)
+201            .field("STREAMS_BLOCKED_UNI", streams_blocked_uni)
+202            .field("STOP_SENDING", stop_sending)
+203            .field("STREAM", stream)
+204            .field("OBSERVED_ADDRESS", observed_addr)
+205            .field("ADD_ADDRESS", add_address)
+206            .field("REACH_OUT", reach_out)
+207            .field("REMOVE_ADDRESS", remove_address)
+208            .finish()
+209    }
+210}
+211
+212/// Statistics related to a transmission path.
+213#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
+214#[non_exhaustive]
+215pub struct PathStats {
+216    /// Current best estimate of this connection's latency (round-trip-time).
+217    pub rtt: Duration,
+218    /// Statistics about datagrams and bytes sent on this path.
+219    pub udp_tx: UdpStats,
+220    /// Statistics about datagrams and bytes received on this path.
+221    pub udp_rx: UdpStats,
+222    /// Statistics about frames transmitted on this path.
+223    pub frame_tx: FrameStats,
+224    /// Statistics about frames received on this path.
+225    pub frame_rx: FrameStats,
+226    /// Current congestion window of the connection.
+227    pub cwnd: u64,
+228    /// Congestion events on the connection.
+229    pub congestion_events: u64,
+230    /// Spurious congestion events on the connection.
+231    pub spurious_congestion_events: u64,
+232    /// The number of packets lost on this path.
+233    pub lost_packets: u64,
+234    /// The number of bytes lost on this path.
+235    pub lost_bytes: u64,
+236    /// The number of PLPMTUD probe packets sent on this path.
+237    ///
+238    /// These are also counted by [`UdpStats::datagrams`].
+239    pub sent_plpmtud_probes: u64,
+240    /// The number of PLPMTUD probe packets lost on this path.
+241    ///
+242    /// These are not included in [`Self::lost_packets`] and [`Self::lost_bytes`].
+243    pub lost_plpmtud_probes: u64,
+244    /// The number of times a black hole was detected in the path.
+245    pub black_holes_detected: u64,
+246    /// Largest UDP payload size the path currently supports.
+247    pub current_mtu: u16,
+248}
+249
+250/// Connection statistics.
+251///
+252/// The fields here are a sum of the respective fields in the [`PathStats`] for all the
+253/// paths that exist as well as all paths that previously existed.
+254#[derive(Debug, Default, Clone)]
+255#[non_exhaustive]
+256pub struct ConnectionStats {
+257    /// Statistics about UDP datagrams transmitted on the connection.
+258    pub udp_tx: UdpStats,
+259    /// Statistics about UDP datagrams received on the connection.
+260    pub udp_rx: UdpStats,
+261    /// Statistics about frames transmitted on the connection.
+262    pub frame_tx: FrameStats,
+263    /// Statistics about frames received on the connection.
+264    pub frame_rx: FrameStats,
+265    /// The number of packets lost on the connection.
+266    pub lost_packets: u64,
+267    /// The number of bytes lost on the connection.
+268    pub lost_bytes: u64,
+269}
+270
+271impl std::ops::Add<PathStats> for ConnectionStats {
+272    type Output = Self;
+273
+274    fn add(self, rhs: PathStats) -> Self::Output {
+275        // Be aware that Connection::stats() relies on the fact this function ignores the
+276        // rtt, cwnd and current_mtu fields.
+277        let PathStats {
+278            rtt: _,
+279            udp_tx,
+280            udp_rx,
+281            frame_tx,
+282            frame_rx,
+283            cwnd: _,
+284            congestion_events: _,
+285            spurious_congestion_events: _,
+286            lost_packets,
+287            lost_bytes,
+288            sent_plpmtud_probes: _,
+289            lost_plpmtud_probes: _,
+290            black_holes_detected: _,
+291            current_mtu: _,
+292        } = rhs;
+293        Self {
+294            udp_tx: self.udp_tx + udp_tx,
+295            udp_rx: self.udp_rx + udp_rx,
+296            frame_tx: self.frame_tx + frame_tx,
+297            frame_rx: self.frame_rx + frame_rx,
+298            lost_packets: self.lost_packets + lost_packets,
+299            lost_bytes: self.lost_bytes + lost_bytes,
+300        }
+301    }
+302}
+303
+304impl std::ops::AddAssign<PathStats> for ConnectionStats {
+305    fn add_assign(&mut self, rhs: PathStats) {
+306        // Be aware that Connection::stats() relies on the fact this function ignores the
+307        // rtt, cwnd and current_mtu fields.
+308        let PathStats {
+309            rtt: _,
+310            udp_tx: path_udp_tx,
+311            udp_rx: path_udp_rx,
+312            frame_tx: path_frame_tx,
+313            frame_rx: path_frame_rx,
+314            cwnd: _,
+315            congestion_events: _,
+316            spurious_congestion_events: _,
+317            lost_packets: path_lost_packets,
+318            lost_bytes: path_lost_bytes,
+319            sent_plpmtud_probes: _,
+320            lost_plpmtud_probes: _,
+321            black_holes_detected: _,
+322            current_mtu: _,
+323        } = rhs;
+324        let Self {
+325            udp_tx,
+326            udp_rx,
+327            frame_tx,
+328            frame_rx,
+329            lost_packets,
+330            lost_bytes,
+331        } = self;
+332        *udp_tx += path_udp_tx;
+333        *udp_rx += path_udp_rx;
+334        *frame_tx += path_frame_tx;
+335        *frame_rx += path_frame_rx;
+336        *lost_packets += path_lost_packets;
+337        *lost_bytes += path_lost_bytes;
+338    }
+339}
+340
+341/// Helper to make [`PathStats`] infallibly available.
+342///
+343/// This helper also helps with borrowing issues compared to having the [`Self::for_path`]
+344/// function as a helper directly on [`Connection`].
+345///
+346/// [`Connection`]: super::Connection
+347#[derive(Debug, Default)]
+348pub(super) struct PathStatsMap(FxHashMap<PathId, PathStats>);
+349
+350impl PathStatsMap {
+351    /// Returns the [`PathStats`] for the path.
+352    pub(super) fn for_path(&mut self, path_id: PathId) -> &mut PathStats {
+353        self.0.entry(path_id).or_default()
+354    }
+355
+356    /// An iterator over all contained [`PathStats`].
+357    pub(super) fn iter_stats(&self) -> impl Iterator<Item = &PathStats> {
+358        self.0.values()
+359    }
+360
+361    /// Removes the stats for a given path.
+362    ///
+363    /// Only do this once you are discarding the path.
+364    pub(super) fn discard(&mut self, path_id: &PathId) -> PathStats {
+365        self.0.remove(path_id).unwrap_or_default()
+366    }
+367}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/streams/mod.rs.html b/pr/672/docs/src/noq_proto/connection/streams/mod.rs.html new file mode 100644 index 000000000..53414f7b5 --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/streams/mod.rs.html @@ -0,0 +1,573 @@ +mod.rs - source

noq_proto/connection/streams/
mod.rs

1use std::{
+2    collections::{BinaryHeap, hash_map},
+3    io,
+4};
+5
+6use bytes::Bytes;
+7use thiserror::Error;
+8use tracing::trace;
+9
+10use super::spaces::Retransmits;
+11use crate::{
+12    Dir, StreamId, VarInt,
+13    connection::streams::state::{get_or_insert_recv, get_or_insert_send},
+14    frame,
+15};
+16
+17mod recv;
+18use recv::Recv;
+19pub use recv::{Chunks, ReadError, ReadableError};
+20
+21mod send;
+22pub(crate) use send::{ByteSlice, BytesArray};
+23use send::{BytesSource, Send, SendState};
+24pub use send::{FinishError, WriteError};
+25pub(crate) use send::Written;
+26
+27mod state;
+28#[allow(unreachable_pub)] // fuzzing only
+29pub use state::StreamsState;
+30
+31/// Access to streams
+32pub struct Streams<'a> {
+33    pub(super) state: &'a mut StreamsState,
+34    pub(super) conn_state: &'a super::State,
+35}
+36
+37#[allow(clippy::needless_lifetimes)] // Needed for cfg(fuzzing)
+38impl<'a> Streams<'a> {
+39    #[cfg(fuzzing)]
+40    pub fn new(state: &'a mut StreamsState, conn_state: &'a super::State) -> Self {
+41        Self { state, conn_state }
+42    }
+43
+44    /// Open a single stream if possible
+45    ///
+46    /// Returns `None` if the streams in the given direction are currently exhausted.
+47    pub fn open(&mut self, dir: Dir) -> Option<StreamId> {
+48        if self.conn_state.is_closed() {
+49            return None;
+50        }
+51
+52        if self.state.next[dir as usize] >= self.state.max[dir as usize] {
+53            self.state.streams_blocked[dir as usize] = true;
+54            return None;
+55        }
+56
+57        self.state.next[dir as usize] += 1;
+58        let id = StreamId::new(self.state.side, dir, self.state.next[dir as usize] - 1);
+59        self.state.insert_local(id);
+60        self.state.send_streams += 1;
+61        Some(id)
+62    }
+63
+64    /// Accept a remotely initiated stream of a certain directionality, if possible
+65    ///
+66    /// Returns `None` if there are no new incoming streams for this connection.
+67    /// Has no impact on the data flow-control or stream concurrency limits.
+68    pub fn accept(&mut self, dir: Dir) -> Option<StreamId> {
+69        if self.state.next_remote[dir as usize] == self.state.next_reported_remote[dir as usize] {
+70            return None;
+71        }
+72
+73        let x = self.state.next_reported_remote[dir as usize];
+74        self.state.next_reported_remote[dir as usize] = x + 1;
+75        if dir == Dir::Bi {
+76            self.state.send_streams += 1;
+77        }
+78
+79        Some(StreamId::new(!self.state.side, dir, x))
+80    }
+81
+82    #[cfg(fuzzing)]
+83    pub fn state(&mut self) -> &mut StreamsState {
+84        self.state
+85    }
+86
+87    /// The number of streams that may have unacknowledged data.
+88    pub fn send_streams(&self) -> usize {
+89        self.state.send_streams
+90    }
+91
+92    /// The number of remotely initiated open streams of a certain directionality.
+93    ///
+94    /// Includes remotely initiated streams, which have not been accepted via [`accept`](Self::accept).
+95    /// These streams count against the respective concurrency limit reported by
+96    /// [`Connection::max_concurrent_streams`](super::Connection::max_concurrent_streams).
+97    pub fn remote_open_streams(&self, dir: Dir) -> u64 {
+98        // total opened - total closed = total opened - ( total permitted - total permitted unclosed )
+99        self.state.next_remote[dir as usize]
+100            - (self.state.max_remote[dir as usize]
+101                - self.state.allocated_remote_count[dir as usize])
+102    }
+103}
+104
+105/// Access to streams
+106pub struct RecvStream<'a> {
+107    pub(super) id: StreamId,
+108    pub(super) state: &'a mut StreamsState,
+109    pub(super) pending: &'a mut Retransmits,
+110}
+111
+112impl RecvStream<'_> {
+113    /// Read from the given recv stream
+114    ///
+115    /// `max_length` limits the maximum size of the returned `Bytes` value; passing `usize::MAX`
+116    /// will yield the best performance. `ordered` will make sure the returned chunk's offset will
+117    /// have an offset exactly equal to the previously returned offset plus the previously returned
+118    /// bytes' length.
+119    ///
+120    /// Yields `Ok(None)` if the stream was finished. Otherwise, yields a segment of data and its
+121    /// offset in the stream. If `ordered` is `false`, segments may be received in any order, and
+122    /// the `Chunk`'s `offset` field can be used to determine ordering in the caller.
+123    ///
+124    /// While most applications will prefer to consume stream data in order, unordered reads can
+125    /// improve performance when packet loss occurs and data cannot be retransmitted before the flow
+126    /// control window is filled. On any given stream, you can switch from ordered to unordered
+127    /// reads, but ordered reads on streams that have seen previous unordered reads will return
+128    /// `ReadError::IllegalOrderedRead`.
+129    pub fn read(&mut self, ordered: bool) -> Result<Chunks<'_>, ReadableError> {
+130        Chunks::new(self.id, ordered, self.state, self.pending)
+131    }
+132
+133    /// Stop accepting data on the given receive stream
+134    ///
+135    /// Discards unread data and notifies the peer to stop transmitting. Once stopped, further
+136    /// attempts to operate on a stream will yield `ClosedStream` errors.
+137    pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+138        let mut entry = match self.state.recv.entry(self.id) {
+139            hash_map::Entry::Occupied(s) => s,
+140            hash_map::Entry::Vacant(_) => return Err(ClosedStream { _private: () }),
+141        };
+142        let stream = get_or_insert_recv(self.state.stream_receive_window)(entry.get_mut());
+143
+144        let (read_credits, stop_sending) = stream.stop()?;
+145        if stop_sending.should_transmit() {
+146            self.pending.stop_sending.push(frame::StopSending {
+147                id: self.id,
+148                error_code,
+149            });
+150        }
+151
+152        // We need to keep stopped streams around until they're finished or reset so we can update
+153        // connection-level flow control to account for discarded data. Otherwise, we can discard
+154        // state immediately.
+155        if !stream.final_offset_unknown() {
+156            let recv = entry.remove().expect("must have recv when stopping");
+157            self.state.stream_recv_freed(self.id, recv);
+158        }
+159
+160        if self.state.add_read_credits(read_credits).should_transmit() {
+161            self.pending.max_data = true;
+162        }
+163
+164        Ok(())
+165    }
+166
+167    /// Returns the number of bytes read from this stream.
+168    ///
+169    /// This is the offset of the next byte to be read, i.e. the length of the contiguous
+170    /// prefix of the stream consumed by the application.
+171    pub fn bytes_read(&self) -> Result<u64, ClosedStream> {
+172        let recv = self
+173            .state
+174            .recv
+175            .get(&self.id)
+176            .and_then(|s| s.as_ref())
+177            .and_then(|s| s.as_open_recv())
+178            .ok_or(ClosedStream { _private: () })?;
+179        Ok(recv.assembler.bytes_read())
+180    }
+181
+182    /// Check whether this stream has been reset by the peer, returning the reset error code if so
+183    ///
+184    /// After returning `Ok(Some(_))` once, stream state will be discarded and all future calls will
+185    /// return `Err(ClosedStream)`.
+186    pub fn received_reset(&mut self) -> Result<Option<VarInt>, ClosedStream> {
+187        let hash_map::Entry::Occupied(entry) = self.state.recv.entry(self.id) else {
+188            return Err(ClosedStream { _private: () });
+189        };
+190        let Some(s) = entry.get().as_ref().and_then(|s| s.as_open_recv()) else {
+191            return Ok(None);
+192        };
+193        if s.stopped {
+194            return Err(ClosedStream { _private: () });
+195        }
+196        let Some(code) = s.reset_code() else {
+197            return Ok(None);
+198        };
+199
+200        // Clean up state after application observes the reset, since there's no reason for the
+201        // application to attempt to read or stop the stream once it knows it's reset
+202        let (_, recv) = entry.remove_entry();
+203        self.state
+204            .stream_recv_freed(self.id, recv.expect("must have recv on reset"));
+205        self.state.queue_max_stream_id(self.pending);
+206
+207        Ok(Some(code))
+208    }
+209}
+210
+211/// Access to streams
+212pub struct SendStream<'a> {
+213    pub(super) id: StreamId,
+214    pub(super) state: &'a mut StreamsState,
+215    pub(super) pending: &'a mut Retransmits,
+216    pub(super) conn_state: &'a super::State,
+217}
+218
+219#[allow(clippy::needless_lifetimes)] // Needed for cfg(fuzzing)
+220impl<'a> SendStream<'a> {
+221    #[cfg(fuzzing)]
+222    pub fn new(
+223        id: StreamId,
+224        state: &'a mut StreamsState,
+225        pending: &'a mut Retransmits,
+226        conn_state: &'a super::State,
+227    ) -> Self {
+228        Self {
+229            id,
+230            state,
+231            pending,
+232            conn_state,
+233        }
+234    }
+235
+236    /// Send data on the given stream
+237    ///
+238    /// Returns the number of bytes successfully written.
+239    pub fn write(&mut self, data: &[u8]) -> Result<usize, WriteError> {
+240        Ok(self.write_source(&mut ByteSlice::from_slice(data))?.bytes)
+241    }
+242
+243    /// Send data on the given stream
+244    ///
+245    /// Returns the number of bytes written and advances the provided `Bytes`
+246    /// slice, removing all completely written chunks.
+247    ///
+248    /// Note that this method might also write a partial chunk. In this case
+249    /// the chunk will be advanced and contain only non-written data after the call.
+250    pub fn write_chunks(&mut self, data: &mut &mut [Bytes]) -> Result<usize, WriteError> {
+251        let written = self.write_source(&mut BytesArray::from_chunks(data))?;
+252        *data = &mut std::mem::take(data)[written.chunks..];
+253        Ok(written.bytes)
+254    }
+255
+256    fn write_source<'b, B: BytesSource<'b>>(
+257        &mut self,
+258        source: &'b mut B,
+259    ) -> Result<Written, WriteError> {
+260        if self.conn_state.is_closed() {
+261            trace!(%self.id, "write blocked; connection draining");
+262            return Err(WriteError::Blocked);
+263        }
+264
+265        let limit = self.state.write_limit();
+266
+267        let max_send_data = self.state.max_send_data(self.id);
+268
+269        let stream = self
+270            .state
+271            .send
+272            .get_mut(&self.id)
+273            .map(get_or_insert_send(max_send_data))
+274            .ok_or(WriteError::ClosedStream)?;
+275
+276        if limit == 0 {
+277            trace!(
+278                stream = %self.id, max_data = self.state.max_data, data_sent = self.state.data_sent,
+279                "write blocked by connection-level flow control or send window"
+280            );
+281            if !stream.connection_blocked {
+282                stream.connection_blocked = true;
+283                self.state.connection_blocked.push(self.id);
+284            }
+285            return Err(WriteError::Blocked);
+286        }
+287
+288        let was_pending = stream.is_pending();
+289        let written = stream.write(source, limit)?;
+290        self.state.data_sent += written.bytes as u64;
+291        self.state.unacked_data += written.bytes as u64;
+292        trace!(stream = %self.id, "wrote {} bytes", written.bytes);
+293        if !was_pending {
+294            self.state.pending.push_pending(self.id, stream.priority);
+295        }
+296        Ok(written)
+297    }
+298
+299    /// Check if this stream was stopped, get the reason if it was
+300    pub fn stopped(&self) -> Result<Option<VarInt>, ClosedStream> {
+301        match self.state.send.get(&self.id).as_ref() {
+302            Some(Some(s)) => Ok(s.stop_reason),
+303            Some(None) => Ok(None),
+304            None => Err(ClosedStream { _private: () }),
+305        }
+306    }
+307
+308    /// Finish a send stream, signalling that no more data will be sent.
+309    ///
+310    /// If this fails, no [`StreamEvent::Finished`] will be generated.
+311    ///
+312    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+313    pub fn finish(&mut self) -> Result<(), FinishError> {
+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(FinishError::ClosedStream)?;
+321
+322        let was_pending = stream.is_pending();
+323        stream.finish()?;
+324        if !was_pending {
+325            self.state.pending.push_pending(self.id, stream.priority);
+326        }
+327
+328        Ok(())
+329    }
+330
+331    /// Abandon transmitting data on a stream
+332    ///
+333    /// # Panics
+334    /// - when applied to a receive stream
+335    pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+336        let max_send_data = self.state.max_send_data(self.id);
+337        let stream = self
+338            .state
+339            .send
+340            .get_mut(&self.id)
+341            .map(get_or_insert_send(max_send_data))
+342            .ok_or(ClosedStream { _private: () })?;
+343
+344        if matches!(stream.state, SendState::ResetSent) {
+345            // Redundant reset call
+346            return Err(ClosedStream { _private: () });
+347        }
+348
+349        // Restore the portion of the send window consumed by the data that we aren't about to
+350        // send. We leave flow control alone because the peer's responsible for issuing additional
+351        // credit based on the final offset communicated in the RESET_STREAM frame we send.
+352        self.state.unacked_data -= stream.pending.unacked();
+353        stream.reset();
+354        self.pending.reset_stream.push((self.id, error_code));
+355
+356        // Don't reopen an already-closed stream we haven't forgotten yet
+357        Ok(())
+358    }
+359
+360    /// Set the priority of a stream
+361    ///
+362    /// # Panics
+363    /// - when applied to a receive stream
+364    pub fn set_priority(&mut self, priority: i32) -> Result<(), ClosedStream> {
+365        let max_send_data = self.state.max_send_data(self.id);
+366        let stream = self
+367            .state
+368            .send
+369            .get_mut(&self.id)
+370            .map(get_or_insert_send(max_send_data))
+371            .ok_or(ClosedStream { _private: () })?;
+372
+373        stream.priority = priority;
+374        Ok(())
+375    }
+376
+377    /// Get the priority of a stream
+378    ///
+379    /// # Panics
+380    /// - when applied to a receive stream
+381    pub fn priority(&self) -> Result<i32, ClosedStream> {
+382        let stream = self
+383            .state
+384            .send
+385            .get(&self.id)
+386            .ok_or(ClosedStream { _private: () })?;
+387
+388        Ok(stream.as_ref().map(|s| s.priority).unwrap_or_default())
+389    }
+390}
+391
+392/// A queue of streams with pending outgoing data, sorted by priority
+393struct PendingStreamsQueue {
+394    streams: BinaryHeap<PendingStream>,
+395    /// The next stream to write out. This is `Some` when `TransportConfig::send_fairness(false)` and writing a stream is
+396    /// interrupted while the stream still has some pending data. See `reinsert_pending()`.
+397    next: Option<PendingStream>,
+398    /// A monotonically decreasing counter, used to implement round-robin scheduling for streams of the same priority.
+399    /// Underflowing is not a practical concern, as it is initialized to u64::MAX and only decremented by 1 in `push_pending`
+400    recency: u64,
+401}
+402
+403impl PendingStreamsQueue {
+404    fn new() -> Self {
+405        Self {
+406            streams: BinaryHeap::new(),
+407            next: None,
+408            recency: u64::MAX,
+409        }
+410    }
+411
+412    /// Reinsert a stream that was pending and still contains unsent data.
+413    fn reinsert_pending(&mut self, id: StreamId, priority: i32) {
+414        assert!(self.next.is_none());
+415
+416        self.next = Some(PendingStream {
+417            priority,
+418            recency: self.recency, // the value here doesn't really matter
+419            id,
+420        });
+421    }
+422
+423    /// Push a pending stream ID with the given priority, queued after any already-queued streams for the priority
+424    fn push_pending(&mut self, id: StreamId, priority: i32) {
+425        // Note that in the case where fairness is disabled, if we have a reinserted stream we don't
+426        // bump it even if priority > next.priority. In order to minimize fragmentation we
+427        // always try to complete a stream once part of it has been written.
+428
+429        // As the recency counter is monotonically decreasing, we know that using its value to sort this stream will queue it
+430        // after all other queued streams of the same priority.
+431        // This is enough to implement round-robin scheduling for streams that are still pending even after being handled,
+432        // as in that case they are removed from the `BinaryHeap`, handled, and then immediately reinserted.
+433        self.recency -= 1;
+434        self.streams.push(PendingStream {
+435            priority,
+436            recency: self.recency,
+437            id,
+438        });
+439    }
+440
+441    fn pop(&mut self) -> Option<PendingStream> {
+442        self.next.take().or_else(|| self.streams.pop())
+443    }
+444
+445    fn clear(&mut self) {
+446        self.next = None;
+447        self.streams.clear();
+448    }
+449
+450    fn iter(&self) -> impl Iterator<Item = &PendingStream> {
+451        self.next.iter().chain(self.streams.iter())
+452    }
+453
+454    #[cfg(test)]
+455    fn len(&self) -> usize {
+456        self.streams.len() + self.next.is_some() as usize
+457    }
+458}
+459
+460/// The [`StreamId`] of a stream with pending data queued, ordered by its priority and recency
+461#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
+462struct PendingStream {
+463    /// The priority of the stream
+464    // Note that this field should be kept above the `recency` field, in order for the `Ord` derive to be correct
+465    // (See https://doc.rust-lang.org/stable/std/cmp/trait.Ord.html#derivable)
+466    priority: i32,
+467    /// A tie-breaker for streams of the same priority, used to improve fairness by implementing round-robin scheduling:
+468    /// Larger values are prioritized, so it is initialised to `u64::MAX`, and when a stream writes data, we know
+469    /// that it currently has the highest recency value, so it is deprioritized by setting its recency to 1 less than the
+470    /// previous lowest recency value, such that all other streams of this priority will get processed once before we get back
+471    /// round to this one
+472    recency: u64,
+473    /// The ID of the stream
+474    // The way this type is used ensures that every instance has a unique `recency` value, so this field should be kept below
+475    // the `priority` and `recency` fields, so that it does not interfere with the behaviour of the `Ord` derive
+476    id: StreamId,
+477}
+478
+479/// Application events about streams
+480#[derive(Debug, PartialEq, Eq)]
+481pub enum StreamEvent {
+482    /// One or more new streams has been opened and might be readable
+483    Opened {
+484        /// Directionality for which streams have been opened
+485        dir: Dir,
+486    },
+487    /// A currently open stream likely has data or errors waiting to be read
+488    Readable {
+489        /// Which stream is now readable
+490        id: StreamId,
+491    },
+492    /// A formerly write-blocked stream might be ready for a write or have been stopped
+493    ///
+494    /// Only generated for streams that are currently open.
+495    Writable {
+496        /// Which stream is now writable
+497        id: StreamId,
+498    },
+499    /// A finished stream has been fully acknowledged or stopped
+500    Finished {
+501        /// Which stream has been finished
+502        id: StreamId,
+503    },
+504    /// The peer asked us to stop sending on an outgoing stream
+505    Stopped {
+506        /// Which stream has been stopped
+507        id: StreamId,
+508        /// Error code supplied by the peer
+509        error_code: VarInt,
+510    },
+511    /// At least one new stream of a certain directionality may be opened
+512    Available {
+513        /// Directionality for which streams are newly available
+514        dir: Dir,
+515    },
+516}
+517
+518/// Indicates whether a frame needs to be transmitted
+519///
+520/// This type wraps around bool and uses the `#[must_use]` attribute in order
+521/// to prevent accidental loss of the frame transmission requirement.
+522#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
+523#[must_use = "A frame might need to be enqueued"]
+524pub struct ShouldTransmit(bool);
+525
+526impl ShouldTransmit {
+527    /// Returns whether a frame should be transmitted
+528    pub fn should_transmit(self) -> bool {
+529        self.0
+530    }
+531}
+532
+533/// Error indicating that a stream has not been opened or has already been finished or reset
+534#[derive(Debug, Default, Error, Clone, PartialEq, Eq)]
+535#[error("closed stream")]
+536pub struct ClosedStream {
+537    _private: (),
+538}
+539
+540impl From<ClosedStream> for io::Error {
+541    fn from(x: ClosedStream) -> Self {
+542        Self::new(io::ErrorKind::NotConnected, x)
+543    }
+544}
+545
+546#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+547enum StreamHalf {
+548    Send,
+549    Recv,
+550}
+551
+552/// A helper trait to unify Bytes, `Vec<u8>` and `&[u8]` as sources of bytes
+553pub(super) trait BytesOrSlice<'a>: AsRef<[u8]> + 'a {
+554    fn len(&self) -> usize {
+555        self.as_ref().len()
+556    }
+557    fn is_empty(&self) -> bool {
+558        self.as_ref().is_empty()
+559    }
+560    fn into_bytes(self) -> Bytes;
+561}
+562
+563impl BytesOrSlice<'_> for Bytes {
+564    fn into_bytes(self) -> Bytes {
+565        self
+566    }
+567}
+568
+569impl<'a> BytesOrSlice<'a> for &'a [u8] {
+570    fn into_bytes(self) -> Bytes {
+571        Bytes::copy_from_slice(self)
+572    }
+573}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/streams/recv.rs.html b/pr/672/docs/src/noq_proto/connection/streams/recv.rs.html new file mode 100644 index 000000000..961b9961f --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/streams/recv.rs.html @@ -0,0 +1,544 @@ +recv.rs - source

noq_proto/connection/streams/
recv.rs

1use std::collections::hash_map::Entry;
+2use std::mem;
+3
+4use thiserror::Error;
+5use tracing::debug;
+6
+7use super::state::get_or_insert_recv;
+8use super::{ClosedStream, Retransmits, ShouldTransmit, StreamId, StreamsState};
+9use crate::connection::assembler::{Assembler, Chunk, IllegalOrderedRead};
+10use crate::connection::streams::state::StreamRecv;
+11use crate::{TransportError, VarInt, frame};
+12
+13#[derive(Debug, Default)]
+14pub(super) struct Recv {
+15    // NB: when adding or removing fields, remember to update `reinit`.
+16    state: RecvState,
+17    pub(super) assembler: Assembler,
+18    sent_max_stream_data: u64,
+19    pub(super) end: u64,
+20    pub(super) stopped: bool,
+21}
+22
+23impl Recv {
+24    pub(super) fn new(initial_max_data: u64) -> Box<Self> {
+25        Box::new(Self {
+26            state: RecvState::default(),
+27            assembler: Assembler::new(),
+28            sent_max_stream_data: initial_max_data,
+29            end: 0,
+30            stopped: false,
+31        })
+32    }
+33
+34    /// Reset to the initial state
+35    pub(super) fn reinit(&mut self, initial_max_data: u64) {
+36        self.state = RecvState::default();
+37        self.assembler.reinit();
+38        self.sent_max_stream_data = initial_max_data;
+39        self.end = 0;
+40        self.stopped = false;
+41    }
+42
+43    /// Process a STREAM frame
+44    ///
+45    /// Return value is `(number_of_new_bytes_ingested, stream_is_closed)`
+46    pub(super) fn ingest(
+47        &mut self,
+48        frame: frame::Stream,
+49        payload_len: usize,
+50        received: u64,
+51        max_data: u64,
+52    ) -> Result<(u64, bool), TransportError> {
+53        let end = frame.offset + frame.data.len() as u64;
+54        if end >= 2u64.pow(62) {
+55            return Err(TransportError::FLOW_CONTROL_ERROR(
+56                "maximum stream offset too large",
+57            ));
+58        }
+59
+60        if let Some(final_offset) = self.final_offset()
+61            && (end > final_offset || (frame.fin && end != final_offset))
+62        {
+63            debug!(end, final_offset, "final size error");
+64            return Err(TransportError::FINAL_SIZE_ERROR(""));
+65        }
+66
+67        let new_bytes = self.credit_consumed_by(end, received, max_data)?;
+68
+69        // Stopped streams don't need to wait for the actual data, they just need to know
+70        // how much there was.
+71        if frame.fin
+72            && !self.stopped
+73            && let RecvState::Recv { ref mut size } = self.state
+74        {
+75            *size = Some(end);
+76        }
+77
+78        self.end = self.end.max(end);
+79        // Don't bother storing data or releasing stream-level flow control credit if the stream's
+80        // already stopped
+81        if !self.stopped {
+82            self.assembler.insert(frame.offset, frame.data, payload_len);
+83        }
+84
+85        Ok((new_bytes, frame.fin && self.stopped))
+86    }
+87
+88    pub(super) fn stop(&mut self) -> Result<(u64, ShouldTransmit), ClosedStream> {
+89        if self.stopped {
+90            return Err(ClosedStream { _private: () });
+91        }
+92
+93        self.stopped = true;
+94        self.assembler.clear();
+95        // Issue flow control credit for unread data
+96        let read_credits = self.end - self.assembler.bytes_read();
+97        // This may send a spurious STOP_SENDING if we've already received all data, but it's a bit
+98        // fiddly to distinguish that from the case where we've received a FIN but are missing some
+99        // data that the peer might still be trying to retransmit, in which case a STOP_SENDING is
+100        // still useful.
+101        Ok((read_credits, ShouldTransmit(self.is_receiving())))
+102    }
+103
+104    /// Returns the window that should be advertised in a `MAX_STREAM_DATA` frame
+105    ///
+106    /// The method returns a tuple which consists of the window that should be
+107    /// announced, as well as a boolean parameter which indicates if a new
+108    /// transmission of the value is recommended. If the boolean value is
+109    /// `false` the new window should only be transmitted if a previous transmission
+110    /// had failed.
+111    pub(super) fn max_stream_data(&mut self, stream_receive_window: u64) -> (u64, ShouldTransmit) {
+112        let max_stream_data = self.assembler.bytes_read() + stream_receive_window;
+113
+114        // Only announce a window update if it's significant enough
+115        // to make it worthwhile sending a MAX_STREAM_DATA frame.
+116        // We use here a fraction of the configured stream receive window to make
+117        // the decision, and accommodate for streams using bigger windows requiring
+118        // less updates. A fixed size would also work - but it would need to be
+119        // smaller than `stream_receive_window` in order to make sure the stream
+120        // does not get stuck.
+121        let diff = max_stream_data - self.sent_max_stream_data;
+122        let transmit = self.can_send_flow_control() && diff >= (stream_receive_window / 8);
+123        (max_stream_data, ShouldTransmit(transmit))
+124    }
+125
+126    /// Records that a `MAX_STREAM_DATA` announcing a certain window was sent
+127    ///
+128    /// This will suppress enqueuing further `MAX_STREAM_DATA` frames unless
+129    /// either the previous transmission was not acknowledged or the window
+130    /// further increased.
+131    pub(super) fn record_sent_max_stream_data(&mut self, sent_value: u64) {
+132        if sent_value > self.sent_max_stream_data {
+133            self.sent_max_stream_data = sent_value;
+134        }
+135    }
+136
+137    /// Whether the total amount of data that the peer will send on this stream is unknown
+138    ///
+139    /// True until we've received either a reset or the final frame.
+140    ///
+141    /// Implies that the sender might benefit from stream-level flow control updates, and we might
+142    /// need to issue connection-level flow control updates due to flow control budget use by this
+143    /// stream in the future, even if it's been stopped.
+144    pub(super) fn final_offset_unknown(&self) -> bool {
+145        matches!(self.state, RecvState::Recv { size: None })
+146    }
+147
+148    /// Whether stream-level flow control updates should be sent for this stream
+149    pub(super) fn can_send_flow_control(&self) -> bool {
+150        // Stream-level flow control is redundant if the sender has already sent the whole stream,
+151        // and moot if we no longer want data on this stream.
+152        self.final_offset_unknown() && !self.stopped
+153    }
+154
+155    /// Whether data is still being accepted from the peer
+156    pub(super) fn is_receiving(&self) -> bool {
+157        matches!(self.state, RecvState::Recv { .. })
+158    }
+159
+160    fn final_offset(&self) -> Option<u64> {
+161        match self.state {
+162            RecvState::Recv { size } => size,
+163            RecvState::ResetRecvd { size, .. } => Some(size),
+164        }
+165    }
+166
+167    /// Returns `false` iff the reset was redundant
+168    pub(super) fn reset(
+169        &mut self,
+170        error_code: VarInt,
+171        final_offset: VarInt,
+172        received: u64,
+173        max_data: u64,
+174    ) -> Result<bool, TransportError> {
+175        // Validate final_offset
+176        if let Some(offset) = self.final_offset() {
+177            if offset != final_offset.into_inner() {
+178                return Err(TransportError::FINAL_SIZE_ERROR("inconsistent value"));
+179            }
+180        } else if self.end > u64::from(final_offset) {
+181            return Err(TransportError::FINAL_SIZE_ERROR(
+182                "lower than high water mark",
+183            ));
+184        }
+185        self.credit_consumed_by(final_offset.into(), received, max_data)?;
+186
+187        if matches!(self.state, RecvState::ResetRecvd { .. }) {
+188            return Ok(false);
+189        }
+190        self.state = RecvState::ResetRecvd {
+191            size: final_offset.into(),
+192            error_code,
+193        };
+194        // Nuke buffers so that future reads fail immediately, which ensures future reads don't
+195        // issue flow control credit redundant to that already issued. We could instead special-case
+196        // reset streams during read, but it's unclear if there's any benefit to retaining data for
+197        // reset streams.
+198        self.assembler.clear();
+199        Ok(true)
+200    }
+201
+202    pub(super) fn reset_code(&self) -> Option<VarInt> {
+203        match self.state {
+204            RecvState::ResetRecvd { error_code, .. } => Some(error_code),
+205            _ => None,
+206        }
+207    }
+208
+209    /// Compute the amount of flow control credit consumed, or return an error if more was consumed
+210    /// than issued
+211    fn credit_consumed_by(
+212        &self,
+213        offset: u64,
+214        received: u64,
+215        max_data: u64,
+216    ) -> Result<u64, TransportError> {
+217        let prev_end = self.end;
+218        let new_bytes = offset.saturating_sub(prev_end);
+219        if offset > self.sent_max_stream_data || received + new_bytes > max_data {
+220            debug!(
+221                received,
+222                new_bytes,
+223                max_data,
+224                offset,
+225                stream_max_data = self.sent_max_stream_data,
+226                "flow control error"
+227            );
+228            return Err(TransportError::FLOW_CONTROL_ERROR(""));
+229        }
+230
+231        Ok(new_bytes)
+232    }
+233}
+234
+235/// Chunks returned from [`RecvStream::read()`][crate::RecvStream::read].
+236///
+237/// ### Note: Finalization Needed
+238/// Bytes read from the stream are not released from the congestion window until
+239/// either [`Self::finalize()`] is called, or this type is dropped.
+240///
+241/// It is recommended that you call [`Self::finalize()`] because it returns a flag
+242/// telling you whether reading from the stream has resulted in the need to transmit a packet.
+243///
+244/// If this type is leaked, the stream will remain blocked on the remote peer until
+245/// another read from the stream is done.
+246pub struct Chunks<'a> {
+247    id: StreamId,
+248    ordered: bool,
+249    streams: &'a mut StreamsState,
+250    pending: &'a mut Retransmits,
+251    state: ChunksState,
+252    read: u64,
+253}
+254
+255impl<'a> Chunks<'a> {
+256    pub(super) fn new(
+257        id: StreamId,
+258        ordered: bool,
+259        streams: &'a mut StreamsState,
+260        pending: &'a mut Retransmits,
+261    ) -> Result<Self, ReadableError> {
+262        let mut entry = match streams.recv.entry(id) {
+263            Entry::Occupied(entry) => entry,
+264            Entry::Vacant(_) => return Err(ReadableError::ClosedStream),
+265        };
+266
+267        let mut recv =
+268            match get_or_insert_recv(streams.stream_receive_window)(entry.get_mut()).stopped {
+269                true => return Err(ReadableError::ClosedStream),
+270                false => entry.remove().unwrap().into_inner(), // this can't fail due to the previous get_or_insert_with
+271            };
+272
+273        recv.assembler.ensure_ordering(ordered)?;
+274        Ok(Self {
+275            id,
+276            ordered,
+277            streams,
+278            pending,
+279            state: ChunksState::Readable(recv),
+280            read: 0,
+281        })
+282    }
+283
+284    /// Next
+285    ///
+286    /// Should call finalize() when done calling this.
+287    pub fn next(&mut self, max_length: usize) -> Result<Option<Chunk>, ReadError> {
+288        let rs = match self.state {
+289            ChunksState::Readable(ref mut rs) => rs,
+290            ChunksState::Reset(error_code) => {
+291                return Err(ReadError::Reset(error_code));
+292            }
+293            ChunksState::Finished => {
+294                return Ok(None);
+295            }
+296            ChunksState::Finalized => panic!("must not call next() after finalize()"),
+297        };
+298
+299        if let Some(chunk) = rs.assembler.read(max_length, self.ordered) {
+300            self.read += chunk.bytes.len() as u64;
+301            return Ok(Some(chunk));
+302        }
+303
+304        match rs.state {
+305            RecvState::ResetRecvd { error_code, .. } => {
+306                debug_assert_eq!(self.read, 0, "reset streams have empty buffers");
+307                let state = mem::replace(&mut self.state, ChunksState::Reset(error_code));
+308                // At this point if we have `rs` self.state must be `ChunksState::Readable`
+309                let recv = match state {
+310                    ChunksState::Readable(recv) => StreamRecv::Open(recv),
+311                    _ => unreachable!("state must be ChunkState::Readable"),
+312                };
+313                self.streams.stream_recv_freed(self.id, recv);
+314                Err(ReadError::Reset(error_code))
+315            }
+316            RecvState::Recv { size } => {
+317                if size == Some(rs.end) && rs.assembler.bytes_read() == rs.end {
+318                    let state = mem::replace(&mut self.state, ChunksState::Finished);
+319                    // At this point if we have `rs` self.state must be `ChunksState::Readable`
+320                    let recv = match state {
+321                        ChunksState::Readable(recv) => StreamRecv::Open(recv),
+322                        _ => unreachable!("state must be ChunkState::Readable"),
+323                    };
+324                    self.streams.stream_recv_freed(self.id, recv);
+325                    Ok(None)
+326                } else {
+327                    // We don't need a distinct `ChunksState` variant for a blocked stream because
+328                    // retrying a read harmlessly re-traces our steps back to returning
+329                    // `Err(Blocked)` again. The buffers can't refill and the stream's own state
+330                    // can't change so long as this `Chunks` exists.
+331                    Err(ReadError::Blocked)
+332                }
+333            }
+334        }
+335    }
+336
+337    /// Mark the read data as consumed from the stream.
+338    ///
+339    /// The number of read bytes will be released from the congestion window,
+340    /// allowing the remote peer to send more data if it was previously blocked.
+341    ///
+342    /// If [`ShouldTransmit::should_transmit()`] returns `true`,
+343    /// a packet needs to be sent to the peer informing them that the stream is unblocked.
+344    /// This means that you should call [`Connection::poll_transmit()`][crate::Connection::poll_transmit]
+345    /// and send the returned packet as soon as is reasonable, to unblock the remote peer.
+346    pub fn finalize(mut self) -> ShouldTransmit {
+347        self.finalize_inner()
+348    }
+349
+350    fn finalize_inner(&mut self) -> ShouldTransmit {
+351        let state = mem::replace(&mut self.state, ChunksState::Finalized);
+352        if let ChunksState::Finalized = state {
+353            // Noop on repeated calls
+354            return ShouldTransmit(false);
+355        }
+356
+357        // We issue additional stream ID credit after the application is notified that a previously
+358        // open stream has finished or been reset and we've therefore disposed of its state, as
+359        // recorded by `stream_freed` calls in `next`.
+360        let mut should_transmit = self.streams.queue_max_stream_id(self.pending);
+361
+362        // If the stream hasn't finished, we may need to issue stream-level flow control credit
+363        if let ChunksState::Readable(mut rs) = state {
+364            let (_, max_stream_data) = rs.max_stream_data(self.streams.stream_receive_window);
+365            should_transmit |= max_stream_data.0;
+366            if max_stream_data.0 {
+367                self.pending.max_stream_data.insert(self.id);
+368            }
+369            // Return the stream to storage for future use
+370            self.streams
+371                .recv
+372                .insert(self.id, Some(StreamRecv::Open(rs)));
+373        }
+374
+375        // Issue connection-level flow control credit for any data we read regardless of state
+376        let max_data = self.streams.add_read_credits(self.read);
+377        self.pending.max_data |= max_data.0;
+378        should_transmit |= max_data.0;
+379        ShouldTransmit(should_transmit)
+380    }
+381}
+382
+383impl Drop for Chunks<'_> {
+384    fn drop(&mut self) {
+385        let _ = self.finalize_inner();
+386    }
+387}
+388
+389enum ChunksState {
+390    Readable(Box<Recv>),
+391    Reset(VarInt),
+392    Finished,
+393    Finalized,
+394}
+395
+396/// Errors triggered when reading from a recv stream
+397#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+398pub enum ReadError {
+399    /// No more data is currently available on this stream.
+400    ///
+401    /// If more data on this stream is received from the peer, an `Event::StreamReadable` will be
+402    /// generated for this stream, indicating that retrying the read might succeed.
+403    #[error("blocked")]
+404    Blocked,
+405    /// The peer abandoned transmitting data on this stream.
+406    ///
+407    /// Carries an application-defined error code.
+408    #[error("reset by peer: code {0}")]
+409    Reset(VarInt),
+410}
+411
+412/// Errors triggered when opening a recv stream for reading
+413#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+414pub enum ReadableError {
+415    /// The stream has not been opened or was already stopped, finished, or reset
+416    #[error("closed stream")]
+417    ClosedStream,
+418    /// Attempted an ordered read following an unordered read
+419    ///
+420    /// Performing an unordered read allows discontinuities to arise in the receive buffer of a
+421    /// stream which cannot be recovered, making further ordered reads impossible.
+422    #[error("ordered read after unordered read")]
+423    IllegalOrderedRead,
+424}
+425
+426impl From<IllegalOrderedRead> for ReadableError {
+427    fn from(_: IllegalOrderedRead) -> Self {
+428        Self::IllegalOrderedRead
+429    }
+430}
+431
+432#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+433enum RecvState {
+434    Recv { size: Option<u64> },
+435    ResetRecvd { size: u64, error_code: VarInt },
+436}
+437
+438impl Default for RecvState {
+439    fn default() -> Self {
+440        Self::Recv { size: None }
+441    }
+442}
+443
+444#[cfg(test)]
+445mod tests {
+446    use bytes::Bytes;
+447
+448    use crate::{Dir, Side};
+449
+450    use super::*;
+451
+452    #[test]
+453    fn reordered_frames_while_stopped() {
+454        const INITIAL_BYTES: u64 = 3;
+455        const INITIAL_OFFSET: u64 = 3;
+456        const RECV_WINDOW: u64 = 8;
+457        let mut s = Recv::new(RECV_WINDOW);
+458        let mut data_recvd = 0;
+459        // Receive bytes 3..6
+460        let (new_bytes, is_closed) = s
+461            .ingest(
+462                frame::Stream {
+463                    id: StreamId::new(Side::Client, Dir::Uni, 0),
+464                    offset: INITIAL_OFFSET,
+465                    fin: false,
+466                    data: Bytes::from_static(&[0; INITIAL_BYTES as usize]),
+467                },
+468                123,
+469                data_recvd,
+470                data_recvd + 1024,
+471            )
+472            .unwrap();
+473        data_recvd += new_bytes;
+474        assert_eq!(new_bytes, INITIAL_OFFSET + INITIAL_BYTES);
+475        assert!(!is_closed);
+476
+477        let (credits, transmit) = s.stop().unwrap();
+478        assert!(transmit.should_transmit());
+479        assert_eq!(
+480            credits,
+481            INITIAL_OFFSET + INITIAL_BYTES,
+482            "full connection flow control credit is issued by stop"
+483        );
+484
+485        let (max_stream_data, transmit) = s.max_stream_data(RECV_WINDOW);
+486        assert!(!transmit.should_transmit());
+487        assert_eq!(
+488            max_stream_data, RECV_WINDOW,
+489            "stream flow control credit isn't issued by stop"
+490        );
+491
+492        // Receive byte 7
+493        let (new_bytes, is_closed) = s
+494            .ingest(
+495                frame::Stream {
+496                    id: StreamId::new(Side::Client, Dir::Uni, 0),
+497                    offset: RECV_WINDOW - 1,
+498                    fin: false,
+499                    data: Bytes::from_static(&[0; 1]),
+500                },
+501                123,
+502                data_recvd,
+503                data_recvd + 1024,
+504            )
+505            .unwrap();
+506        data_recvd += new_bytes;
+507        assert_eq!(new_bytes, RECV_WINDOW - (INITIAL_OFFSET + INITIAL_BYTES));
+508        assert!(!is_closed);
+509
+510        let (max_stream_data, transmit) = s.max_stream_data(RECV_WINDOW);
+511        assert!(!transmit.should_transmit());
+512        assert_eq!(
+513            max_stream_data, RECV_WINDOW,
+514            "stream flow control credit isn't issued after stop"
+515        );
+516
+517        // Receive bytes 0..3
+518        let (new_bytes, is_closed) = s
+519            .ingest(
+520                frame::Stream {
+521                    id: StreamId::new(Side::Client, Dir::Uni, 0),
+522                    offset: 0,
+523                    fin: false,
+524                    data: Bytes::from_static(&[0; INITIAL_OFFSET as usize]),
+525                },
+526                123,
+527                data_recvd,
+528                data_recvd + 1024,
+529            )
+530            .unwrap();
+531        assert_eq!(
+532            new_bytes, 0,
+533            "reordered frames don't issue connection-level flow control for stopped streams"
+534        );
+535        assert!(!is_closed);
+536
+537        let (max_stream_data, transmit) = s.max_stream_data(RECV_WINDOW);
+538        assert!(!transmit.should_transmit());
+539        assert_eq!(
+540            max_stream_data, RECV_WINDOW,
+541            "stream flow control credit isn't issued after stop"
+542        );
+543    }
+544}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/streams/send.rs.html b/pr/672/docs/src/noq_proto/connection/streams/send.rs.html new file mode 100644 index 000000000..c6ab8ff77 --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/streams/send.rs.html @@ -0,0 +1,414 @@ +send.rs - source

noq_proto/connection/streams/
send.rs

1use bytes::Bytes;
+2use thiserror::Error;
+3
+4use crate::{
+5    VarInt,
+6    connection::{send_buffer::SendBuffer, streams::BytesOrSlice},
+7    frame,
+8};
+9
+10#[derive(Debug)]
+11pub(super) struct Send {
+12    pub(super) max_data: u64,
+13    pub(super) state: SendState,
+14    pub(super) pending: SendBuffer,
+15    pub(super) priority: i32,
+16    /// Whether a frame containing a FIN bit must be transmitted, even if we don't have any new data
+17    pub(super) fin_pending: bool,
+18    /// Whether this stream is in the `connection_blocked` list of `Streams`
+19    pub(super) connection_blocked: bool,
+20    /// The reason the peer wants us to stop, if `STOP_SENDING` was received
+21    pub(super) stop_reason: Option<VarInt>,
+22}
+23
+24impl Send {
+25    pub(super) fn new(max_data: VarInt) -> Box<Self> {
+26        Box::new(Self {
+27            max_data: max_data.into(),
+28            state: SendState::Ready,
+29            pending: SendBuffer::new(),
+30            priority: 0,
+31            fin_pending: false,
+32            connection_blocked: false,
+33            stop_reason: None,
+34        })
+35    }
+36
+37    /// Whether the stream has been reset
+38    pub(super) fn is_reset(&self) -> bool {
+39        matches!(self.state, SendState::ResetSent)
+40    }
+41
+42    pub(super) fn finish(&mut self) -> Result<(), FinishError> {
+43        if let Some(error_code) = self.stop_reason {
+44            Err(FinishError::Stopped(error_code))
+45        } else if self.state == SendState::Ready {
+46            self.state = SendState::DataSent {
+47                finish_acked: false,
+48            };
+49            self.fin_pending = true;
+50            Ok(())
+51        } else {
+52            Err(FinishError::ClosedStream)
+53        }
+54    }
+55
+56    pub(super) fn write<'a, S: BytesSource<'a>>(
+57        &mut self,
+58        source: &'a mut S,
+59        limit: u64,
+60    ) -> Result<Written, WriteError> {
+61        if !self.is_writable() {
+62            return Err(WriteError::ClosedStream);
+63        }
+64        if let Some(error_code) = self.stop_reason {
+65            return Err(WriteError::Stopped(error_code));
+66        }
+67        let budget = self.max_data - self.pending.offset();
+68        if budget == 0 {
+69            return Err(WriteError::Blocked);
+70        }
+71        let mut limit = limit.min(budget) as usize;
+72
+73        let mut result = Written::default();
+74        loop {
+75            let (chunk, chunks_consumed) = source.pop_chunk(limit);
+76            result.chunks += chunks_consumed;
+77            result.bytes += chunk.len();
+78
+79            if chunk.is_empty() {
+80                break;
+81            }
+82
+83            limit -= chunk.len();
+84            self.pending.write(chunk);
+85        }
+86
+87        Ok(result)
+88    }
+89
+90    /// Update stream state due to a reset sent by the local application
+91    pub(super) fn reset(&mut self) {
+92        use SendState::*;
+93        if let DataSent { .. } | Ready = self.state {
+94            self.state = ResetSent;
+95        }
+96    }
+97
+98    /// Handle STOP_SENDING
+99    ///
+100    /// Returns true if the stream was stopped due to this frame, and false
+101    /// if it had been stopped before
+102    pub(super) fn try_stop(&mut self, error_code: VarInt) -> bool {
+103        if self.stop_reason.is_none() {
+104            self.stop_reason = Some(error_code);
+105            true
+106        } else {
+107            false
+108        }
+109    }
+110
+111    /// Returns whether the stream has been finished and all data has been acknowledged by the peer
+112    pub(super) fn ack(&mut self, frame: frame::StreamMeta) -> bool {
+113        self.pending.ack(frame.offsets);
+114        match self.state {
+115            SendState::DataSent {
+116                ref mut finish_acked,
+117            } => {
+118                *finish_acked |= frame.fin;
+119                *finish_acked && self.pending.is_fully_acked()
+120            }
+121            _ => false,
+122        }
+123    }
+124
+125    /// Handle increase to stream-level flow control limit
+126    ///
+127    /// Returns whether the stream was unblocked
+128    pub(super) fn increase_max_data(&mut self, offset: u64) -> bool {
+129        if offset <= self.max_data || self.state != SendState::Ready {
+130            return false;
+131        }
+132        let was_blocked = self.pending.offset() == self.max_data;
+133        self.max_data = offset;
+134        was_blocked
+135    }
+136
+137    pub(super) fn offset(&self) -> u64 {
+138        self.pending.offset()
+139    }
+140
+141    pub(super) fn is_pending(&self) -> bool {
+142        self.pending.has_unsent_data() || self.fin_pending
+143    }
+144
+145    pub(super) fn is_writable(&self) -> bool {
+146        matches!(self.state, SendState::Ready)
+147    }
+148}
+149
+150/// A [`BytesSource`] implementation for `&'a mut [Bytes]`
+151///
+152/// The type allows to dequeue [`Bytes`] chunks from an array of chunks, up to
+153/// a configured limit.
+154pub(crate) struct BytesArray<'a> {
+155    /// The wrapped slice of `Bytes`
+156    chunks: &'a mut [Bytes],
+157    /// The amount of chunks consumed from this source
+158    consumed: usize,
+159}
+160
+161impl<'a> BytesArray<'a> {
+162    pub(crate) fn from_chunks(chunks: &'a mut [Bytes]) -> Self {
+163        Self {
+164            chunks,
+165            consumed: 0,
+166        }
+167    }
+168}
+169
+170impl<'a> BytesSource<'a> for BytesArray<'a> {
+171    fn pop_chunk<'b>(&'b mut self, limit: usize) -> (impl BytesOrSlice<'b>, usize)
+172    where
+173        'a: 'b,
+174    {
+175        // The loop exists to skip empty chunks while still marking them as
+176        // consumed
+177        let mut chunks_consumed = 0;
+178
+179        while self.consumed < self.chunks.len() {
+180            let chunk = &mut self.chunks[self.consumed];
+181
+182            if chunk.len() <= limit {
+183                let chunk = std::mem::take(chunk);
+184                self.consumed += 1;
+185                chunks_consumed += 1;
+186                if chunk.is_empty() {
+187                    continue;
+188                }
+189                return (chunk, chunks_consumed);
+190            } else if limit > 0 {
+191                let chunk = chunk.split_to(limit);
+192                return (chunk, chunks_consumed);
+193            } else {
+194                break;
+195            }
+196        }
+197
+198        (Bytes::new(), chunks_consumed)
+199    }
+200}
+201
+202/// A [`BytesSource`] implementation for `&[u8]`
+203///
+204/// The type allows to dequeue a single [`Bytes`] chunk, which will be lazily
+205/// created from a reference. This allows to defer the allocation until it is
+206/// known how much data needs to be copied.
+207pub(crate) struct ByteSlice<'a> {
+208    /// The wrapped byte slice
+209    data: &'a [u8],
+210}
+211
+212impl<'a> ByteSlice<'a> {
+213    pub(crate) fn from_slice(data: &'a [u8]) -> Self {
+214        Self { data }
+215    }
+216}
+217
+218impl<'a> BytesSource<'a> for ByteSlice<'a> {
+219    fn pop_chunk<'b>(&'b mut self, limit: usize) -> (impl BytesOrSlice<'b>, usize)
+220    where
+221        'a: 'b,
+222    {
+223        let limit = limit.min(self.data.len());
+224        if limit == 0 {
+225            return (&[][..], 0);
+226        }
+227
+228        let chunk = &self.data[..limit];
+229        self.data = &self.data[chunk.len()..];
+230
+231        let chunks_consumed = usize::from(self.data.is_empty());
+232        (chunk, chunks_consumed)
+233    }
+234}
+235
+236/// A source of one or more buffers which can be converted into `Bytes` buffers on demand
+237///
+238/// The purpose of this data type is to defer conversion as long as possible,
+239/// so that no heap allocation is required in case no data is writable.
+240pub(super) trait BytesSource<'a> {
+241    /// Returns the next chunk from the source of owned chunks.
+242    ///
+243    /// This method will consume parts of the source.
+244    /// Calling it will yield `Bytes` elements up to the configured `limit`.
+245    ///
+246    /// The method returns a tuple:
+247    /// - The first item is the yielded `Bytes` element. The element will be
+248    ///   empty if the limit is zero or no more data is available.
+249    /// - The second item returns how many complete chunks inside the source had
+250    ///   had been consumed. This can be less than 1, if a chunk inside the
+251    ///   source had been truncated in order to adhere to the limit. It can also
+252    ///   be more than 1, if zero-length chunks had been skipped.
+253    fn pop_chunk<'b>(&'b mut self, limit: usize) -> (impl BytesOrSlice<'b>, usize)
+254    where
+255        'a: 'b;
+256}
+257
+258/// Indicates how many bytes and chunks had been transferred in a write operation
+259#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
+260pub(crate) struct Written {
+261    /// The amount of bytes which had been written
+262    pub(crate) bytes: usize,
+263    /// The amount of full chunks which had been written
+264    ///
+265    /// If a chunk was only partially written, it will not be counted by this field.
+266    pub(crate) chunks: usize,
+267}
+268
+269/// Errors triggered while writing to a send stream
+270#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+271pub enum WriteError {
+272    /// The peer is not able to accept additional data, or the connection is congested.
+273    ///
+274    /// If the peer issues additional flow control credit, a [`StreamEvent::Writable`] event will
+275    /// be generated, indicating that retrying the write might succeed.
+276    ///
+277    /// [`StreamEvent::Writable`]: crate::StreamEvent::Writable
+278    #[error("unable to accept further writes")]
+279    Blocked,
+280    /// The peer is no longer accepting data on this stream, and it has been implicitly reset. The
+281    /// stream cannot be finished or further written to.
+282    ///
+283    /// Carries an application-defined error code.
+284    ///
+285    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+286    #[error("stopped by peer: code {0}")]
+287    Stopped(VarInt),
+288    /// The stream has not been opened or has already been finished or reset
+289    #[error("closed stream")]
+290    ClosedStream,
+291}
+292
+293#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+294pub(super) enum SendState {
+295    /// Sending new data
+296    Ready,
+297    /// Stream was finished; now sending retransmits only
+298    DataSent { finish_acked: bool },
+299    /// Sent RESET
+300    ResetSent,
+301}
+302
+303/// Reasons why attempting to finish a stream might fail
+304#[derive(Debug, Error, Clone, PartialEq, Eq)]
+305pub enum FinishError {
+306    /// The peer is no longer accepting data on this stream. No
+307    /// [`StreamEvent::Finished`] event will be emitted for this stream.
+308    ///
+309    /// Carries an application-defined error code.
+310    ///
+311    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+312    #[error("stopped by peer: code {0}")]
+313    Stopped(VarInt),
+314    /// The stream has not been opened or was already finished or reset
+315    #[error("closed stream")]
+316    ClosedStream,
+317}
+318
+319#[cfg(test)]
+320mod tests {
+321    use super::*;
+322
+323    #[test]
+324    fn bytes_array() {
+325        let full = b"Hello World 123456789 ABCDEFGHJIJKLMNOPQRSTUVWXYZ".to_owned();
+326        for limit in 0..full.len() {
+327            let mut chunks = [
+328                Bytes::from_static(b""),
+329                Bytes::from_static(b"Hello "),
+330                Bytes::from_static(b"Wo"),
+331                Bytes::from_static(b""),
+332                Bytes::from_static(b"r"),
+333                Bytes::from_static(b"ld"),
+334                Bytes::from_static(b""),
+335                Bytes::from_static(b" 12345678"),
+336                Bytes::from_static(b"9 ABCDE"),
+337                Bytes::from_static(b"F"),
+338                Bytes::from_static(b"GHJIJKLMNOPQRSTUVWXYZ"),
+339            ];
+340            let num_chunks = chunks.len();
+341            let last_chunk_len = chunks[chunks.len() - 1].len();
+342
+343            let mut array = BytesArray::from_chunks(&mut chunks);
+344
+345            let mut buf = Vec::new();
+346            let mut chunks_popped = 0;
+347            let mut chunks_consumed = 0;
+348            let mut remaining = limit;
+349            loop {
+350                let (chunk, consumed) = array.pop_chunk(remaining);
+351                chunks_consumed += consumed;
+352
+353                if !chunk.is_empty() {
+354                    buf.extend_from_slice(chunk.as_ref());
+355                    remaining -= chunk.len();
+356                    chunks_popped += 1;
+357                } else {
+358                    break;
+359                }
+360            }
+361
+362            assert_eq!(&buf[..], &full[..limit]);
+363
+364            if limit == full.len() {
+365                // Full consumption of the last chunk
+366                assert_eq!(chunks_consumed, num_chunks);
+367                // Since there are empty chunks, we consume more than there are popped
+368                assert_eq!(chunks_consumed, chunks_popped + 3);
+369            } else if limit > full.len() - last_chunk_len {
+370                // Partial consumption of the last chunk
+371                assert_eq!(chunks_consumed, num_chunks - 1);
+372                assert_eq!(chunks_consumed, chunks_popped + 2);
+373            }
+374        }
+375    }
+376
+377    #[test]
+378    fn byte_slice() {
+379        let full = b"Hello World 123456789 ABCDEFGHJIJKLMNOPQRSTUVWXYZ".to_owned();
+380        for limit in 0..full.len() {
+381            let mut array = ByteSlice::from_slice(&full[..]);
+382
+383            let mut buf = Vec::new();
+384            let mut chunks_popped = 0;
+385            let mut chunks_consumed = 0;
+386            let mut remaining = limit;
+387            loop {
+388                let (chunk, consumed) = array.pop_chunk(remaining);
+389                chunks_consumed += consumed;
+390
+391                if !chunk.is_empty() {
+392                    buf.extend_from_slice(chunk.as_ref());
+393                    remaining -= chunk.len();
+394                    chunks_popped += 1;
+395                } else {
+396                    break;
+397                }
+398            }
+399
+400            assert_eq!(&buf[..], &full[..limit]);
+401            if limit != 0 {
+402                assert_eq!(chunks_popped, 1);
+403            } else {
+404                assert_eq!(chunks_popped, 0);
+405            }
+406
+407            if limit == full.len() {
+408                assert_eq!(chunks_consumed, 1);
+409            } else {
+410                assert_eq!(chunks_consumed, 0);
+411            }
+412        }
+413    }
+414}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/streams/state.rs.html b/pr/672/docs/src/noq_proto/connection/streams/state.rs.html new file mode 100644 index 000000000..eb0ee432a --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/streams/state.rs.html @@ -0,0 +1,2230 @@ +state.rs - source

noq_proto/connection/streams/
state.rs

1use std::{
+2    collections::{VecDeque, hash_map},
+3    convert::TryFrom,
+4    mem,
+5};
+6
+7use rustc_hash::FxHashMap;
+8use tracing::{debug, trace};
+9
+10use super::{
+11    PendingStreamsQueue, Recv, Retransmits, Send, SendState, ShouldTransmit, StreamEvent,
+12    StreamHalf,
+13};
+14use crate::{
+15    Dir, MAX_STREAM_COUNT, Side, StreamId, TransportError, VarInt,
+16    connection::{PacketBuilder, stats::FrameStats},
+17    frame::{self, FrameStruct},
+18    transport_parameters::TransportParameters,
+19};
+20
+21/// Wrapper around `Recv` that facilitates reusing `Recv` instances
+22#[derive(Debug)]
+23pub(super) enum StreamRecv {
+24    /// A `Recv` that is ready to be opened
+25    Free(Box<Recv>),
+26    /// A `Recv` that has been opened
+27    Open(Box<Recv>),
+28}
+29
+30impl StreamRecv {
+31    /// Returns a reference to the inner `Recv` if the stream is open
+32    pub(super) fn as_open_recv(&self) -> Option<&Recv> {
+33        match self {
+34            Self::Open(r) => Some(r),
+35            _ => None,
+36        }
+37    }
+38
+39    // Returns a mutable reference to the inner `Recv` if the stream is open
+40    pub(super) fn as_open_recv_mut(&mut self) -> Option<&mut Recv> {
+41        match self {
+42            Self::Open(r) => Some(r),
+43            _ => None,
+44        }
+45    }
+46
+47    // Returns the inner `Recv`
+48    pub(super) fn into_inner(self) -> Box<Recv> {
+49        match self {
+50            Self::Free(r) | Self::Open(r) => r,
+51        }
+52    }
+53
+54    // Reinitialize the stream so the inner `Recv` can be reused
+55    pub(super) fn free(self, initial_max_data: u64) -> Self {
+56        match self {
+57            Self::Free(_) => unreachable!("Self::Free on reinit()"),
+58            Self::Open(mut recv) => {
+59                recv.reinit(initial_max_data);
+60                Self::Free(recv)
+61            }
+62        }
+63    }
+64}
+65
+66#[allow(unreachable_pub)] // fuzzing only
+67pub struct StreamsState {
+68    pub(super) side: Side,
+69    // Set of streams that are currently open, or could be immediately opened by the peer
+70    pub(super) send: FxHashMap<StreamId, Option<Box<Send>>>,
+71    pub(super) recv: FxHashMap<StreamId, Option<StreamRecv>>,
+72    pub(super) free_recv: Vec<StreamRecv>,
+73    pub(super) next: [u64; 2],
+74    /// Maximum number of locally-initiated streams that may be opened over the lifetime of the
+75    /// connection so far, per direction
+76    pub(super) max: [u64; 2],
+77    /// Maximum number of remotely-initiated streams that may be opened over the lifetime of the
+78    /// connection so far, per direction
+79    pub(super) max_remote: [u64; 2],
+80    /// Value of `max_remote` most recently transmitted to the peer in a `MAX_STREAMS` frame
+81    sent_max_remote: [u64; 2],
+82    /// Number of streams that we've given the peer permission to open and which aren't fully closed
+83    pub(super) allocated_remote_count: [u64; 2],
+84    /// Size of the desired stream flow control window. May be smaller than `allocated_remote_count`
+85    /// due to `set_max_concurrent` calls.
+86    max_concurrent_remote_count: [u64; 2],
+87    /// Whether `max_concurrent_remote_count` has ever changed
+88    flow_control_adjusted: bool,
+89    /// Lowest remotely-initiated stream index that haven't actually been opened by the peer
+90    pub(super) next_remote: [u64; 2],
+91    /// Whether the remote endpoint has opened any streams the application doesn't know about yet,
+92    /// per directionality
+93    opened: [bool; 2],
+94    // Next to report to the application, once opened
+95    pub(super) next_reported_remote: [u64; 2],
+96    /// Number of outbound streams
+97    ///
+98    /// This differs from `self.send.len()` in that it does not include streams that the peer is
+99    /// permitted to open but which have not yet been opened.
+100    pub(super) send_streams: usize,
+101    /// Streams with outgoing data queued, sorted by priority
+102    pub(super) pending: PendingStreamsQueue,
+103
+104    events: VecDeque<StreamEvent>,
+105    /// Streams blocked on connection-level flow control or stream window space
+106    ///
+107    /// Streams are only added to this list when a write fails.
+108    pub(super) connection_blocked: Vec<StreamId>,
+109    /// Connection-level flow control budget dictated by the peer
+110    pub(super) max_data: u64,
+111    /// The initial receive window
+112    receive_window: u64,
+113    /// Limit on incoming data, which is transmitted through `MAX_DATA` frames
+114    local_max_data: u64,
+115    /// The last value of `MAX_DATA` which had been queued for transmission in
+116    /// an outgoing `MAX_DATA` frame
+117    sent_max_data: VarInt,
+118    /// Sum of current offsets of all send streams.
+119    pub(super) data_sent: u64,
+120    /// Sum of end offsets of all receive streams. Includes gaps, so it's an upper bound.
+121    data_recvd: u64,
+122    /// Total quantity of unacknowledged outgoing data
+123    pub(super) unacked_data: u64,
+124    /// Configured upper bound for `unacked_data`.
+125    ///
+126    /// Note this may be less than `unacked_data` if the user has set a new value.
+127    pub(super) send_window: u64,
+128    /// Configured upper bound for how much unacked data the peer can send us per stream
+129    pub(super) stream_receive_window: u64,
+130
+131    // Pertinent state from the TransportParameters supplied by the peer
+132    initial_max_stream_data_uni: VarInt,
+133    initial_max_stream_data_bidi_local: VarInt,
+134    initial_max_stream_data_bidi_remote: VarInt,
+135
+136    /// The shrink to be applied to local_max_data when receive_window is shrunk
+137    receive_window_shrink_debt: u64,
+138    /// Whether the locally-initiated stream limit has been hit, per direction
+139    pub(super) streams_blocked: [bool; 2],
+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        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            streams_blocked: [false, false],
+185        }
+186    }
+187
+188    pub(crate) fn set_params(&mut self, params: &TransportParameters) {
+189        self.initial_max_stream_data_uni = params.initial_max_stream_data_uni;
+190        self.initial_max_stream_data_bidi_local = params.initial_max_stream_data_bidi_local;
+191        self.initial_max_stream_data_bidi_remote = params.initial_max_stream_data_bidi_remote;
+192        self.max[Dir::Bi as usize] = params.initial_max_streams_bidi.into();
+193        self.max[Dir::Uni as usize] = params.initial_max_streams_uni.into();
+194        self.received_max_data(params.initial_max_data);
+195        for (&id, slot) in self.send.iter_mut() {
+196            if id.initiator() != self.side
+197                && id.dir() == Dir::Bi
+198                && let Some(s) = slot.as_mut()
+199            {
+200                s.max_data = params.initial_max_stream_data_bidi_local.into();
+201            }
+202        }
+203    }
+204
+205    /// Ensure we have space for at least a full flow control window of remotely-initiated streams
+206    /// to be open, and notify the peer if the window has moved
+207    fn ensure_remote_streams(&mut self, dir: Dir) {
+208        let new_count = self.max_concurrent_remote_count[dir as usize]
+209            .saturating_sub(self.allocated_remote_count[dir as usize]);
+210        self.allocated_remote_count[dir as usize] += new_count;
+211        self.max_remote[dir as usize] += new_count;
+212    }
+213
+214    pub(crate) fn zero_rtt_rejected(&mut self) {
+215        // Revert to initial state for outgoing streams
+216        for dir in Dir::iter() {
+217            for i in 0..self.next[dir as usize] {
+218                // We don't bother calling `stream_freed` here because we explicitly reset affected
+219                // counters below.
+220                let id = StreamId::new(self.side, dir, i);
+221                self.send.remove(&id).unwrap();
+222                if let Dir::Bi = dir {
+223                    self.recv.remove(&id).unwrap();
+224                }
+225            }
+226            self.next[dir as usize] = 0;
+227
+228            // If 0-RTT was rejected, any flow control frames we sent were lost.
+229            if self.flow_control_adjusted {
+230                // Conservative approximation of whatever we sent in transport parameters
+231                self.sent_max_remote[dir as usize] = 0;
+232            }
+233        }
+234
+235        self.pending.clear();
+236        self.send_streams = 0;
+237        self.data_sent = 0;
+238        self.connection_blocked.clear();
+239    }
+240
+241    /// Process incoming stream frame
+242    ///
+243    /// If successful, returns whether a `MAX_DATA` frame needs to be transmitted
+244    pub(crate) fn received(
+245        &mut self,
+246        frame: frame::Stream,
+247        payload_len: usize,
+248    ) -> Result<ShouldTransmit, TransportError> {
+249        let id = frame.id;
+250        self.validate_receive_id(id).inspect_err(|_e| {
+251            debug!("received illegal STREAM frame");
+252        })?;
+253
+254        // Create state for this stream if the remote peer created it.
+255        let newly_created = self.ensure_remote(id);
+256
+257        let Some(rs) = self
+258            .recv
+259            .get_mut(&id)
+260            .map(get_or_insert_recv(self.stream_receive_window))
+261        else {
+262            trace!("dropping frame for closed stream");
+263            return Ok(ShouldTransmit(false));
+264        };
+265
+266        if !rs.is_receiving() {
+267            trace!("dropping frame for finished stream");
+268            return Ok(ShouldTransmit(false));
+269        }
+270
+271        let (new_bytes, closed) =
+272            rs.ingest(frame, payload_len, self.data_recvd, self.local_max_data)?;
+273        self.data_recvd = self.data_recvd.saturating_add(new_bytes);
+274
+275        if !rs.stopped {
+276            // Newly opened streams are inherently readable; the app discovers them via
+277            // `StreamEvent::Opened` and a separate `Readable` would be redundant.
+278            if !newly_created {
+279                self.events.push_back(StreamEvent::Readable { id });
+280            }
+281            return Ok(ShouldTransmit(false));
+282        }
+283
+284        // Stopped streams become closed instantly on FIN, so check whether we need to clean up
+285        if closed {
+286            let rs = self.recv.remove(&id).flatten().unwrap();
+287            self.stream_recv_freed(id, rs);
+288        }
+289
+290        // We don't buffer data on stopped streams, so issue flow control credit immediately
+291        Ok(self.add_read_credits(new_bytes))
+292    }
+293
+294    /// Process incoming RESET_STREAM frame
+295    ///
+296    /// If successful, returns whether a `MAX_DATA` frame needs to be transmitted
+297    #[allow(unreachable_pub)] // fuzzing only
+298    pub fn received_reset(
+299        &mut self,
+300        frame: frame::ResetStream,
+301    ) -> Result<ShouldTransmit, TransportError> {
+302        let frame::ResetStream {
+303            id,
+304            error_code,
+305            final_offset,
+306        } = frame;
+307        self.validate_receive_id(id).inspect_err(|_e| {
+308            debug!("received illegal RESET_STREAM frame");
+309        })?;
+310
+311        // Create state for this stream if the remote peer created it.
+312        let newly_created = self.ensure_remote(id);
+313
+314        let Some(rs) = self
+315            .recv
+316            .get_mut(&id)
+317            .map(get_or_insert_recv(self.stream_receive_window))
+318        else {
+319            trace!("received RESET_STREAM on closed stream");
+320            return Ok(ShouldTransmit(false));
+321        };
+322
+323        // State transition
+324        if !rs.reset(
+325            error_code,
+326            final_offset,
+327            self.data_recvd,
+328            self.local_max_data,
+329        )? {
+330            // Redundant reset
+331            return Ok(ShouldTransmit(false));
+332        }
+333        let bytes_read = rs.assembler.bytes_read();
+334        let stopped = rs.stopped;
+335        let end = rs.end;
+336        if stopped {
+337            // Stopped streams should be disposed immediately on reset
+338            let rs = self.recv.remove(&id).flatten().unwrap();
+339            self.stream_recv_freed(id, rs);
+340        } else if !newly_created {
+341            // Newly opened streams are inherently readable; the app discovers them via
+342            // `StreamEvent::Opened` and a separate `Readable` would be redundant.
+343            self.events.push_back(StreamEvent::Readable { id });
+344        }
+345
+346        // Update connection-level flow control
+347        Ok(if bytes_read != final_offset.into_inner() {
+348            // bytes_read is always <= end, so this won't underflow.
+349            self.data_recvd = self
+350                .data_recvd
+351                .saturating_add(u64::from(final_offset) - end);
+352            self.add_read_credits(u64::from(final_offset) - bytes_read)
+353        } else {
+354            ShouldTransmit(false)
+355        })
+356    }
+357
+358    /// Process incoming `STOP_SENDING` frame
+359    #[allow(unreachable_pub)] // fuzzing only
+360    pub fn received_stop_sending(&mut self, id: StreamId, error_code: VarInt) {
+361        // Create state for this stream if the remote peer created it.
+362        self.ensure_remote(id);
+363
+364        let max_send_data = self.max_send_data(id);
+365        let Some(stream) = self
+366            .send
+367            .get_mut(&id)
+368            .map(get_or_insert_send(max_send_data))
+369        else {
+370            return;
+371        };
+372
+373        if stream.try_stop(error_code) {
+374            self.events
+375                .push_back(StreamEvent::Stopped { id, error_code });
+376        }
+377    }
+378
+379    pub(crate) fn reset_acked(&mut self, id: StreamId) {
+380        match self.send.entry(id) {
+381            hash_map::Entry::Vacant(_) => {}
+382            hash_map::Entry::Occupied(e) => {
+383                if let Some(SendState::ResetSent) = e.get().as_ref().map(|s| s.state) {
+384                    e.remove_entry();
+385                    self.stream_freed(id, StreamHalf::Send);
+386                }
+387            }
+388        }
+389    }
+390
+391    /// Whether any stream data is queued, regardless of control frames
+392    pub(crate) fn can_send_stream_data(&self) -> bool {
+393        // Reset streams may linger in the pending stream list, but will never produce stream frames
+394        self.pending.iter().any(|stream| {
+395            self.send
+396                .get(&stream.id)
+397                .and_then(|s| s.as_ref())
+398                .is_some_and(|s| !s.is_reset())
+399        })
+400    }
+401
+402    /// Whether MAX_STREAM_DATA frames could be sent for stream `id`
+403    pub(crate) fn can_send_flow_control(&self, id: StreamId) -> bool {
+404        self.recv
+405            .get(&id)
+406            .and_then(|s| s.as_ref())
+407            .and_then(|s| s.as_open_recv())
+408            .is_some_and(|s| s.can_send_flow_control())
+409    }
+410
+411    pub(in crate::connection) fn write_control_frames<'a, 'b>(
+412        &mut self,
+413        builder: &mut PacketBuilder<'a, 'b>,
+414        pending: &mut Retransmits,
+415        stats: &mut FrameStats,
+416    ) {
+417        // RESET_STREAM
+418        while builder.frame_space_remaining() > frame::ResetStream::SIZE_BOUND {
+419            let Some((id, error_code)) = pending.reset_stream.pop() else {
+420                break;
+421            };
+422            let Some(stream) = self.send.get_mut(&id).and_then(|s| s.as_mut()) else {
+423                continue;
+424            };
+425            let frame = frame::ResetStream {
+426                id,
+427                error_code,
+428                final_offset: VarInt::try_from(stream.offset()).expect("impossibly large offset"),
+429            };
+430            builder.write_frame(frame, stats);
+431        }
+432
+433        // STOP_SENDING
+434        while builder.frame_space_remaining() > frame::StopSending::SIZE_BOUND {
+435            let Some(frame) = pending.stop_sending.pop() else {
+436                break;
+437            };
+438            // We may need to transmit STOP_SENDING even for streams whose state we have discarded,
+439            // because we are able to discard local state for stopped streams immediately upon
+440            // receiving FIN, even if the peer still has arbitrarily large amounts of data to
+441            // (re)transmit due to loss or unconventional sending strategy. We could fine-tune this
+442            // a little by dropping the frame if we specifically know the stream's been reset by the
+443            // peer, but we discard that information as soon as the application consumes it, so it
+444            // can't be relied upon regardless.
+445            builder.write_frame(frame, stats);
+446        }
+447
+448        // MAX_DATA
+449        if pending.max_data && builder.frame_space_remaining() > 9 {
+450            pending.max_data = false;
+451
+452            // `local_max_data` can grow bigger than `VarInt`.
+453            // For transmission inside QUIC frames we need to clamp it to the
+454            // maximum allowed `VarInt` size.
+455            let max = VarInt::try_from(self.local_max_data).unwrap_or(VarInt::MAX);
+456
+457            if max > self.sent_max_data {
+458                // Record that a `MAX_DATA` announcing a certain window was sent. This will
+459                // suppress enqueuing further `MAX_DATA` frames unless either the previous
+460                // transmission was not acknowledged or the window further increased.
+461                self.sent_max_data = max;
+462            }
+463
+464            builder.write_frame(frame::MaxData(max), stats);
+465        }
+466
+467        // MAX_STREAM_DATA
+468        while builder.frame_space_remaining() > 17 {
+469            let id = match pending.max_stream_data.iter().next() {
+470                Some(x) => *x,
+471                None => break,
+472            };
+473            pending.max_stream_data.remove(&id);
+474            let Some(rs) = self
+475                .recv
+476                .get_mut(&id)
+477                .and_then(|s| s.as_mut())
+478                .and_then(|s| s.as_open_recv_mut())
+479            else {
+480                continue;
+481            };
+482            if !rs.can_send_flow_control() {
+483                continue;
+484            }
+485
+486            let (max, _) = rs.max_stream_data(self.stream_receive_window);
+487            rs.record_sent_max_stream_data(max);
+488            builder.write_frame(frame::MaxStreamData { id, offset: max }, stats);
+489        }
+490
+491        // MAX_STREAMS
+492        for dir in Dir::iter() {
+493            if !pending.max_stream_id[dir as usize] || builder.frame_space_remaining() <= 9 {
+494                continue;
+495            }
+496
+497            pending.max_stream_id[dir as usize] = false;
+498            self.sent_max_remote[dir as usize] = self.max_remote[dir as usize];
+499            let count = self.max_remote[dir as usize];
+500            builder.write_frame(frame::MaxStreams { dir, count }, stats);
+501        }
+502
+503        // STREAMS_BLOCKED
+504        for dir in Dir::iter() {
+505            if self.streams_blocked[dir as usize] {
+506                pending.streams_blocked[dir as usize] = true;
+507                self.streams_blocked[dir as usize] = false;
+508            }
+509
+510            if !pending.streams_blocked[dir as usize] || builder.frame_space_remaining() <= 9 {
+511                continue;
+512            }
+513
+514            pending.streams_blocked[dir as usize] = false;
+515            let limit = self.max[dir as usize];
+516            builder.write_frame(frame::StreamsBlocked { dir, limit }, stats);
+517        }
+518    }
+519
+520    pub(in crate::connection) fn write_stream_frames<'a, 'b>(
+521        &mut self,
+522        builder: &mut PacketBuilder<'a, 'b>,
+523        fair: bool,
+524        stats: &mut FrameStats,
+525    ) {
+526        while builder.frame_space_remaining() > frame::Stream::SIZE_BOUND {
+527            // Pop the stream of the highest priority that currently has pending data. If
+528            // the stream still has some pending data left after writing, it will be
+529            // reinserted, otherwise not
+530            let Some(stream) = self.pending.pop() else {
+531                break;
+532            };
+533
+534            let id = stream.id;
+535
+536            let Some(stream) = self.send.get_mut(&id).and_then(|s| s.as_mut()) else {
+537                // Stream was reset with pending data and the reset was acknowledged
+538                continue;
+539            };
+540
+541            // Reset streams aren't removed from the pending list and still exist while the peer
+542            // hasn't acknowledged the reset, but should not generate STREAM frames, so we need to
+543            // check for them explicitly.
+544            if stream.is_reset() {
+545                continue;
+546            }
+547
+548            // Now that we know the `StreamId`, we can better account for how many bytes
+549            // are required to encode it.
+550            let max_buf_size = builder.frame_space_remaining() - 1 - VarInt::size(id.into());
+551            let (offsets, encode_length) = stream.pending.poll_transmit(max_buf_size);
+552            let fin = offsets.end == stream.pending.offset()
+553                && matches!(stream.state, SendState::DataSent { .. });
+554            if fin {
+555                stream.fin_pending = false;
+556            }
+557
+558            if stream.is_pending() {
+559                // If the stream still has pending data, reinsert it, possibly with an updated priority value
+560                // Fairness with other streams is achieved by implementing round-robin scheduling,
+561                // so that the other streams will have a chance to write data
+562                // before we touch this stream again.
+563                if fair {
+564                    self.pending.push_pending(id, stream.priority);
+565                } else {
+566                    self.pending.reinsert_pending(id, stream.priority);
+567                }
+568            }
+569
+570            let range = offsets.clone();
+571            let meta = frame::StreamMeta { id, offsets, fin };
+572            builder.write_frame(meta.encoder(encode_length), stats);
+573            stream.pending.get_into(range, builder.buf);
+574        }
+575    }
+576
+577    #[cfg(test)]
+578    fn write_frames_for_test(&mut self, capacity: usize, fair: bool) -> frame::StreamMetaVec {
+579        let buf = &mut Vec::with_capacity(capacity);
+580        let mut tbuf = crate::connection::TransmitBuf::new(buf, std::num::NonZeroUsize::MIN, 1_200);
+581        tbuf.start_new_datagram_with_size(capacity);
+582        let builder = &mut PacketBuilder::simple_data_buf(&mut tbuf);
+583        let stats = &mut FrameStats::default();
+584        self.write_stream_frames(builder, fair, stats);
+585        builder.sent_frames().stream_frames.clone()
+586    }
+587
+588    pub(crate) fn received_ack_of(&mut self, frame: frame::StreamMeta) {
+589        let mut entry = match self.send.entry(frame.id) {
+590            hash_map::Entry::Vacant(_) => return,
+591            hash_map::Entry::Occupied(e) => e,
+592        };
+593
+594        let Some(stream) = entry.get_mut().as_mut() else {
+595            // Because we only call this after sending data on this stream,
+596            // this closure should be unreachable. If we did somehow screw that up,
+597            // then we might hit an underflow below with unpredictable effects down
+598            // the line. Best to short-circuit.
+599            return;
+600        };
+601
+602        if stream.is_reset() {
+603            // We account for outstanding data on reset streams at time of reset
+604            return;
+605        }
+606        let id = frame.id;
+607        self.unacked_data -= frame.offsets.end - frame.offsets.start;
+608        if !stream.ack(frame) {
+609            // The stream is unfinished or may still need retransmits
+610            return;
+611        }
+612
+613        entry.remove_entry();
+614        self.stream_freed(id, StreamHalf::Send);
+615        self.events.push_back(StreamEvent::Finished { id });
+616    }
+617
+618    pub(crate) fn retransmit(&mut self, frame: frame::StreamMeta) {
+619        let Some(stream) = self.send.get_mut(&frame.id).and_then(|s| s.as_mut()) else {
+620            // Loss of data on a closed stream is a noop
+621            return;
+622        };
+623        if !stream.is_pending() {
+624            self.pending.push_pending(frame.id, stream.priority);
+625        }
+626        stream.fin_pending |= frame.fin;
+627        stream.pending.retransmit(frame.offsets);
+628    }
+629
+630    pub(crate) fn retransmit_all_for_0rtt(&mut self) {
+631        for dir in Dir::iter() {
+632            for index in 0..self.next[dir as usize] {
+633                let id = StreamId::new(Side::Client, dir, index);
+634                let Some(stream) = self.send.get_mut(&id).and_then(|s| s.as_mut()) else {
+635                    continue;
+636                };
+637                if stream.pending.is_fully_acked() && !stream.fin_pending {
+638                    // Stream data can't be acked in 0-RTT, so we must not have sent anything on
+639                    // this stream
+640                    continue;
+641                }
+642                if !stream.is_pending() {
+643                    self.pending.push_pending(id, stream.priority);
+644                }
+645                stream.pending.retransmit_all_for_0rtt();
+646            }
+647        }
+648    }
+649
+650    pub(crate) fn received_max_streams(
+651        &mut self,
+652        dir: Dir,
+653        count: u64,
+654    ) -> Result<(), TransportError> {
+655        if count > MAX_STREAM_COUNT {
+656            return Err(TransportError::FRAME_ENCODING_ERROR(
+657                "unrepresentable stream limit",
+658            ));
+659        }
+660
+661        let current = &mut self.max[dir as usize];
+662        if count > *current {
+663            *current = count;
+664            self.streams_blocked[dir as usize] = false;
+665            self.events.push_back(StreamEvent::Available { dir });
+666        }
+667
+668        Ok(())
+669    }
+670
+671    /// Handle increase to connection-level flow control limit
+672    pub(crate) fn received_max_data(&mut self, n: VarInt) {
+673        self.max_data = self.max_data.max(n.into());
+674    }
+675
+676    pub(crate) fn received_max_stream_data(
+677        &mut self,
+678        id: StreamId,
+679        offset: u64,
+680    ) -> Result<(), TransportError> {
+681        if id.initiator() != self.side && id.dir() == Dir::Uni {
+682            debug!("got MAX_STREAM_DATA on recv-only {}", id);
+683            return Err(TransportError::STREAM_STATE_ERROR(
+684                "MAX_STREAM_DATA on recv-only stream",
+685            ));
+686        }
+687
+688        // Create state for this stream if the remote peer created it.
+689        self.ensure_remote(id);
+690
+691        let write_limit = self.write_limit();
+692        let max_send_data = self.max_send_data(id);
+693        if let Some(ss) = self
+694            .send
+695            .get_mut(&id)
+696            .map(get_or_insert_send(max_send_data))
+697        {
+698            if ss.increase_max_data(offset) {
+699                if write_limit > 0 {
+700                    self.events.push_back(StreamEvent::Writable { id });
+701                } else if !ss.connection_blocked {
+702                    // The stream is still blocked on the connection flow control
+703                    // window. In order to get unblocked when the window relaxes
+704                    // it needs to be in the connection blocked list.
+705                    ss.connection_blocked = true;
+706                    self.connection_blocked.push(id);
+707                }
+708            }
+709        } else if id.initiator() == self.side && self.is_local_unopened(id) {
+710            debug!("got MAX_STREAM_DATA on unopened {}", id);
+711            return Err(TransportError::STREAM_STATE_ERROR(
+712                "MAX_STREAM_DATA on unopened stream",
+713            ));
+714        }
+715
+716        Ok(())
+717    }
+718
+719    /// Returns the maximum amount of data this is allowed to be written on the connection
+720    pub(crate) fn write_limit(&self) -> u64 {
+721        (self.max_data - self.data_sent)
+722            // `send_window` can be set after construction to something *less* than `unacked_data`
+723            .min(self.send_window.saturating_sub(self.unacked_data))
+724    }
+725
+726    /// Yield stream events
+727    pub(crate) fn poll(&mut self) -> Option<StreamEvent> {
+728        if let Some(dir) = Dir::iter().find(|&i| mem::replace(&mut self.opened[i as usize], false))
+729        {
+730            return Some(StreamEvent::Opened { dir });
+731        }
+732
+733        if self.write_limit() > 0 {
+734            while let Some(id) = self.connection_blocked.pop() {
+735                let Some(stream) = self.send.get_mut(&id).and_then(|s| s.as_mut()) else {
+736                    continue;
+737                };
+738
+739                debug_assert!(stream.connection_blocked);
+740                stream.connection_blocked = false;
+741
+742                // If it's no longer sensible to write to a stream (even to detect an error) then don't
+743                // report it.
+744                if stream.is_writable() && stream.max_data > stream.offset() {
+745                    return Some(StreamEvent::Writable { id });
+746                }
+747            }
+748        }
+749
+750        self.events.pop_front()
+751    }
+752
+753    /// Queues MAX_STREAM_ID frames in `pending` if needed
+754    ///
+755    /// Returns whether any frames were queued.
+756    pub(crate) fn queue_max_stream_id(&mut self, pending: &mut Retransmits) -> bool {
+757        let mut queued = false;
+758        for dir in Dir::iter() {
+759            let diff = self.max_remote[dir as usize] - self.sent_max_remote[dir as usize];
+760            // To reduce traffic, only announce updates if at least 1/8 of the flow control window
+761            // has been consumed.
+762            if diff > self.max_concurrent_remote_count[dir as usize] / 8 {
+763                pending.max_stream_id[dir as usize] = true;
+764                queued = true;
+765            }
+766        }
+767        queued
+768    }
+769
+770    /// Check for errors entailed by the peer's use of `id` as a send stream
+771    fn validate_receive_id(&mut self, id: StreamId) -> Result<(), TransportError> {
+772        if self.side == id.initiator() {
+773            match id.dir() {
+774                Dir::Uni => {
+775                    return Err(TransportError::STREAM_STATE_ERROR(
+776                        "illegal operation on send-only stream",
+777                    ));
+778                }
+779                Dir::Bi if id.index() >= self.next[Dir::Bi as usize] => {
+780                    return Err(TransportError::STREAM_STATE_ERROR(
+781                        "operation on unopened stream",
+782                    ));
+783                }
+784                Dir::Bi => {}
+785            };
+786        } else {
+787            let limit = self.max_remote[id.dir() as usize];
+788            if id.index() >= limit {
+789                return Err(TransportError::STREAM_LIMIT_ERROR(""));
+790            }
+791        }
+792        Ok(())
+793    }
+794
+795    /// Whether a locally initiated stream has never been open
+796    pub(crate) fn is_local_unopened(&self, id: StreamId) -> bool {
+797        id.index() >= self.next[id.dir() as usize]
+798    }
+799
+800    pub(crate) fn set_max_concurrent(&mut self, dir: Dir, count: VarInt) {
+801        self.flow_control_adjusted = true;
+802        self.max_concurrent_remote_count[dir as usize] = count.into();
+803        self.ensure_remote_streams(dir);
+804    }
+805
+806    pub(crate) fn max_concurrent(&self, dir: Dir) -> u64 {
+807        self.allocated_remote_count[dir as usize]
+808    }
+809
+810    pub(crate) fn set_send_window(&mut self, send_window: u64) {
+811        self.send_window = send_window;
+812    }
+813
+814    /// Set the receive_window and returns whether the receive_window has been
+815    /// expanded or shrunk: true if expanded, false if shrunk.
+816    pub(crate) fn set_receive_window(&mut self, receive_window: VarInt) -> bool {
+817        let receive_window = receive_window.into();
+818        let mut expanded = false;
+819        if receive_window > self.receive_window {
+820            self.local_max_data = self
+821                .local_max_data
+822                .saturating_add(receive_window - self.receive_window);
+823            expanded = true;
+824        } else {
+825            let diff = self.receive_window - receive_window;
+826            self.receive_window_shrink_debt = self.receive_window_shrink_debt.saturating_add(diff);
+827        }
+828        self.receive_window = receive_window;
+829        expanded
+830    }
+831
+832    /// Insert `(id, None)` placeholders for a locally-initiated stream into `send` (and `recv`
+833    /// for bidi). Called from `Streams::open`; the caller guarantees the id is fresh.
+834    pub(super) fn insert_local(&mut self, id: StreamId) {
+835        debug_assert_eq!(id.initiator(), self.side);
+836        assert!(self.send.insert(id, None).is_none());
+837        if id.dir() == Dir::Bi {
+838            let recv = self.free_recv.pop();
+839            assert!(self.recv.insert(id, recv).is_none());
+840        }
+841    }
+842
+843    /// Allocate any new remote streams when a packet arrives for a stream id.
+844    /// Any streams above `next_remote` are considered in the default state, avoiding allocations.
+845    /// Once we receive a packet for a new remote stream, we advance `next_remote` and allocate actual state.
+846    /// If there's a gap, we insert `None` placeholders for the missing streams.
+847    /// Returns `true` if `id` was a newly allocated remote stream.
+848    fn ensure_remote(&mut self, id: StreamId) -> bool {
+849        let dir = id.dir();
+850        let dir_idx = dir as usize;
+851
+852        // If we initiated this stream, nothing to do
+853        if id.initiator() == self.side
+854            // If this stream is larger than the max allowed, return.
+855            // NOTE: STREAM/RESET_STREAM already enforces this, however STOP_SENDING/MAX_STREAM_DATA do not
+856            || id.index() >= self.max_remote[dir_idx]
+857            // If this stream has already been opened, nothing to do
+858            || id.index() < self.next_remote[dir_idx]
+859        {
+860            return false;
+861        }
+862
+863        // Create all of the streams between the largest opened and this stream.
+864        for i in self.next_remote[dir_idx]..=id.index() {
+865            let id = StreamId::new(!self.side, dir, i);
+866            let recv = self.free_recv.pop();
+867            assert!(self.recv.insert(id, recv).is_none());
+868            if dir == Dir::Bi {
+869                assert!(self.send.insert(id, None).is_none());
+870            }
+871        }
+872        self.next_remote[dir_idx] = id.index() + 1;
+873        self.opened[dir_idx] = true;
+874        true
+875    }
+876
+877    /// Adds credits to the connection flow control window
+878    ///
+879    /// Returns whether a `MAX_DATA` frame should be enqueued as soon as possible.
+880    /// This will only be the case if the window update would is significant
+881    /// enough. As soon as a window update with a `MAX_DATA` frame has been
+882    /// queued, the [`Recv::record_sent_max_stream_data`] function should be called to
+883    /// suppress sending further updates until the window increases significantly
+884    /// again.
+885    pub(super) fn add_read_credits(&mut self, credits: u64) -> ShouldTransmit {
+886        if credits > self.receive_window_shrink_debt {
+887            let net_credits = credits - self.receive_window_shrink_debt;
+888            self.local_max_data = self.local_max_data.saturating_add(net_credits);
+889            self.receive_window_shrink_debt = 0;
+890        } else {
+891            self.receive_window_shrink_debt -= credits;
+892        }
+893
+894        if self.local_max_data > VarInt::MAX.into_inner() {
+895            return ShouldTransmit(false);
+896        }
+897
+898        // Only announce a window update if it's significant enough
+899        // to make it worthwhile sending a MAX_DATA frame.
+900        // We use a fraction of the configured connection receive window to make
+901        // the decision, to accommodate for connection using bigger windows requiring
+902        // less updates.
+903        let diff = self.local_max_data - self.sent_max_data.into_inner();
+904        ShouldTransmit(diff >= (self.receive_window / 8))
+905    }
+906
+907    /// Update counters for removal of a stream
+908    pub(super) fn stream_freed(&mut self, id: StreamId, half: StreamHalf) {
+909        if id.initiator() != self.side {
+910            let fully_free = id.dir() == Dir::Uni
+911                || match half {
+912                    StreamHalf::Send => !self.recv.contains_key(&id),
+913                    StreamHalf::Recv => !self.send.contains_key(&id),
+914                };
+915            if fully_free {
+916                self.allocated_remote_count[id.dir() as usize] -= 1;
+917                self.ensure_remote_streams(id.dir());
+918            }
+919        }
+920        if half == StreamHalf::Send {
+921            self.send_streams -= 1;
+922        }
+923    }
+924
+925    pub(super) fn stream_recv_freed(&mut self, id: StreamId, recv: StreamRecv) {
+926        self.free_recv.push(recv.free(self.stream_receive_window));
+927        self.stream_freed(id, StreamHalf::Recv);
+928    }
+929
+930    pub(super) fn max_send_data(&self, id: StreamId) -> VarInt {
+931        let remote = self.side != id.initiator();
+932        match id.dir() {
+933            Dir::Uni => self.initial_max_stream_data_uni,
+934            // Remote/local appear reversed here because the transport parameters are named from
+935            // the perspective of the peer.
+936            Dir::Bi if remote => self.initial_max_stream_data_bidi_local,
+937            Dir::Bi => self.initial_max_stream_data_bidi_remote,
+938        }
+939    }
+940}
+941
+942#[inline]
+943pub(super) fn get_or_insert_send(
+944    max_data: VarInt,
+945) -> impl Fn(&mut Option<Box<Send>>) -> &mut Box<Send> {
+946    move |opt| opt.get_or_insert_with(|| Send::new(max_data))
+947}
+948
+949#[inline]
+950pub(super) fn get_or_insert_recv(
+951    initial_max_data: u64,
+952) -> impl FnMut(&mut Option<StreamRecv>) -> &mut Recv {
+953    move |opt| {
+954        *opt = opt.take().map(|s| match s {
+955            StreamRecv::Free(recv) => StreamRecv::Open(recv),
+956            s => s,
+957        });
+958        opt.get_or_insert_with(|| StreamRecv::Open(Recv::new(initial_max_data)))
+959            .as_open_recv_mut()
+960            .unwrap()
+961    }
+962}
+963
+964#[cfg(test)]
+965mod tests {
+966    use super::*;
+967    use crate::{
+968        ReadableError, RecvStream, SendStream, TransportErrorCode, WriteError,
+969        connection::State as ConnState, connection::Streams,
+970    };
+971    use bytes::Bytes;
+972
+973    fn make(side: Side) -> StreamsState {
+974        StreamsState::new(
+975            side,
+976            128u32.into(),
+977            128u32.into(),
+978            1024 * 1024,
+979            (1024 * 1024u32).into(),
+980            (1024 * 1024u32).into(),
+981        )
+982    }
+983
+984    #[test]
+985    fn trivial_flow_control() {
+986        let mut client = StreamsState::new(
+987            Side::Client,
+988            1u32.into(),
+989            1u32.into(),
+990            1024 * 1024,
+991            (1024 * 1024u32).into(),
+992            (1024 * 1024u32).into(),
+993        );
+994        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+995        let initial_max = client.local_max_data;
+996        const MESSAGE_SIZE: usize = 2048;
+997        assert_eq!(
+998            client
+999                .received(
+1000                    frame::Stream {
+1001                        id,
+1002                        offset: 0,
+1003                        fin: true,
+1004                        data: Bytes::from_static(&[0; MESSAGE_SIZE]),
+1005                    },
+1006                    2048
+1007                )
+1008                .unwrap(),
+1009            ShouldTransmit(false)
+1010        );
+1011        assert_eq!(client.data_recvd, 2048);
+1012        assert_eq!(client.local_max_data - initial_max, 0);
+1013
+1014        let mut pending = Retransmits::default();
+1015        let mut recv = RecvStream {
+1016            id,
+1017            state: &mut client,
+1018            pending: &mut pending,
+1019        };
+1020
+1021        let mut chunks = recv.read(true).unwrap();
+1022        assert_eq!(
+1023            chunks.next(MESSAGE_SIZE).unwrap().unwrap().bytes.len(),
+1024            MESSAGE_SIZE
+1025        );
+1026        assert!(chunks.next(0).unwrap().is_none());
+1027        let should_transmit = chunks.finalize();
+1028        assert!(should_transmit.0);
+1029        assert!(pending.max_stream_id[Dir::Uni as usize]);
+1030        assert_eq!(client.local_max_data - initial_max, MESSAGE_SIZE as u64);
+1031    }
+1032
+1033    #[test]
+1034    fn reset_flow_control() {
+1035        let mut client = make(Side::Client);
+1036        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1037        let initial_max = client.local_max_data;
+1038        assert_eq!(
+1039            client
+1040                .received(
+1041                    frame::Stream {
+1042                        id,
+1043                        offset: 0,
+1044                        fin: false,
+1045                        data: Bytes::from_static(&[0; 2048]),
+1046                    },
+1047                    2048
+1048                )
+1049                .unwrap(),
+1050            ShouldTransmit(false)
+1051        );
+1052        assert_eq!(client.data_recvd, 2048);
+1053        assert_eq!(client.local_max_data - initial_max, 0);
+1054
+1055        let mut pending = Retransmits::default();
+1056        let mut recv = RecvStream {
+1057            id,
+1058            state: &mut client,
+1059            pending: &mut pending,
+1060        };
+1061
+1062        let mut chunks = recv.read(true).unwrap();
+1063        chunks.next(1024).unwrap();
+1064        let _ = chunks.finalize();
+1065        assert_eq!(client.local_max_data - initial_max, 1024);
+1066        assert_eq!(
+1067            client
+1068                .received_reset(frame::ResetStream {
+1069                    id,
+1070                    error_code: 0u32.into(),
+1071                    final_offset: 4096u32.into(),
+1072                })
+1073                .unwrap(),
+1074            ShouldTransmit(false)
+1075        );
+1076
+1077        assert_eq!(client.data_recvd, 4096);
+1078        assert_eq!(client.local_max_data - initial_max, 4096);
+1079
+1080        // Ensure reading after a reset doesn't issue redundant credit
+1081        let mut recv = RecvStream {
+1082            id,
+1083            state: &mut client,
+1084            pending: &mut pending,
+1085        };
+1086        let mut chunks = recv.read(true).unwrap();
+1087        assert_eq!(
+1088            chunks.next(1024).unwrap_err(),
+1089            crate::ReadError::Reset(0u32.into())
+1090        );
+1091        let _ = chunks.finalize();
+1092        assert_eq!(client.data_recvd, 4096);
+1093        assert_eq!(client.local_max_data - initial_max, 4096);
+1094    }
+1095
+1096    #[test]
+1097    fn reset_after_empty_frame_flow_control() {
+1098        let mut client = make(Side::Client);
+1099        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1100        let initial_max = client.local_max_data;
+1101        assert_eq!(
+1102            client
+1103                .received(
+1104                    frame::Stream {
+1105                        id,
+1106                        offset: 4096,
+1107                        fin: false,
+1108                        data: Bytes::from_static(&[0; 0]),
+1109                    },
+1110                    0
+1111                )
+1112                .unwrap(),
+1113            ShouldTransmit(false)
+1114        );
+1115        assert_eq!(client.data_recvd, 4096);
+1116        assert_eq!(client.local_max_data - initial_max, 0);
+1117        assert_eq!(
+1118            client
+1119                .received_reset(frame::ResetStream {
+1120                    id,
+1121                    error_code: 0u32.into(),
+1122                    final_offset: 4096u32.into(),
+1123                })
+1124                .unwrap(),
+1125            ShouldTransmit(false)
+1126        );
+1127        assert_eq!(client.data_recvd, 4096);
+1128        assert_eq!(client.local_max_data - initial_max, 4096);
+1129    }
+1130
+1131    #[test]
+1132    fn duplicate_reset_flow_control() {
+1133        let mut client = make(Side::Client);
+1134        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1135        assert_eq!(
+1136            client
+1137                .received_reset(frame::ResetStream {
+1138                    id,
+1139                    error_code: 0u32.into(),
+1140                    final_offset: 4096u32.into(),
+1141                })
+1142                .unwrap(),
+1143            ShouldTransmit(false)
+1144        );
+1145        assert_eq!(client.data_recvd, 4096);
+1146        assert_eq!(
+1147            client
+1148                .received_reset(frame::ResetStream {
+1149                    id,
+1150                    error_code: 0u32.into(),
+1151                    final_offset: 4096u32.into(),
+1152                })
+1153                .unwrap(),
+1154            ShouldTransmit(false)
+1155        );
+1156        assert_eq!(client.data_recvd, 4096);
+1157    }
+1158
+1159    #[test]
+1160    fn recv_stopped() {
+1161        let mut client = make(Side::Client);
+1162        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1163        let initial_max = client.local_max_data;
+1164        assert_eq!(
+1165            client
+1166                .received(
+1167                    frame::Stream {
+1168                        id,
+1169                        offset: 0,
+1170                        fin: false,
+1171                        data: Bytes::from_static(&[0; 32]),
+1172                    },
+1173                    32
+1174                )
+1175                .unwrap(),
+1176            ShouldTransmit(false)
+1177        );
+1178        assert_eq!(client.local_max_data, initial_max);
+1179
+1180        let mut pending = Retransmits::default();
+1181        let mut recv = RecvStream {
+1182            id,
+1183            state: &mut client,
+1184            pending: &mut pending,
+1185        };
+1186
+1187        recv.stop(0u32.into()).unwrap();
+1188        assert_eq!(recv.pending.stop_sending.len(), 1);
+1189        assert!(!recv.pending.max_data);
+1190
+1191        assert!(recv.stop(0u32.into()).is_err());
+1192        assert_eq!(recv.read(true).err(), Some(ReadableError::ClosedStream));
+1193        assert_eq!(recv.read(false).err(), Some(ReadableError::ClosedStream));
+1194
+1195        assert_eq!(client.local_max_data - initial_max, 32);
+1196        assert_eq!(
+1197            client
+1198                .received(
+1199                    frame::Stream {
+1200                        id,
+1201                        offset: 32,
+1202                        fin: true,
+1203                        data: Bytes::from_static(&[0; 16]),
+1204                    },
+1205                    16
+1206                )
+1207                .unwrap(),
+1208            ShouldTransmit(false)
+1209        );
+1210        assert_eq!(client.local_max_data - initial_max, 48);
+1211        assert!(!client.recv.contains_key(&id));
+1212    }
+1213
+1214    #[test]
+1215    fn stopped_reset() {
+1216        let mut client = make(Side::Client);
+1217        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1218        // Server opens stream
+1219        assert_eq!(
+1220            client
+1221                .received(
+1222                    frame::Stream {
+1223                        id,
+1224                        offset: 0,
+1225                        fin: false,
+1226                        data: Bytes::from_static(&[0; 32])
+1227                    },
+1228                    32
+1229                )
+1230                .unwrap(),
+1231            ShouldTransmit(false)
+1232        );
+1233
+1234        let mut pending = Retransmits::default();
+1235        let mut recv = RecvStream {
+1236            id,
+1237            state: &mut client,
+1238            pending: &mut pending,
+1239        };
+1240
+1241        recv.stop(0u32.into()).unwrap();
+1242        assert_eq!(pending.stop_sending.len(), 1);
+1243        assert!(!pending.max_data);
+1244
+1245        // Server complies
+1246        let prev_max = client.max_remote[Dir::Uni as usize];
+1247        assert_eq!(
+1248            client
+1249                .received_reset(frame::ResetStream {
+1250                    id,
+1251                    error_code: 0u32.into(),
+1252                    final_offset: 32u32.into(),
+1253                })
+1254                .unwrap(),
+1255            ShouldTransmit(false)
+1256        );
+1257        assert!(!client.recv.contains_key(&id), "stream state is freed");
+1258        assert_eq!(client.max_remote[Dir::Uni as usize], prev_max + 1);
+1259    }
+1260
+1261    #[test]
+1262    fn send_stopped() {
+1263        let mut server = make(Side::Server);
+1264        server.set_params(&TransportParameters {
+1265            initial_max_streams_uni: 1u32.into(),
+1266            initial_max_data: 42u32.into(),
+1267            initial_max_stream_data_uni: 42u32.into(),
+1268            ..TransportParameters::default()
+1269        });
+1270
+1271        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1272        let id = Streams {
+1273            state: &mut server,
+1274            conn_state: &state,
+1275        }
+1276        .open(Dir::Uni)
+1277        .unwrap();
+1278
+1279        let mut stream = SendStream {
+1280            id,
+1281            state: &mut server,
+1282            pending: &mut pending,
+1283            conn_state: &state,
+1284        };
+1285
+1286        let error_code = 0u32.into();
+1287        stream.state.received_stop_sending(id, error_code);
+1288        assert!(
+1289            stream
+1290                .state
+1291                .events
+1292                .contains(&StreamEvent::Stopped { id, error_code })
+1293        );
+1294        stream.state.events.clear();
+1295
+1296        assert_eq!(stream.write(&[]), Err(WriteError::Stopped(error_code)));
+1297
+1298        stream.reset(0u32.into()).unwrap();
+1299        assert_eq!(stream.write(&[]), Err(WriteError::ClosedStream));
+1300
+1301        // A duplicate frame is a no-op
+1302        stream.state.received_stop_sending(id, error_code);
+1303        assert!(stream.state.events.is_empty());
+1304    }
+1305
+1306    #[test]
+1307    fn final_offset_flow_control() {
+1308        let mut client = make(Side::Client);
+1309        assert_eq!(
+1310            client
+1311                .received_reset(frame::ResetStream {
+1312                    id: StreamId::new(Side::Server, Dir::Uni, 0),
+1313                    error_code: 0u32.into(),
+1314                    final_offset: VarInt::MAX,
+1315                })
+1316                .unwrap_err()
+1317                .code,
+1318            TransportErrorCode::FLOW_CONTROL_ERROR
+1319        );
+1320    }
+1321
+1322    #[test]
+1323    fn stream_priority() {
+1324        let mut server = make(Side::Server);
+1325        server.set_params(&TransportParameters {
+1326            initial_max_streams_bidi: 3u32.into(),
+1327            initial_max_data: 10u32.into(),
+1328            initial_max_stream_data_bidi_remote: 10u32.into(),
+1329            ..TransportParameters::default()
+1330        });
+1331
+1332        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1333        let mut streams = Streams {
+1334            state: &mut server,
+1335            conn_state: &state,
+1336        };
+1337
+1338        let id_high = streams.open(Dir::Bi).unwrap();
+1339        let id_mid = streams.open(Dir::Bi).unwrap();
+1340        let id_low = streams.open(Dir::Bi).unwrap();
+1341
+1342        let mut mid = SendStream {
+1343            id: id_mid,
+1344            state: &mut server,
+1345            pending: &mut pending,
+1346            conn_state: &state,
+1347        };
+1348        mid.write(b"mid").unwrap();
+1349
+1350        let mut low = SendStream {
+1351            id: id_low,
+1352            state: &mut server,
+1353            pending: &mut pending,
+1354            conn_state: &state,
+1355        };
+1356        low.set_priority(-1).unwrap();
+1357        low.write(b"low").unwrap();
+1358
+1359        let mut high = SendStream {
+1360            id: id_high,
+1361            state: &mut server,
+1362            pending: &mut pending,
+1363            conn_state: &state,
+1364        };
+1365        high.set_priority(1).unwrap();
+1366        high.write(b"high").unwrap();
+1367
+1368        let meta = server.write_frames_for_test(40, true);
+1369        assert_eq!(meta[0].id, id_high);
+1370        assert_eq!(meta[1].id, id_mid);
+1371        assert_eq!(meta[2].id, id_low);
+1372
+1373        assert!(!server.can_send_stream_data());
+1374        assert_eq!(server.pending.len(), 0);
+1375    }
+1376
+1377    #[test]
+1378    fn requeue_stream_priority() {
+1379        let mut server = make(Side::Server);
+1380        server.set_params(&TransportParameters {
+1381            initial_max_streams_bidi: 3u32.into(),
+1382            initial_max_data: 1000u32.into(),
+1383            initial_max_stream_data_bidi_remote: 1000u32.into(),
+1384            ..TransportParameters::default()
+1385        });
+1386
+1387        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1388        let mut streams = Streams {
+1389            state: &mut server,
+1390            conn_state: &state,
+1391        };
+1392
+1393        let id_high = streams.open(Dir::Bi).unwrap();
+1394        let id_mid = streams.open(Dir::Bi).unwrap();
+1395
+1396        let mut mid = SendStream {
+1397            id: id_mid,
+1398            state: &mut server,
+1399            pending: &mut pending,
+1400            conn_state: &state,
+1401        };
+1402        assert_eq!(mid.write(b"mid").unwrap(), 3);
+1403        assert_eq!(server.pending.len(), 1);
+1404
+1405        let mut high = SendStream {
+1406            id: id_high,
+1407            state: &mut server,
+1408            pending: &mut pending,
+1409            conn_state: &state,
+1410        };
+1411        high.set_priority(1).unwrap();
+1412        assert_eq!(high.write(&[0; 200]).unwrap(), 200);
+1413        assert_eq!(server.pending.len(), 2);
+1414
+1415        // Requeue the high priority stream to lowest priority. The initial send
+1416        // still uses high priority since it's queued that way. After that it will
+1417        // switch to low priority
+1418        let mut high = SendStream {
+1419            id: id_high,
+1420            state: &mut server,
+1421            pending: &mut pending,
+1422            conn_state: &state,
+1423        };
+1424        high.set_priority(-1).unwrap();
+1425
+1426        let meta = server.write_frames_for_test(40, true);
+1427        assert_eq!(meta.len(), 1);
+1428        assert_eq!(meta[0].id, id_high);
+1429
+1430        // After requeuing we should end up with 2 priorities - not 3
+1431        assert_eq!(server.pending.len(), 2);
+1432
+1433        // Send the remaining data. The initial mid priority one should go first now
+1434        let meta = server.write_frames_for_test(1000 - 40, true);
+1435        assert_eq!(meta.len(), 2);
+1436        assert_eq!(meta[0].id, id_mid);
+1437        assert_eq!(meta[1].id, id_high);
+1438
+1439        assert!(!server.can_send_stream_data());
+1440        assert_eq!(server.pending.len(), 0);
+1441    }
+1442
+1443    #[test]
+1444    fn same_stream_priority() {
+1445        for fair in [true, false] {
+1446            let mut server = make(Side::Server);
+1447            server.set_params(&TransportParameters {
+1448                initial_max_streams_bidi: 3u32.into(),
+1449                initial_max_data: 300u32.into(),
+1450                initial_max_stream_data_bidi_remote: 300u32.into(),
+1451                ..TransportParameters::default()
+1452            });
+1453
+1454            let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1455            let mut streams = Streams {
+1456                state: &mut server,
+1457                conn_state: &state,
+1458            };
+1459
+1460            // a, b and c all have the same priority
+1461            let id_a = streams.open(Dir::Bi).unwrap();
+1462            let id_b = streams.open(Dir::Bi).unwrap();
+1463            let id_c = streams.open(Dir::Bi).unwrap();
+1464
+1465            let mut stream_a = SendStream {
+1466                id: id_a,
+1467                state: &mut server,
+1468                pending: &mut pending,
+1469                conn_state: &state,
+1470            };
+1471            stream_a.write(&[b'a'; 100]).unwrap();
+1472
+1473            let mut stream_b = SendStream {
+1474                id: id_b,
+1475                state: &mut server,
+1476                pending: &mut pending,
+1477                conn_state: &state,
+1478            };
+1479            stream_b.write(&[b'b'; 100]).unwrap();
+1480
+1481            let mut stream_c = SendStream {
+1482                id: id_c,
+1483                state: &mut server,
+1484                pending: &mut pending,
+1485                conn_state: &state,
+1486            };
+1487            stream_c.write(&[b'c'; 100]).unwrap();
+1488
+1489            let mut metas = vec![];
+1490
+1491            // loop until all the streams are written
+1492            loop {
+1493                let meta = server.write_frames_for_test(40, fair);
+1494                if meta.is_empty() {
+1495                    break;
+1496                }
+1497                metas.extend(meta);
+1498            }
+1499
+1500            assert!(!server.can_send_stream_data());
+1501            assert_eq!(server.pending.len(), 0);
+1502
+1503            let stream_ids = metas.iter().map(|m| m.id).collect::<Vec<_>>();
+1504            if fair {
+1505                // When fairness is enabled, if we run out of buffer space to write out a stream,
+1506                // the stream is re-queued after all the streams with the same priority.
+1507                assert_eq!(
+1508                    stream_ids,
+1509                    vec![id_a, id_b, id_c, id_a, id_b, id_c, id_a, id_b, id_c]
+1510                );
+1511            } else {
+1512                // When fairness is disabled the stream is re-queued before all the other streams
+1513                // with the same priority.
+1514                assert_eq!(
+1515                    stream_ids,
+1516                    vec![id_a, id_a, id_a, id_b, id_b, id_b, id_c, id_c, id_c]
+1517                );
+1518            }
+1519        }
+1520    }
+1521
+1522    #[test]
+1523    fn unfair_priority_bump() {
+1524        let mut server = make(Side::Server);
+1525        server.set_params(&TransportParameters {
+1526            initial_max_streams_bidi: 3u32.into(),
+1527            initial_max_data: 300u32.into(),
+1528            initial_max_stream_data_bidi_remote: 300u32.into(),
+1529            ..TransportParameters::default()
+1530        });
+1531
+1532        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1533        let mut streams = Streams {
+1534            state: &mut server,
+1535            conn_state: &state,
+1536        };
+1537
+1538        // a, and b have the same priority, c has higher priority
+1539        let id_a = streams.open(Dir::Bi).unwrap();
+1540        let id_b = streams.open(Dir::Bi).unwrap();
+1541        let id_c = streams.open(Dir::Bi).unwrap();
+1542
+1543        let mut stream_a = SendStream {
+1544            id: id_a,
+1545            state: &mut server,
+1546            pending: &mut pending,
+1547            conn_state: &state,
+1548        };
+1549        stream_a.write(&[b'a'; 100]).unwrap();
+1550
+1551        let mut stream_b = SendStream {
+1552            id: id_b,
+1553            state: &mut server,
+1554            pending: &mut pending,
+1555            conn_state: &state,
+1556        };
+1557        stream_b.write(&[b'b'; 100]).unwrap();
+1558
+1559        let mut metas = vec![];
+1560
+1561        // Write the first chunk of stream_a
+1562        let meta = server.write_frames_for_test(40, false);
+1563        assert!(!meta.is_empty());
+1564        metas.extend(meta);
+1565
+1566        // Queue stream_c which has higher priority
+1567        let mut stream_c = SendStream {
+1568            id: id_c,
+1569            state: &mut server,
+1570            pending: &mut pending,
+1571            conn_state: &state,
+1572        };
+1573        stream_c.set_priority(1).unwrap();
+1574        stream_c.write(&[b'b'; 100]).unwrap();
+1575
+1576        // loop until all the streams are written
+1577        loop {
+1578            let meta = server.write_frames_for_test(40, false);
+1579            if meta.is_empty() {
+1580                break;
+1581            }
+1582            metas.extend(meta);
+1583        }
+1584
+1585        assert!(!server.can_send_stream_data());
+1586        assert_eq!(server.pending.len(), 0);
+1587
+1588        let stream_ids = metas.iter().map(|m| m.id).collect::<Vec<_>>();
+1589        assert_eq!(
+1590            stream_ids,
+1591            // stream_c bumps stream_b but doesn't bump stream_a which had already been partly
+1592            // written out
+1593            vec![id_a, id_a, id_a, id_c, id_c, id_c, id_b, id_b, id_b]
+1594        );
+1595    }
+1596
+1597    #[test]
+1598    fn stop_finished() {
+1599        let mut client = make(Side::Client);
+1600        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1601        // Server finishes stream
+1602        let _ = client
+1603            .received(
+1604                frame::Stream {
+1605                    id,
+1606                    offset: 0,
+1607                    fin: true,
+1608                    data: Bytes::from_static(&[0; 32]),
+1609                },
+1610                32,
+1611            )
+1612            .unwrap();
+1613        let mut pending = Retransmits::default();
+1614        let mut stream = RecvStream {
+1615            id,
+1616            state: &mut client,
+1617            pending: &mut pending,
+1618        };
+1619        stream.stop(0u32.into()).unwrap();
+1620        assert!(client.recv.get_mut(&id).is_none(), "stream is freed");
+1621    }
+1622
+1623    // Verify that a stream that's been reset doesn't cause the appearance of pending data
+1624    #[test]
+1625    fn reset_stream_cannot_send() {
+1626        let mut server = make(Side::Server);
+1627        server.set_params(&TransportParameters {
+1628            initial_max_streams_uni: 1u32.into(),
+1629            initial_max_data: 42u32.into(),
+1630            initial_max_stream_data_uni: 42u32.into(),
+1631            ..TransportParameters::default()
+1632        });
+1633        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1634        let mut streams = Streams {
+1635            state: &mut server,
+1636            conn_state: &state,
+1637        };
+1638
+1639        let id = streams.open(Dir::Uni).unwrap();
+1640        let mut stream = SendStream {
+1641            id,
+1642            state: &mut server,
+1643            pending: &mut pending,
+1644            conn_state: &state,
+1645        };
+1646        stream.write(b"hello").unwrap();
+1647        stream.reset(0u32.into()).unwrap();
+1648
+1649        assert_eq!(pending.reset_stream, &[(id, 0u32.into())]);
+1650        assert!(!server.can_send_stream_data());
+1651    }
+1652
+1653    #[test]
+1654    fn stream_limit_fixed() {
+1655        let mut client = make(Side::Client);
+1656        // Open streams 0-127
+1657        assert_eq!(
+1658            client.received(
+1659                frame::Stream {
+1660                    id: StreamId::new(Side::Server, Dir::Uni, 127),
+1661                    offset: 0,
+1662                    fin: true,
+1663                    data: Bytes::from_static(&[]),
+1664                },
+1665                0
+1666            ),
+1667            Ok(ShouldTransmit(false))
+1668        );
+1669        // Try to open stream 128, exceeding limit
+1670        assert_eq!(
+1671            client
+1672                .received(
+1673                    frame::Stream {
+1674                        id: StreamId::new(Side::Server, Dir::Uni, 128),
+1675                        offset: 0,
+1676                        fin: true,
+1677                        data: Bytes::from_static(&[]),
+1678                    },
+1679                    0
+1680                )
+1681                .unwrap_err()
+1682                .code,
+1683            TransportErrorCode::STREAM_LIMIT_ERROR
+1684        );
+1685
+1686        // Free stream 127
+1687        let mut pending = Retransmits::default();
+1688        let mut stream = RecvStream {
+1689            id: StreamId::new(Side::Server, Dir::Uni, 127),
+1690            state: &mut client,
+1691            pending: &mut pending,
+1692        };
+1693        stream.stop(0u32.into()).unwrap();
+1694
+1695        // Open stream 128
+1696        assert_eq!(
+1697            client.received(
+1698                frame::Stream {
+1699                    id: StreamId::new(Side::Server, Dir::Uni, 128),
+1700                    offset: 0,
+1701                    fin: true,
+1702                    data: Bytes::from_static(&[]),
+1703                },
+1704                0
+1705            ),
+1706            Ok(ShouldTransmit(false))
+1707        );
+1708    }
+1709
+1710    #[test]
+1711    fn stream_limit_grows() {
+1712        let mut client = make(Side::Client);
+1713        // Open streams 0-127
+1714        assert_eq!(
+1715            client.received(
+1716                frame::Stream {
+1717                    id: StreamId::new(Side::Server, Dir::Uni, 127),
+1718                    offset: 0,
+1719                    fin: true,
+1720                    data: Bytes::from_static(&[]),
+1721                },
+1722                0
+1723            ),
+1724            Ok(ShouldTransmit(false))
+1725        );
+1726        // Try to open stream 128, exceeding limit
+1727        assert_eq!(
+1728            client
+1729                .received(
+1730                    frame::Stream {
+1731                        id: StreamId::new(Side::Server, Dir::Uni, 128),
+1732                        offset: 0,
+1733                        fin: true,
+1734                        data: Bytes::from_static(&[]),
+1735                    },
+1736                    0
+1737                )
+1738                .unwrap_err()
+1739                .code,
+1740            TransportErrorCode::STREAM_LIMIT_ERROR
+1741        );
+1742
+1743        // Relax limit by one
+1744        client.set_max_concurrent(Dir::Uni, 129u32.into());
+1745
+1746        // Open stream 128
+1747        assert_eq!(
+1748            client.received(
+1749                frame::Stream {
+1750                    id: StreamId::new(Side::Server, Dir::Uni, 128),
+1751                    offset: 0,
+1752                    fin: true,
+1753                    data: Bytes::from_static(&[]),
+1754                },
+1755                0
+1756            ),
+1757            Ok(ShouldTransmit(false))
+1758        );
+1759    }
+1760
+1761    #[test]
+1762    fn stream_limit_shrinks() {
+1763        let mut client = make(Side::Client);
+1764        // Open streams 0-127
+1765        assert_eq!(
+1766            client.received(
+1767                frame::Stream {
+1768                    id: StreamId::new(Side::Server, Dir::Uni, 127),
+1769                    offset: 0,
+1770                    fin: true,
+1771                    data: Bytes::from_static(&[]),
+1772                },
+1773                0
+1774            ),
+1775            Ok(ShouldTransmit(false))
+1776        );
+1777
+1778        // Tighten limit by one
+1779        client.set_max_concurrent(Dir::Uni, 127u32.into());
+1780
+1781        // Free stream 127
+1782        let mut pending = Retransmits::default();
+1783        let mut stream = RecvStream {
+1784            id: StreamId::new(Side::Server, Dir::Uni, 127),
+1785            state: &mut client,
+1786            pending: &mut pending,
+1787        };
+1788        stream.stop(0u32.into()).unwrap();
+1789
+1790        // Try to open stream 128, still exceeding limit
+1791        assert_eq!(
+1792            client
+1793                .received(
+1794                    frame::Stream {
+1795                        id: StreamId::new(Side::Server, Dir::Uni, 128),
+1796                        offset: 0,
+1797                        fin: true,
+1798                        data: Bytes::from_static(&[]),
+1799                    },
+1800                    0
+1801                )
+1802                .unwrap_err()
+1803                .code,
+1804            TransportErrorCode::STREAM_LIMIT_ERROR
+1805        );
+1806
+1807        // Free stream 126
+1808        assert_eq!(
+1809            client.received_reset(frame::ResetStream {
+1810                id: StreamId::new(Side::Server, Dir::Uni, 126),
+1811                error_code: 0u32.into(),
+1812                final_offset: 0u32.into(),
+1813            }),
+1814            Ok(ShouldTransmit(false))
+1815        );
+1816        let mut pending = Retransmits::default();
+1817        let mut stream = RecvStream {
+1818            id: StreamId::new(Side::Server, Dir::Uni, 126),
+1819            state: &mut client,
+1820            pending: &mut pending,
+1821        };
+1822        stream.stop(0u32.into()).unwrap();
+1823
+1824        // Open stream 128
+1825        assert_eq!(
+1826            client.received(
+1827                frame::Stream {
+1828                    id: StreamId::new(Side::Server, Dir::Uni, 128),
+1829                    offset: 0,
+1830                    fin: true,
+1831                    data: Bytes::from_static(&[]),
+1832                },
+1833                0
+1834            ),
+1835            Ok(ShouldTransmit(false))
+1836        );
+1837    }
+1838
+1839    #[test]
+1840    fn remote_stream_capacity() {
+1841        let mut client = make(Side::Client);
+1842        for _ in 0..2 {
+1843            client.set_max_concurrent(Dir::Uni, 200u32.into());
+1844            client.set_max_concurrent(Dir::Bi, 201u32.into());
+1845            assert_eq!(client.max_remote[Dir::Uni as usize], 200);
+1846            assert_eq!(client.max_remote[Dir::Bi as usize], 201);
+1847            assert_eq!(client.allocated_remote_count[Dir::Uni as usize], 200);
+1848            assert_eq!(client.allocated_remote_count[Dir::Bi as usize], 201);
+1849            // Slots are materialized lazily: no remote stream has been touched yet.
+1850            assert!(client.recv.is_empty());
+1851            assert!(client.send.is_empty());
+1852        }
+1853    }
+1854
+1855    #[test]
+1856    fn lazy_remote_allocation_starts_empty() {
+1857        // `StreamsState::new` must not pre-populate `send`/`recv` with placeholder slots.
+1858        let client = StreamsState::new(
+1859            Side::Client,
+1860            10_000u32.into(),
+1861            10_000u32.into(),
+1862            1024 * 1024,
+1863            (1024 * 1024u32).into(),
+1864            (1024 * 1024u32).into(),
+1865        );
+1866        // No slots allocated until a stream is actually received.
+1867        assert!(client.recv.is_empty());
+1868        assert!(client.send.is_empty());
+1869        assert_eq!(client.recv.capacity(), 0);
+1870        assert_eq!(client.send.capacity(), 0);
+1871    }
+1872
+1873    #[test]
+1874    fn out_of_order_implicit_open() {
+1875        // Receiving idx=5 implicitly opens idx 0..=4. A later frame for idx=3 must be
+1876        // processed normally, not dropped as "closed".
+1877        const STREAM_5_PAYLOAD: &[u8] = &[0xAA; 8];
+1878        const STREAM_3_PAYLOAD: &[u8] = &[0xBB; 4];
+1879
+1880        let mut client = make(Side::Client);
+1881        assert_eq!(
+1882            client.received(
+1883                frame::Stream {
+1884                    id: StreamId::new(Side::Server, Dir::Uni, 5),
+1885                    offset: 0,
+1886                    fin: true,
+1887                    data: Bytes::from_static(STREAM_5_PAYLOAD),
+1888                },
+1889                STREAM_5_PAYLOAD.len(),
+1890            ),
+1891            Ok(ShouldTransmit(false))
+1892        );
+1893        assert_eq!(client.next_remote[Dir::Uni as usize], 6);
+1894        assert_eq!(
+1895            client.received(
+1896                frame::Stream {
+1897                    id: StreamId::new(Side::Server, Dir::Uni, 3),
+1898                    offset: 0,
+1899                    fin: true,
+1900                    data: Bytes::from_static(STREAM_3_PAYLOAD),
+1901                },
+1902                STREAM_3_PAYLOAD.len(),
+1903            ),
+1904            Ok(ShouldTransmit(false))
+1905        );
+1906
+1907        let id = StreamId::new(Side::Server, Dir::Uni, 3);
+1908        let mut pending = Retransmits::default();
+1909        let mut recv = RecvStream {
+1910            id,
+1911            state: &mut client,
+1912            pending: &mut pending,
+1913        };
+1914        let mut chunks = recv.read(true).unwrap();
+1915        assert_eq!(
+1916            chunks.next(STREAM_3_PAYLOAD.len()).unwrap().unwrap().bytes,
+1917            STREAM_3_PAYLOAD
+1918        );
+1919        let _ = chunks.finalize();
+1920    }
+1921
+1922    #[test]
+1923    fn frame_for_closed_stream_is_dropped() {
+1924        // After a remote stream is fully freed, a subsequent frame for the same id must be
+1925        // dropped — absence from the map unambiguously means "closed" for ids below the
+1926        // frontier.
+1927        const PAYLOAD: &[u8] = &[0; 4];
+1928
+1929        let mut client = make(Side::Client);
+1930        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1931        assert_eq!(
+1932            client.received(
+1933                frame::Stream {
+1934                    id,
+1935                    offset: 0,
+1936                    fin: true,
+1937                    data: Bytes::from_static(PAYLOAD),
+1938                },
+1939                PAYLOAD.len(),
+1940            ),
+1941            Ok(ShouldTransmit(false))
+1942        );
+1943        // Stop the stream so it's fully freed.
+1944        let mut pending = Retransmits::default();
+1945        RecvStream {
+1946            id,
+1947            state: &mut client,
+1948            pending: &mut pending,
+1949        }
+1950        .stop(0u32.into())
+1951        .unwrap();
+1952        assert!(!client.recv.contains_key(&id));
+1953
+1954        // A stray retransmit for the freed stream must be dropped without resurrecting state.
+1955        assert_eq!(
+1956            client.received(
+1957                frame::Stream {
+1958                    id,
+1959                    offset: 0,
+1960                    fin: true,
+1961                    data: Bytes::from_static(PAYLOAD),
+1962                },
+1963                PAYLOAD.len(),
+1964            ),
+1965            Ok(ShouldTransmit(false))
+1966        );
+1967        assert!(!client.recv.contains_key(&id));
+1968    }
+1969
+1970    #[test]
+1971    fn churn_keeps_maps_bounded() {
+1972        // Rapidly open + fully close a long sequence of remote streams. The maps must stay
+1973        // bounded (only active streams are materialized) even though thousands of ids have
+1974        // been used over the connection's lifetime.
+1975        const N: u64 = 5_000;
+1976
+1977        let mut client = make(Side::Client);
+1978        for i in 0..N {
+1979            let id = StreamId::new(Side::Server, Dir::Uni, i);
+1980            assert_eq!(
+1981                client.received(
+1982                    frame::Stream {
+1983                        id,
+1984                        offset: 0,
+1985                        fin: true,
+1986                        data: Bytes::from_static(&[0; 1]),
+1987                    },
+1988                    1,
+1989                ),
+1990                Ok(ShouldTransmit(false))
+1991            );
+1992            let mut pending = Retransmits::default();
+1993            let mut recv = RecvStream {
+1994                id,
+1995                state: &mut client,
+1996                pending: &mut pending,
+1997            };
+1998            let mut chunks = recv.read(true).unwrap();
+1999            let _ = chunks.next(1).unwrap();
+2000            assert!(chunks.next(1).unwrap().is_none());
+2001            let _ = chunks.finalize();
+2002        }
+2003        // Every stream was fully drained; the map must be empty.
+2004        assert_eq!(client.recv.len(), 0);
+2005        assert_eq!(client.send.len(), 0);
+2006    }
+2007
+2008    #[test]
+2009    fn expand_receive_window() {
+2010        let mut server = make(Side::Server);
+2011        let new_receive_window = 2 * server.receive_window as u32;
+2012        let expanded = server.set_receive_window(new_receive_window.into());
+2013        assert!(expanded);
+2014        assert_eq!(server.receive_window, new_receive_window as u64);
+2015        assert_eq!(server.local_max_data, new_receive_window as u64);
+2016        assert_eq!(server.receive_window_shrink_debt, 0);
+2017        let prev_local_max_data = server.local_max_data;
+2018
+2019        // credit, expecting all of them added to local_max_data
+2020        let credits = 1024u64;
+2021        let should_transmit = server.add_read_credits(credits);
+2022        assert_eq!(server.receive_window_shrink_debt, 0);
+2023        assert_eq!(server.local_max_data, prev_local_max_data + credits);
+2024        assert!(should_transmit.should_transmit());
+2025    }
+2026
+2027    #[test]
+2028    fn shrink_receive_window() {
+2029        let mut server = make(Side::Server);
+2030        let new_receive_window = server.receive_window as u32 / 2;
+2031        let prev_local_max_data = server.local_max_data;
+2032
+2033        // shrink the receive_winbow, local_max_data is not expected to be changed
+2034        let shrink_diff = server.receive_window - new_receive_window as u64;
+2035        let expanded = server.set_receive_window(new_receive_window.into());
+2036        assert!(!expanded);
+2037        assert_eq!(server.receive_window, new_receive_window as u64);
+2038        assert_eq!(server.local_max_data, prev_local_max_data);
+2039        assert_eq!(server.receive_window_shrink_debt, shrink_diff);
+2040        let prev_local_max_data = server.local_max_data;
+2041
+2042        // credit twice, local_max_data does not change as it is absorbed by receive_window_shrink_debt
+2043        let credits = 1024u64;
+2044        for _ in 0..2 {
+2045            let expected_receive_window_shrink_debt = server.receive_window_shrink_debt - credits;
+2046            let should_transmit = server.add_read_credits(credits);
+2047            assert_eq!(
+2048                server.receive_window_shrink_debt,
+2049                expected_receive_window_shrink_debt
+2050            );
+2051            assert_eq!(server.local_max_data, prev_local_max_data);
+2052            assert!(!should_transmit.should_transmit());
+2053        }
+2054
+2055        // credit again which exceeds all remaining expected_receive_window_shrink_debt
+2056        let credits = 1024 * 512;
+2057        let prev_local_max_data = server.local_max_data;
+2058        let expected_local_max_data =
+2059            server.local_max_data + (credits - server.receive_window_shrink_debt);
+2060        let _should_transmit = server.add_read_credits(credits);
+2061        assert_eq!(server.receive_window_shrink_debt, 0);
+2062        assert_eq!(server.local_max_data, expected_local_max_data);
+2063        assert!(server.local_max_data > prev_local_max_data);
+2064
+2065        // credit again, all should be added to local_max_data
+2066        let credits = 1024 * 512;
+2067        let expected_local_max_data = server.local_max_data + credits;
+2068        let should_transmit = server.add_read_credits(credits);
+2069        assert_eq!(server.receive_window_shrink_debt, 0);
+2070        assert_eq!(server.local_max_data, expected_local_max_data);
+2071        assert!(should_transmit.should_transmit());
+2072    }
+2073
+2074    #[test]
+2075    fn expand_send_window() {
+2076        let mut server = make(Side::Server);
+2077
+2078        let initial_send_window = server.send_window;
+2079        let larger_send_window = initial_send_window * 2;
+2080
+2081        // Set `initial_max_data` larger than `send_window` so we're limited by local flow control
+2082        server.set_params(&TransportParameters {
+2083            initial_max_data: VarInt::MAX,
+2084            initial_max_stream_data_uni: VarInt::MAX,
+2085            initial_max_streams_uni: VarInt::from_u32(100),
+2086            ..TransportParameters::default()
+2087        });
+2088
+2089        assert_eq!(server.write_limit(), initial_send_window);
+2090        assert_eq!(server.poll(), None);
+2091
+2092        let mut retransmits = Retransmits::default();
+2093        let conn_state = ConnState::established();
+2094
+2095        let stream_id = Streams {
+2096            state: &mut server,
+2097            conn_state: &conn_state,
+2098        }
+2099        .open(Dir::Uni)
+2100        .expect("should be able to open a stream");
+2101
+2102        let mut stream = SendStream {
+2103            id: stream_id,
+2104            state: &mut server,
+2105            pending: &mut retransmits,
+2106            conn_state: &conn_state,
+2107        };
+2108
+2109        // Check that the stream accepts `initial_send_window` bytes
+2110        let initial_send_len = initial_send_window as usize;
+2111        let data = vec![0xFFu8; initial_send_len];
+2112
+2113        assert_eq!(stream.write(&data), Ok(initial_send_len));
+2114
+2115        // Try to write the same data again, observe that it's blocked
+2116        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2117
+2118        // Check that we get a `Writable` event after increasing the send window
+2119        stream.state.set_send_window(larger_send_window);
+2120        assert_eq!(
+2121            stream.state.poll(),
+2122            Some(StreamEvent::Writable { id: stream_id })
+2123        );
+2124
+2125        // Check that the stream accepts the exact same amount of data again
+2126        assert_eq!(stream.write(&data), Ok(initial_send_len));
+2127        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2128
+2129        assert_eq!(stream.state.poll(), None);
+2130
+2131        // Ack the data
+2132        stream.state.received_ack_of(frame::StreamMeta {
+2133            id: stream_id,
+2134            offsets: 0..larger_send_window,
+2135            fin: false,
+2136        });
+2137
+2138        assert_eq!(
+2139            stream.state.poll(),
+2140            Some(StreamEvent::Writable { id: stream_id })
+2141        );
+2142
+2143        // Check that our full send window is available again
+2144        assert_eq!(stream.write(&data), Ok(initial_send_len));
+2145        assert_eq!(stream.write(&data), Ok(initial_send_len));
+2146        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2147    }
+2148
+2149    #[test]
+2150    fn shrink_send_window() {
+2151        let mut server = make(Side::Server);
+2152
+2153        let initial_send_window = server.send_window;
+2154        let smaller_send_window = server.send_window / 2;
+2155
+2156        // Set `initial_max_data` larger than `send_window` so we're limited by local flow control
+2157        server.set_params(&TransportParameters {
+2158            initial_max_data: VarInt::MAX,
+2159            initial_max_stream_data_uni: VarInt::MAX,
+2160            initial_max_streams_uni: VarInt::from_u32(100),
+2161            ..TransportParameters::default()
+2162        });
+2163
+2164        assert_eq!(server.write_limit(), initial_send_window);
+2165        assert_eq!(server.poll(), None);
+2166
+2167        let mut retransmits = Retransmits::default();
+2168        let conn_state = ConnState::established();
+2169
+2170        let stream_id = Streams {
+2171            state: &mut server,
+2172            conn_state: &conn_state,
+2173        }
+2174        .open(Dir::Uni)
+2175        .expect("should be able to open a stream");
+2176
+2177        let mut stream = SendStream {
+2178            id: stream_id,
+2179            state: &mut server,
+2180            pending: &mut retransmits,
+2181            conn_state: &conn_state,
+2182        };
+2183
+2184        let initial_send_len = initial_send_window as usize;
+2185
+2186        let data = vec![0xFFu8; initial_send_len];
+2187
+2188        // Assert that the full send window is accepted
+2189        assert_eq!(stream.write(&data), Ok(initial_send_len));
+2190        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2191
+2192        assert_eq!(stream.state.write_limit(), 0);
+2193        assert_eq!(stream.state.poll(), None);
+2194
+2195        // Shrink our send window, assert that it's still not writable
+2196        stream.state.set_send_window(smaller_send_window);
+2197        assert_eq!(stream.state.write_limit(), 0);
+2198        assert_eq!(stream.state.poll(), None);
+2199
+2200        // Assert that data is still not accepted
+2201        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2202
+2203        // Ack some data, assert that writes are still not accepted due to outstanding sends
+2204        stream.state.received_ack_of(frame::StreamMeta {
+2205            id: stream_id,
+2206            offsets: 0..smaller_send_window,
+2207            fin: false,
+2208        });
+2209
+2210        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2211
+2212        // Ack the rest of the data
+2213        stream.state.received_ack_of(frame::StreamMeta {
+2214            id: stream_id,
+2215            offsets: smaller_send_window..initial_send_window,
+2216            fin: false,
+2217        });
+2218
+2219        // This should generate a `Writable` event
+2220        assert_eq!(
+2221            stream.state.poll(),
+2222            Some(StreamEvent::Writable { id: stream_id })
+2223        );
+2224        assert_eq!(stream.state.write_limit(), smaller_send_window);
+2225
+2226        // Assert that only `smaller_send_window` bytes are accepted
+2227        assert_eq!(stream.write(&data), Ok(smaller_send_window as usize));
+2228        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2229    }
+2230}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/timer.rs.html b/pr/672/docs/src/noq_proto/connection/timer.rs.html new file mode 100644 index 000000000..b7ac52e75 --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/timer.rs.html @@ -0,0 +1,538 @@ +timer.rs - source

noq_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    /// Grace period after the remote abandoned the last path.
+31    ///
+32    /// If no new path is opened before this fires, close the connection.
+33    /// See <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-21.html#section-3.4-8>
+34    NoAvailablePath = 5,
+35    /// When to retry NAT traversal probes.
+36    ///
+37    /// Fires at initial PTO intervals to retransmit probes that got no PATH_RESPONSE.
+38    NatTraversalProbeRetry = 6,
+39}
+40
+41impl ConnTimer {
+42    const VALUES: [Self; 7] = [
+43        Self::Idle,
+44        Self::Close,
+45        Self::KeyDiscard,
+46        Self::KeepAlive,
+47        Self::PushNewCid,
+48        Self::NoAvailablePath,
+49        Self::NatTraversalProbeRetry,
+50    ];
+51}
+52
+53#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
+54pub(crate) enum PathTimer {
+55    /// When to send an ack-eliciting probe packet or declare unacked packets lost
+56    LossDetection = 0,
+57    /// When to abandon a path after no activity
+58    PathIdle = 1,
+59    /// When to give up on validating a new path from RFC9000 migration
+60    PathValidationFailed = 2,
+61    /// When to resend an on-path path challenge deemed lost
+62    PathChallengeLost = 3,
+63    /// When to abandon a path due to failed validation.
+64    ///
+65    /// There are two situations in which we give up a path from validation.
+66    /// 1. When opening a path we validate it according to RFC9000 §8.2 as required by the
+67    ///    multipath spec. This timer is armed to time-bound that validation.
+68    /// 2. When validating an already opened multipath path for various reasons in which we
+69    ///    expect the path to either work or not work and want to respond correspondingly in
+70    ///    a timely manner.
+71    AbandonFromValidation = 4,
+72    /// When to send a `PING` frame to keep the path alive
+73    PathKeepAlive = 5,
+74    /// When pacing will allow us to send a packet
+75    Pacing = 6,
+76    /// When to send an immediate ACK if there are unacked ack-eliciting packets of the peer
+77    MaxAckDelay = 7,
+78    /// When to clean up state for an abandoned path
+79    PathDrained = 8,
+80}
+81
+82impl PathTimer {
+83    pub(super) const VALUES: [Self; 9] = [
+84        Self::LossDetection,
+85        Self::PathIdle,
+86        Self::PathValidationFailed,
+87        Self::PathChallengeLost,
+88        Self::AbandonFromValidation,
+89        Self::PathKeepAlive,
+90        Self::Pacing,
+91        Self::MaxAckDelay,
+92        Self::PathDrained,
+93    ];
+94}
+95
+96/// Newtype around [`PathId`] that implements [`IdentityHashable`].
+97#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
+98struct PathIdKey(PathId);
+99
+100impl IdentityHashable for PathIdKey {}
+101
+102impl std::hash::Hash for PathIdKey {
+103    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+104        state.write_u32(self.0.0);
+105    }
+106}
+107
+108/// Keeps track of the nearest timeout for each `Timer`
+109///
+110/// The [`TimerTable`] is advanced with [`TimerTable::expire_before`].
+111#[derive(Debug, Clone, Default)]
+112pub(crate) struct TimerTable {
+113    generic: [Option<Instant>; ConnTimer::VALUES.len()],
+114    path_timers: SmallMap<PathIdKey, PathTimerTable, STACK_TIMERS>,
+115}
+116
+117/// For how many paths we keep the timers on the stack, before spilling onto the heap.
+118const STACK_TIMERS: usize = 4;
+119
+120/// Works like a `HashMap` but stores up to `SIZE` items on the stack.
+121#[derive(Debug, Clone)]
+122struct SmallMap<K, V, const SIZE: usize> {
+123    stack: [Option<(K, V)>; SIZE],
+124    heap: Option<IntMap<K, V>>,
+125}
+126
+127impl<K, V, const SIZE: usize> Default for SmallMap<K, V, SIZE> {
+128    fn default() -> Self {
+129        Self {
+130            stack: [const { None }; SIZE],
+131            heap: None,
+132        }
+133    }
+134}
+135
+136impl<K, V, const SIZE: usize> SmallMap<K, V, SIZE>
+137where
+138    K: std::cmp::Eq + std::hash::Hash + IdentityHashable,
+139{
+140    fn insert(&mut self, key: K, value: V) -> Option<V> {
+141        // check stack for space
+142        for el in self.stack.iter_mut() {
+143            match el {
+144                Some((k, v)) => {
+145                    if *k == key {
+146                        let old_value = std::mem::replace(v, value);
+147                        return Some(old_value);
+148                    }
+149                }
+150                None => {
+151                    // make sure to remove a potentially old value from the heap
+152                    let old_heap = self.heap.as_mut().and_then(|h| h.remove(&key));
+153                    *el = Some((key, value));
+154
+155                    return old_heap;
+156                }
+157            }
+158        }
+159
+160        // No space on the stack, use the heap
+161        let heap = self.heap.get_or_insert_default();
+162        heap.insert(key, value)
+163    }
+164
+165    #[cfg(test)]
+166    fn remove(&mut self, key: &K) -> Option<V> {
+167        for el in self.stack.iter_mut() {
+168            if let Some((k, _)) = el
+169                && key == k
+170            {
+171                return el.take().map(|(_, v)| v);
+172            }
+173        }
+174
+175        self.heap.as_mut().and_then(|h| h.remove(key))
+176    }
+177
+178    fn get(&self, key: &K) -> Option<&V> {
+179        for (k, v) in self.stack.iter().filter_map(|v| v.as_ref()) {
+180            if k == key {
+181                return Some(v);
+182            }
+183        }
+184
+185        self.heap.as_ref().and_then(|h| h.get(key))
+186    }
+187
+188    fn get_mut(&mut self, key: &K) -> Option<&mut V> {
+189        for (k, v) in self.stack.iter_mut().filter_map(|v| v.as_mut()) {
+190            if k == key {
+191                return Some(v);
+192            }
+193        }
+194
+195        self.heap.as_mut().and_then(|h| h.get_mut(key))
+196    }
+197
+198    #[cfg(test)]
+199    fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
+200        let a = self
+201            .stack
+202            .iter()
+203            .filter_map(|v| v.as_ref().map(|(k, v)| (k, v)));
+204        let b = self.heap.iter().flat_map(|h| h.iter());
+205        a.chain(b)
+206    }
+207
+208    fn values(&self) -> impl Iterator<Item = &V> {
+209        let a = self.stack.iter().filter_map(|v| v.as_ref().map(|(_, v)| v));
+210        let b = self.heap.iter().flat_map(|h| h.values());
+211        a.chain(b)
+212    }
+213
+214    fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
+215        let a = self
+216            .stack
+217            .iter_mut()
+218            .filter_map(|v| v.as_mut().map(|(k, v)| (&*k, v)));
+219        let b = self.heap.iter_mut().flat_map(|h| h.iter_mut());
+220        a.chain(b)
+221    }
+222
+223    fn retain<F>(&mut self, mut f: F)
+224    where
+225        F: FnMut(&K, &mut V) -> bool,
+226    {
+227        let mut to_remove = [false; SIZE];
+228        for (i, el) in self.stack.iter_mut().enumerate() {
+229            if let Some((key, value)) = el {
+230                to_remove[i] = !f(key, value);
+231            }
+232        }
+233        for (i, to_remove) in to_remove.into_iter().enumerate() {
+234            if to_remove {
+235                self.stack[i] = None;
+236            }
+237        }
+238
+239        if let Some(ref mut heap) = self.heap {
+240            heap.retain(f);
+241        }
+242    }
+243
+244    fn clear(&mut self) {
+245        for el in self.stack.iter_mut() {
+246            *el = None;
+247        }
+248        self.heap = None;
+249    }
+250}
+251
+252#[derive(Debug, Clone, Copy, Default)]
+253struct PathTimerTable {
+254    timers: [Option<Instant>; PathTimer::VALUES.len()],
+255}
+256
+257impl PathTimerTable {
+258    fn set(&mut self, timer: PathTimer, time: Instant) {
+259        self.timers[timer as usize] = Some(time);
+260    }
+261
+262    fn get(&self, timer: PathTimer) -> Option<Instant> {
+263        self.timers[timer as usize]
+264    }
+265
+266    fn stop(&mut self, timer: PathTimer) {
+267        self.timers[timer as usize] = None;
+268    }
+269
+270    /// Remove the next timer up until `now`, including it
+271    fn expire_before(&mut self, now: Instant) -> Option<(PathTimer, Instant)> {
+272        for timer in PathTimer::VALUES {
+273            if self.timers[timer as usize].is_some()
+274                && self.timers[timer as usize].expect("checked") <= now
+275            {
+276                return self.timers[timer as usize].take().map(|time| (timer, time));
+277            }
+278        }
+279
+280        None
+281    }
+282}
+283
+284impl TimerTable {
+285    /// Sets the timer unconditionally.
+286    ///
+287    /// If the timer is already set, this will change the timer's value.
+288    pub(super) fn set(&mut self, timer: Timer, time: Instant, qlog: QlogSinkWithTime<'_>) {
+289        match timer {
+290            Timer::Conn(timer) => {
+291                self.generic[timer as usize] = Some(time);
+292            }
+293            Timer::PerPath(path_id, timer) => match self.path_timers.get_mut(&PathIdKey(path_id)) {
+294                None => {
+295                    let mut table = PathTimerTable::default();
+296                    table.set(timer, time);
+297                    self.path_timers.insert(PathIdKey(path_id), table);
+298                }
+299                Some(table) => {
+300                    table.set(timer, time);
+301                }
+302            },
+303        }
+304        qlog.emit_timer_set(timer, time);
+305    }
+306
+307    pub(super) fn get(&self, timer: Timer) -> Option<Instant> {
+308        match timer {
+309            Timer::Conn(timer) => self.generic[timer as usize],
+310            Timer::PerPath(path_id, timer) => self.path_timers.get(&PathIdKey(path_id))?.get(timer),
+311        }
+312    }
+313
+314    pub(super) fn set_or_stop(
+315        &mut self,
+316        timer: Timer,
+317        time: Option<Instant>,
+318        qlog: QlogSinkWithTime<'_>,
+319    ) {
+320        match time {
+321            Some(time) => self.set(timer, time, qlog),
+322            None => self.stop(timer, qlog),
+323        }
+324    }
+325
+326    pub(super) fn stop(&mut self, timer: Timer, qlog: QlogSinkWithTime<'_>) {
+327        match timer {
+328            Timer::Conn(timer) => {
+329                self.generic[timer as usize] = None;
+330            }
+331            Timer::PerPath(path_id, timer) => {
+332                if let Some(e) = self.path_timers.get_mut(&PathIdKey(path_id)) {
+333                    e.stop(timer);
+334                }
+335            }
+336        }
+337        qlog.emit_timer_stop(timer);
+338    }
+339
+340    /// Stops all per-path timers
+341    pub(super) fn stop_per_path(&mut self, path_id: PathId, qlog: QlogSinkWithTime<'_>) {
+342        for timer in PathTimer::VALUES {
+343            if let Some(e) = self.path_timers.get_mut(&PathIdKey(path_id)) {
+344                e.stop(timer);
+345                qlog.emit_timer_stop(Timer::PerPath(path_id, timer));
+346            }
+347        }
+348    }
+349
+350    /// Get the next queued timeout
+351    pub(super) fn peek(&mut self) -> Option<Instant> {
+352        // TODO: this is currently linear in the number of paths
+353
+354        let min_generic = self.generic.iter().filter_map(|&x| x).min();
+355        let min_path = self
+356            .path_timers
+357            .values()
+358            .flat_map(|p| p.timers.iter().filter_map(|&x| x))
+359            .min();
+360
+361        match (min_generic, min_path) {
+362            (None, None) => None,
+363            (Some(val), None) => Some(val),
+364            (Some(a), Some(b)) => Some(a.min(b)),
+365            (None, Some(val)) => Some(val),
+366        }
+367    }
+368
+369    /// Remove the next timer up until `now`, including it
+370    pub(super) fn expire_before(
+371        &mut self,
+372        now: Instant,
+373        qlog: &QlogSink,
+374    ) -> Option<(Timer, Instant)> {
+375        let (timer, instant) = self.expire_before_inner(now)?;
+376        qlog.with_time(now).emit_timer_expire(timer);
+377        Some((timer, instant))
+378    }
+379
+380    fn expire_before_inner(&mut self, now: Instant) -> Option<(Timer, Instant)> {
+381        // TODO: this is currently linear in the number of paths
+382
+383        for timer in ConnTimer::VALUES {
+384            if self.generic[timer as usize].is_some()
+385                && self.generic[timer as usize].expect("checked") <= now
+386            {
+387                return self.generic[timer as usize]
+388                    .take()
+389                    .map(|time| (Timer::Conn(timer), time));
+390            }
+391        }
+392
+393        let mut res = None;
+394        for (PathIdKey(path_id), timers) in self.path_timers.iter_mut() {
+395            if let Some((timer, time)) = timers.expire_before(now) {
+396                res = Some((Timer::PerPath(*path_id, timer), time));
+397                break;
+398            }
+399        }
+400
+401        // clear out old timers
+402        self.path_timers
+403            .retain(|_path_id, timers| timers.timers.iter().any(|t| t.is_some()));
+404        res
+405    }
+406
+407    pub(super) fn reset(&mut self) {
+408        for timer in ConnTimer::VALUES {
+409            self.generic[timer as usize] = None;
+410        }
+411        self.path_timers.clear();
+412    }
+413
+414    #[cfg(test)]
+415    pub(super) fn values(&self) -> Vec<(Timer, Instant)> {
+416        let mut values = Vec::new();
+417
+418        for timer in ConnTimer::VALUES {
+419            if let Some(time) = self.generic[timer as usize] {
+420                values.push((Timer::Conn(timer), time));
+421            }
+422        }
+423
+424        for timer in PathTimer::VALUES {
+425            for (PathIdKey(path_id), timers) in self.path_timers.iter() {
+426                if let Some(time) = timers.timers[timer as usize] {
+427                    values.push((Timer::PerPath(*path_id, timer), time));
+428                }
+429            }
+430        }
+431
+432        values
+433    }
+434}
+435
+436#[cfg(test)]
+437mod tests {
+438    use std::time::Duration;
+439
+440    use crate::connection::qlog::QlogSink;
+441
+442    use super::*;
+443
+444    #[test]
+445    fn timer_table() {
+446        let mut timers = TimerTable::default();
+447        let sec = Duration::from_secs(1);
+448        let now = Instant::now() + Duration::from_secs(10);
+449        timers.set(
+450            Timer::Conn(ConnTimer::Idle),
+451            now - 3 * sec,
+452            QlogSink::default().with_time(now),
+453        );
+454        timers.set(
+455            Timer::Conn(ConnTimer::Close),
+456            now - 2 * sec,
+457            QlogSink::default().with_time(now),
+458        );
+459
+460        assert_eq!(timers.peek(), Some(now - 3 * sec));
+461        assert_eq!(
+462            timers.expire_before(now, &QlogSink::default()),
+463            Some((Timer::Conn(ConnTimer::Idle), now - 3 * sec))
+464        );
+465        assert_eq!(
+466            timers.expire_before(now, &QlogSink::default()),
+467            Some((Timer::Conn(ConnTimer::Close), now - 2 * sec))
+468        );
+469        assert_eq!(timers.expire_before(now, &QlogSink::default()), None);
+470    }
+471
+472    #[test]
+473    fn test_small_map() {
+474        let mut map = SmallMap::<usize, usize, 2>::default();
+475
+476        // inserts only on the stack
+477        assert_eq!(map.insert(1, 1), None);
+478        assert!(map.heap.is_none());
+479        assert_eq!(map.insert(2, 2), None);
+480        assert!(map.heap.is_none());
+481
+482        // replace on the stack
+483        assert_eq!(map.insert(1, 2), Some(1));
+484
+485        assert_eq!(map.remove(&1), Some(2));
+486        assert_eq!(map.insert(3, 3), None);
+487        assert!(map.heap.is_none());
+488
+489        // spill
+490        assert_eq!(map.insert(4, 4), None);
+491        assert!(map.heap.is_some());
+492
+493        assert_eq!(
+494            map.iter()
+495                .map(|(&a, &b)| (a, b))
+496                .collect::<Vec<(usize, usize)>>(),
+497            vec![(3, 3), (2, 2), (4, 4)]
+498        );
+499        assert_eq!(
+500            map.iter()
+501                .map(|(a, b)| (*a, *b))
+502                .collect::<Vec<(usize, usize)>>(),
+503            map.iter_mut()
+504                .map(|(a, b)| (*a, *b))
+505                .collect::<Vec<(usize, usize)>>(),
+506        );
+507
+508        assert_eq!(map.heap.as_ref().unwrap().len(), 1);
+509
+510        for i in 0..10 {
+511            map.insert(10 + i, 10 + i);
+512        }
+513        assert_eq!(map.heap.as_ref().unwrap().len(), 11);
+514        map.retain(|k, _v| *k < 10);
+515
+516        assert_eq!(map.heap.as_ref().unwrap().len(), 1);
+517
+518        assert_eq!(
+519            map.iter()
+520                .map(|(&a, &b)| (a, b))
+521                .collect::<Vec<(usize, usize)>>(),
+522            vec![(3, 3), (2, 2), (4, 4)]
+523        );
+524
+525        assert_eq!(
+526            map.iter()
+527                .map(|(a, b)| (*a, *b))
+528                .collect::<Vec<(usize, usize)>>(),
+529            map.iter_mut()
+530                .map(|(a, b)| (*a, *b))
+531                .collect::<Vec<(usize, usize)>>(),
+532        );
+533
+534        map.clear();
+535        assert_eq!(map.iter().collect::<Vec<_>>(), Vec::new());
+536        assert!(map.heap.is_none());
+537    }
+538}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/connection/transmit_buf.rs.html b/pr/672/docs/src/noq_proto/connection/transmit_buf.rs.html new file mode 100644 index 000000000..2f08d5b01 --- /dev/null +++ b/pr/672/docs/src/noq_proto/connection/transmit_buf.rs.html @@ -0,0 +1,236 @@ +transmit_buf.rs - source

noq_proto/connection/
transmit_buf.rs

1use std::num::NonZeroUsize;
+2
+3use bytes::BufMut;
+4use tracing::trace;
+5
+6use crate::packet::BufLen;
+7
+8/// The buffer in which to write datagrams for [`Connection::poll_transmit`]
+9///
+10/// The `poll_transmit` function writes zero or more datagrams to a buffer. Multiple
+11/// datagrams are possible in case GSO (Generic Segmentation Offload) is supported.
+12///
+13/// This buffer tracks datagrams being written to it. There is always a "current" datagram,
+14/// which is started by calling [`TransmitBuf::start_new_datagram`]. Writing to the buffer
+15/// is done through the [`BufMut`] interface.
+16///
+17/// Usually a datagram contains one QUIC packet, though QUIC-TRANSPORT 12.2 Coalescing
+18/// Packets allows for placing multiple packets into a single datagram provided all but the
+19/// last packet uses long headers. This is normally used during connection setup where often
+20/// the initial, handshake and sometimes even a 1-RTT packet can be coalesced into a single
+21/// datagram.
+22///
+23/// Inside a single packet multiple QUIC frames are written.
+24///
+25/// The buffer managed here is passed straight to the OS' `sendmsg` call (or variant) once
+26/// `poll_transmit` returns.  So needs to contain the datagrams as they are sent on the
+27/// wire.
+28///
+29/// [`Connection::poll_transmit`]: super::Connection::poll_transmit
+30#[derive(Debug)]
+31pub(super) struct TransmitBuf<'a> {
+32    /// The buffer itself, packets are written to this buffer
+33    buf: &'a mut Vec<u8>,
+34    /// Offset into the buffer at which the current datagram starts
+35    ///
+36    /// Note that when coalescing packets this might be before the start of the current
+37    /// packet.
+38    datagram_start: usize,
+39    /// The maximum offset allowed to be used for the current datagram in the buffer
+40    ///
+41    /// The first and last datagram in a batch are allowed to be smaller then the maximum
+42    /// size. All datagrams in between need to be exactly this size.
+43    buf_capacity: usize,
+44    /// The maximum number of datagrams allowed to write into [`TransmitBuf::buf`]
+45    max_datagrams: NonZeroUsize,
+46    /// The number of datagrams already (partially) written into the buffer
+47    ///
+48    /// Incremented by a call to [`TransmitBuf::start_new_datagram`].
+49    pub(super) num_datagrams: usize,
+50    /// The segment size of this GSO batch
+51    ///
+52    /// The segment size is the size of each datagram in the GSO batch, only the last
+53    /// datagram in the batch may be smaller.
+54    ///
+55    /// For the first datagram this is set to the maximum size a datagram is allowed to be:
+56    /// the current path MTU. After the first datagram is finished this is reduced to the
+57    /// size of the first datagram and can no longer change.
+58    segment_size: usize,
+59}
+60
+61impl<'a> TransmitBuf<'a> {
+62    pub(super) fn new(buf: &'a mut Vec<u8>, max_datagrams: NonZeroUsize, mtu: usize) -> Self {
+63        buf.clear();
+64        Self {
+65            buf,
+66            datagram_start: 0,
+67            buf_capacity: 0,
+68            max_datagrams,
+69            num_datagrams: 0,
+70            segment_size: mtu,
+71        }
+72    }
+73
+74    /// Starts a datagram with a custom datagram size
+75    ///
+76    /// This is a specialized version of [`TransmitBuf::start_new_datagram`] which sets the
+77    /// datagram size. Useful for e.g. PATH_CHALLENGE, tail-loss probes or MTU probes.
+78    ///
+79    /// After the first datagram you can never increase the segment size. If you decrease
+80    /// the size of a datagram in a batch, it must be the last datagram of the batch.
+81    pub(super) fn start_new_datagram_with_size(&mut self, datagram_size: usize) {
+82        // Only reserve space for this datagram, usually it is the last one in the batch.
+83        let max_capacity_hint = datagram_size;
+84        self.new_datagram_inner(datagram_size, max_capacity_hint)
+85    }
+86
+87    /// Starts a new datagram in the transmit buffer
+88    ///
+89    /// If this starts the second datagram the segment size will be set to the size of the
+90    /// first datagram.
+91    ///
+92    /// If the underlying buffer does not have enough capacity yet this will allocate enough
+93    /// capacity for all the datagrams allowed in a single batch. Use
+94    /// [`TransmitBuf::start_new_datagram_with_size`] if you know you will need less.
+95    pub(super) fn start_new_datagram(&mut self) {
+96        // We reserve the maximum space for sending `max_datagrams` upfront to avoid any
+97        // reallocations if more datagrams have to be appended later on.  Benchmarks have
+98        // shown a 5-10% throughput improvement compared to continuously resizing the
+99        // datagram buffer. While this will lead to over-allocation for small transmits
+100        // (e.g. purely containing ACKs), modern memory allocators (e.g. mimalloc and
+101        // jemalloc) will pool certain allocation sizes and therefore this is still rather
+102        // efficient.
+103        let max_capacity_hint = self.max_datagrams.get() * self.segment_size;
+104        self.new_datagram_inner(self.segment_size, max_capacity_hint)
+105    }
+106
+107    fn new_datagram_inner(&mut self, datagram_size: usize, max_capacity_hint: usize) {
+108        debug_assert!(self.num_datagrams < self.max_datagrams.into());
+109        if self.num_datagrams == 1 {
+110            // Set the segment size to the size of the first datagram.
+111            self.segment_size = self.buf.len();
+112        }
+113        if self.num_datagrams >= 1 {
+114            debug_assert!(datagram_size <= self.segment_size);
+115            if datagram_size < self.segment_size {
+116                // If this is a GSO batch and this datagram is smaller than the segment
+117                // size, this must be the last datagram in the batch.
+118                self.max_datagrams = NonZeroUsize::MIN.saturating_add(self.num_datagrams);
+119            }
+120        }
+121        self.datagram_start = self.buf.len();
+122        debug_assert_eq!(
+123            self.datagram_start % self.segment_size,
+124            0,
+125            "datagrams in a GSO batch must be aligned to the segment size"
+126        );
+127        self.buf_capacity = self.datagram_start + datagram_size;
+128        if self.buf_capacity > self.buf.capacity() {
+129            self.buf
+130                .reserve_exact(max_capacity_hint.saturating_sub(self.buf.capacity()));
+131        }
+132        self.num_datagrams += 1;
+133    }
+134
+135    /// Clips the segment size to the current size
+136    ///
+137    /// Only valid for the first datagram, when the datagram might be smaller than the
+138    /// segment size. Needed before estimating the available space in the next datagram
+139    /// based on [`TransmitBuf::segment_size`].
+140    ///
+141    /// Use [`TransmitBuf::start_new_datagram_with_size`] if you need to reduce the size of
+142    /// the last datagram in a batch.
+143    pub(super) fn clip_segment_size(&mut self) {
+144        debug_assert_eq!(self.num_datagrams, 1);
+145        if self.buf.len() < self.segment_size {
+146            trace!(
+147                segment_size = self.buf.len(),
+148                prev_segment_size = self.segment_size,
+149                "clipped datagram size"
+150            );
+151        }
+152        self.segment_size = self.buf.len();
+153        self.buf_capacity = self.buf.len();
+154    }
+155
+156    /// Returns the GSO segment size
+157    ///
+158    /// This is also the maximum size datagrams are allowed to be. The first and last
+159    /// datagram in a batch are allowed to be smaller however. After the first datagram the
+160    /// segment size is clipped to the size of the first datagram.
+161    ///
+162    /// If the last datagram was created using [`TransmitBuf::start_new_datagram_with_size`]
+163    /// the the segment size will be greater than the current datagram is allowed to be.
+164    /// Thus [`TransmitBuf::datagram_remaining_mut`] should be used if you need to know the
+165    /// amount of data that can be written into the datagram.
+166    pub(super) fn segment_size(&self) -> usize {
+167        self.segment_size
+168    }
+169
+170    /// Returns the number of datagrams written into the buffer
+171    ///
+172    /// The last datagram is not necessarily finished yet.
+173    pub(super) fn num_datagrams(&self) -> usize {
+174        self.num_datagrams
+175    }
+176
+177    /// Returns the maximum number of datagrams allowed to be written into the buffer
+178    pub(super) fn max_datagrams(&self) -> NonZeroUsize {
+179        self.max_datagrams
+180    }
+181
+182    /// Returns the start offset of the current datagram in the buffer
+183    ///
+184    /// In other words, this offset contains the first byte of the current datagram.
+185    pub(super) fn datagram_start_offset(&self) -> usize {
+186        self.datagram_start
+187    }
+188
+189    /// Returns the maximum offset in the buffer allowed for the current datagram
+190    ///
+191    /// The first and last datagram in a batch are allowed to be smaller then the maximum
+192    /// size. All datagrams in between need to be exactly this size.
+193    pub(super) fn datagram_max_offset(&self) -> usize {
+194        self.buf_capacity
+195    }
+196
+197    /// Returns the number of bytes that may still be written into this datagram
+198    pub(super) fn datagram_remaining_mut(&self) -> usize {
+199        self.buf_capacity.saturating_sub(self.buf.len())
+200    }
+201
+202    /// Returns `true` if the buffer did not have anything written into it
+203    pub(super) fn is_empty(&self) -> bool {
+204        self.len() == 0
+205    }
+206
+207    /// The number of bytes written into the buffer so far
+208    pub(super) fn len(&self) -> usize {
+209        self.buf.len()
+210    }
+211
+212    /// Returns the already written bytes in the buffer
+213    pub(super) fn as_mut_slice(&mut self) -> &mut [u8] {
+214        self.buf.as_mut_slice()
+215    }
+216}
+217
+218unsafe impl BufMut for TransmitBuf<'_> {
+219    fn remaining_mut(&self) -> usize {
+220        self.buf.remaining_mut()
+221    }
+222
+223    unsafe fn advance_mut(&mut self, cnt: usize) {
+224        unsafe { self.buf.advance_mut(cnt) };
+225    }
+226
+227    fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice {
+228        self.buf.chunk_mut()
+229    }
+230}
+231
+232impl BufLen for TransmitBuf<'_> {
+233    fn len(&self) -> usize {
+234        self.len()
+235    }
+236}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/constant_time.rs.html b/pr/672/docs/src/noq_proto/constant_time.rs.html new file mode 100644 index 000000000..e31abcc37 --- /dev/null +++ b/pr/672/docs/src/noq_proto/constant_time.rs.html @@ -0,0 +1,22 @@ +constant_time.rs - source

noq_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/672/docs/src/noq_proto/crypto.rs.html b/pr/672/docs/src/noq_proto/crypto.rs.html new file mode 100644 index 000000000..1ca777d86 --- /dev/null +++ b/pr/672/docs/src/noq_proto/crypto.rs.html @@ -0,0 +1,228 @@ +crypto.rs - source

noq_proto/
crypto.rs

1//! Traits and implementations for the QUIC cryptography protocol
+2//!
+3//! The protocol logic in noq 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};
+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 or aws-lc-rs.
+21///
+22/// Prefers ring, if both are enabled.
+23#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
+24pub(crate) mod ring_like;
+25/// TLS interface based on rustls
+26#[cfg(feature = "rustls")]
+27pub mod rustls;
+28
+29/// A cryptographic session (commonly TLS)
+30pub trait Session: Send + Sync + 'static {
+31    /// Create the initial set of keys given the client's initial destination ConnectionId
+32    fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys;
+33
+34    /// Get data negotiated during the handshake, if available
+35    ///
+36    /// Returns `None` until the connection emits `HandshakeDataReady`.
+37    fn handshake_data(&self) -> Option<Box<dyn Any>>;
+38
+39    /// Get the peer's identity, if available
+40    fn peer_identity(&self) -> Option<Box<dyn Any>>;
+41
+42    /// Get the 0-RTT keys if available (clients only)
+43    ///
+44    /// On the client side, this method can be used to see if 0-RTT key material is available
+45    /// to start sending data before the protocol handshake has completed.
+46    ///
+47    /// Returns `None` if the key material is not available. This might happen if you have
+48    /// not connected to this server before.
+49    fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>;
+50
+51    /// If the 0-RTT-encrypted data has been accepted by the peer
+52    fn early_data_accepted(&self) -> Option<bool>;
+53
+54    /// Returns `true` until the connection is fully established.
+55    fn is_handshaking(&self) -> bool;
+56
+57    /// Read bytes of handshake data
+58    ///
+59    /// This should be called with the contents of `CRYPTO` frames. If it returns `Ok`, the
+60    /// caller should call `write_handshake()` to check if the crypto protocol has anything
+61    /// to send to the peer. This method will only return `true` the first time that
+62    /// handshake data is available. Future calls will always return false.
+63    ///
+64    /// On success, returns `true` iff `self.handshake_data()` has been populated.
+65    fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError>;
+66
+67    /// The peer's QUIC transport parameters
+68    ///
+69    /// These are only available after the first flight from the peer has been received.
+70    fn transport_parameters(&self) -> Result<Option<TransportParameters>, TransportError>;
+71
+72    /// Writes handshake bytes into the given buffer and optionally returns the negotiated keys
+73    ///
+74    /// When the handshake proceeds to the next phase, this method will return a new set of
+75    /// keys to encrypt data with.
+76    fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>;
+77
+78    /// Compute keys for the next key update
+79    fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>;
+80
+81    /// Verify the integrity of a retry packet
+82    ///
+83    /// See the specification: <https://www.rfc-editor.org/rfc/rfc9001#name-retry-packet-integrity>
+84    fn is_valid_retry(&self, orig_dst_cid: ConnectionId, header: &[u8], payload: &[u8]) -> bool;
+85
+86    /// Fill `output` with `output.len()` bytes of keying material derived
+87    /// from the [Session]'s secrets, using `label` and `context` for domain
+88    /// separation.
+89    ///
+90    /// This function will fail, returning [ExportKeyingMaterialError],
+91    /// if the requested output length is too large.
+92    fn export_keying_material(
+93        &self,
+94        output: &mut [u8],
+95        label: &[u8],
+96        context: &[u8],
+97    ) -> Result<(), ExportKeyingMaterialError>;
+98}
+99
+100/// A pair of keys for bidirectional communication
+101pub struct KeyPair<T> {
+102    /// Key for encrypting data
+103    pub local: T,
+104    /// Key for decrypting data
+105    pub remote: T,
+106}
+107
+108/// A complete set of keys for a certain packet space
+109pub struct Keys {
+110    /// Header protection keys
+111    pub header: KeyPair<Box<dyn HeaderKey>>,
+112    /// Packet protection keys
+113    pub packet: KeyPair<Box<dyn PacketKey>>,
+114}
+115
+116impl Keys {
+117    pub(crate) fn local(&self) -> (&dyn HeaderKey, &dyn PacketKey) {
+118        (self.header.local.as_ref(), self.packet.local.as_ref())
+119    }
+120
+121    pub(crate) fn remote(&self) -> (&dyn HeaderKey, &dyn PacketKey) {
+122        (self.header.remote.as_ref(), self.packet.remote.as_ref())
+123    }
+124}
+125
+126/// Client-side configuration for the crypto protocol
+127pub trait ClientConfig: Send + Sync {
+128    /// Start a client session with this configuration
+129    fn start_session(
+130        &self,
+131        version: u32,
+132        server_name: &str,
+133        params: &TransportParameters,
+134    ) -> Result<Box<dyn Session>, ConnectError>;
+135}
+136
+137/// Server-side configuration for the crypto protocol
+138pub trait ServerConfig: Send + Sync {
+139    /// Create the initial set of keys given the client's initial destination ConnectionId
+140    fn initial_keys(&self, version: u32, dst_cid: ConnectionId)
+141    -> Result<Keys, UnsupportedVersion>;
+142
+143    /// Generate the integrity tag for a retry packet
+144    ///
+145    /// Never called if `initial_keys` rejected `version`.
+146    ///
+147    /// See the specification: <https://www.rfc-editor.org/rfc/rfc9001#name-retry-packet-integrity>
+148    fn retry_tag(&self, version: u32, orig_dst_cid: ConnectionId, packet: &[u8]) -> [u8; 16];
+149
+150    /// Start a server session with this configuration
+151    ///
+152    /// Never called if `initial_keys` rejected `version`.
+153    fn start_session(&self, version: u32, params: &TransportParameters) -> Box<dyn Session>;
+154}
+155
+156/// Keys used to protect packet payloads
+157pub trait PacketKey: Send + Sync {
+158    /// Encrypt the packet payload with the given packet number
+159    fn encrypt(&self, path_id: PathId, packet: u64, buf: &mut [u8], header_len: usize);
+160    /// Decrypt the packet payload with the given packet number
+161    fn decrypt(
+162        &self,
+163        path_id: PathId,
+164        packet: u64,
+165        header: &[u8],
+166        payload: &mut BytesMut,
+167    ) -> Result<(), CryptoError>;
+168    /// The length of the AEAD tag appended to packets on encryption
+169    fn tag_len(&self) -> usize;
+170    /// Maximum number of packets that may be sent using a single key
+171    fn confidentiality_limit(&self) -> u64;
+172    /// Maximum number of incoming packets that may fail decryption before the connection must be
+173    /// abandoned
+174    fn integrity_limit(&self) -> u64;
+175}
+176
+177/// Keys used to protect packet headers
+178pub trait HeaderKey: Send + Sync {
+179    /// Decrypt the given packet's header
+180    fn decrypt(&self, pn_offset: usize, packet: &mut [u8]);
+181    /// Encrypt the given packet's header
+182    fn encrypt(&self, pn_offset: usize, packet: &mut [u8]);
+183    /// The sample size used for this key's algorithm
+184    fn sample_size(&self) -> usize;
+185}
+186
+187/// A key for signing with HMAC-based algorithms
+188pub trait HmacKey: Send + Sync {
+189    /// Method for signing a message
+190    fn sign(&self, data: &[u8], signature_out: &mut [u8]);
+191    /// Length of `sign`'s output
+192    fn signature_len(&self) -> usize;
+193    /// Method for verifying a message
+194    fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>;
+195}
+196
+197/// Error returned by [Session::export_keying_material].
+198///
+199/// This error occurs if the requested output length is too large.
+200#[derive(Debug, PartialEq, Eq)]
+201pub struct ExportKeyingMaterialError;
+202
+203/// The trait for encrypting tokens that power retry packets and NEW_TOKEN frames.
+204pub trait HandshakeTokenKey: Send + Sync {
+205    /// Method for sealing a token in-place.
+206    ///
+207    /// The nonce doesn't need to be attached to the ciphertext,
+208    /// but the authentication tag is expected to be appended to `data`.
+209    fn seal(&self, token_nonce: u128, data: &mut Vec<u8>) -> Result<(), CryptoError>;
+210    /// Method for opening a sealed message `data` in-place.
+211    ///
+212    /// Returns the portion of `data` that contains the decrypted plaintext.
+213    fn open<'a>(&self, token_nonce: u128, data: &'a mut [u8]) -> Result<&'a [u8], CryptoError>;
+214}
+215
+216/// Generic crypto errors
+217#[derive(Debug)]
+218pub struct CryptoError;
+219
+220/// Error indicating that the specified QUIC version is not supported
+221#[derive(Debug)]
+222pub struct UnsupportedVersion;
+223
+224impl From<UnsupportedVersion> for ConnectError {
+225    fn from(_: UnsupportedVersion) -> Self {
+226        Self::UnsupportedVersion
+227    }
+228}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/crypto/ring_like.rs.html b/pr/672/docs/src/noq_proto/crypto/ring_like.rs.html new file mode 100644 index 000000000..75f692af4 --- /dev/null +++ b/pr/672/docs/src/noq_proto/crypto/ring_like.rs.html @@ -0,0 +1,75 @@ +ring_like.rs - source

noq_proto/crypto/
ring_like.rs

1#[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+2use aws_lc_rs::{aead, hkdf, hmac};
+3#[cfg(feature = "ring")]
+4use ring::{aead, 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        hmac::verify(self, data, signature).map_err(|_| CryptoError)
+19    }
+20}
+21
+22/// Implements retry token encryption using HKDF followed by AES-256-GCM.
+23///
+24/// This construction was originally defined in
+25/// <https://github.com/quinn-rs/quinn/issues/783>. The goal is to produce tokens that look
+26/// random to clients, but contain decryptable information for the server.
+27///
+28/// The problem is the 12-byte nonce in AES-GCM: I suspect the original authors of this
+29/// code didn't like the fact that it limits you to ~2^32 safe encryptions before you're getting
+30/// into "dangerous" nonce-reuse territory with randomly-generated nonces.
+31/// You can't use sequence numbers either, though, because that doesn't look random to
+32/// clients and leaks information.
+33pub(crate) struct RetryTokenKey(hkdf::Prk);
+34
+35impl RetryTokenKey {
+36    pub(crate) fn new(rng: &mut impl rand::Rng) -> Self {
+37        let mut master_key = [0u8; 64];
+38        rng.fill_bytes(&mut master_key);
+39        let master_key = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]).extract(&master_key);
+40        Self(master_key)
+41    }
+42
+43    fn derive_aead(&self, token_nonce: u128) -> aead::LessSafeKey {
+44        let nonce_bytes = token_nonce.to_le_bytes();
+45        let info = &[&nonce_bytes[..]];
+46        let okm = self.0.expand(info, hkdf::HKDF_SHA256).unwrap();
+47
+48        let mut key_buffer = [0u8; 32];
+49        okm.fill(&mut key_buffer).unwrap();
+50
+51        let key = aead::UnboundKey::new(&aead::AES_256_GCM, &key_buffer).unwrap();
+52        aead::LessSafeKey::new(key)
+53    }
+54}
+55
+56impl crypto::HandshakeTokenKey for RetryTokenKey {
+57    fn seal(&self, token_nonce: u128, data: &mut Vec<u8>) -> Result<(), CryptoError> {
+58        let aead_key = self.derive_aead(token_nonce);
+59        let nonce = aead::Nonce::assume_unique_for_key([0u8; 12]); // See docs for RetryTokenKey
+60        let aad = aead::Aad::empty();
+61        aead_key
+62            .seal_in_place_append_tag(nonce, aad, data)
+63            .map_err(|_| CryptoError)?;
+64        Ok(())
+65    }
+66
+67    fn open<'a>(&self, token_nonce: u128, data: &'a mut [u8]) -> Result<&'a [u8], CryptoError> {
+68        let aead_key = self.derive_aead(token_nonce);
+69        let aad = aead::Aad::empty();
+70        let nonce = aead::Nonce::assume_unique_for_key([0u8; 12]); // See docs for RetryTokenKey
+71        Ok(aead_key
+72            .open_in_place(nonce, aad, data)
+73            .map_err(|_| CryptoError)?)
+74    }
+75}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/crypto/rustls.rs.html b/pr/672/docs/src/noq_proto/crypto/rustls.rs.html new file mode 100644 index 000000000..4c404765e --- /dev/null +++ b/pr/672/docs/src/noq_proto/crypto/rustls.rs.html @@ -0,0 +1,680 @@ +rustls.rs - source

noq_proto/crypto/
rustls.rs

1use std::{any::Any, io, str, sync::Arc};
+2
+3use aes_gcm::{KeyInit, aead::AeadMutInPlace};
+4use bytes::BytesMut;
+5pub use rustls::Error;
+6use rustls::{
+7    self, CipherSuite,
+8    pki_types::{CertificateDer, ServerName},
+9    quic::{Connection, HeaderProtectionKey, KeyChange, PacketKey, Secrets, Suite, Version},
+10};
+11#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
+12use rustls::{client::danger::ServerCertVerifier, pki_types::PrivateKeyDer};
+13
+14use crate::{
+15    ConnectError, ConnectionId, PathId, Side, TransportError, TransportErrorCode,
+16    crypto::{
+17        self, CryptoError, ExportKeyingMaterialError, HeaderKey, KeyPair, Keys, UnsupportedVersion,
+18    },
+19    transport_parameters::TransportParameters,
+20};
+21
+22impl From<Side> for rustls::Side {
+23    fn from(s: Side) -> Self {
+24        match s {
+25            Side::Client => Self::Client,
+26            Side::Server => Self::Server,
+27        }
+28    }
+29}
+30
+31/// A rustls TLS session
+32pub struct TlsSession {
+33    version: Version,
+34    got_handshake_data: bool,
+35    next_secrets: Option<Secrets>,
+36    inner: Connection,
+37    suite: Suite,
+38}
+39
+40impl TlsSession {
+41    fn side(&self) -> Side {
+42        match self.inner {
+43            Connection::Client(_) => Side::Client,
+44            Connection::Server(_) => Side::Server,
+45        }
+46    }
+47}
+48
+49impl crypto::Session for TlsSession {
+50    fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys {
+51        initial_keys(self.version, dst_cid, side, &self.suite)
+52    }
+53
+54    fn handshake_data(&self) -> Option<Box<dyn Any>> {
+55        if !self.got_handshake_data {
+56            return None;
+57        }
+58        Some(Box::new(HandshakeData {
+59            protocol: self.inner.alpn_protocol().map(|x| x.into()),
+60            server_name: match self.inner {
+61                Connection::Client(_) => None,
+62                Connection::Server(ref session) => session.server_name().map(|x| x.into()),
+63            },
+64            negotiated_key_exchange_group: self
+65                .inner
+66                .negotiated_key_exchange_group()
+67                .map(|kx| kx.name()),
+68        }))
+69    }
+70
+71    /// For the rustls `TlsSession`, the `Any` type is `Vec<rustls::pki_types::CertificateDer>`
+72    fn peer_identity(&self) -> Option<Box<dyn Any>> {
+73        self.inner.peer_certificates().map(|v| -> Box<dyn Any> {
+74            Box::new(
+75                v.iter()
+76                    .map(|v| v.clone().into_owned())
+77                    .collect::<Vec<CertificateDer<'static>>>(),
+78            )
+79        })
+80    }
+81
+82    fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn crypto::PacketKey>)> {
+83        let keys = self.inner.zero_rtt_keys()?;
+84        Some((Box::new(keys.header), Box::new(keys.packet)))
+85    }
+86
+87    fn early_data_accepted(&self) -> Option<bool> {
+88        match self.inner {
+89            Connection::Client(ref session) => Some(session.is_early_data_accepted()),
+90            _ => None,
+91        }
+92    }
+93
+94    fn is_handshaking(&self) -> bool {
+95        self.inner.is_handshaking()
+96    }
+97
+98    fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError> {
+99        self.inner.read_hs(buf).map_err(|e| {
+100            if let Some(alert) = self.inner.alert() {
+101                TransportError {
+102                    code: TransportErrorCode::crypto(alert.into()),
+103                    frame: crate::frame::MaybeFrame::None,
+104                    reason: e.to_string(),
+105                    crypto: Some(Arc::new(e)),
+106                }
+107            } else {
+108                TransportError::PROTOCOL_VIOLATION(format!("TLS error: {e}"))
+109            }
+110        })?;
+111        if !self.got_handshake_data {
+112            // Hack around the lack of an explicit signal from rustls to reflect ClientHello being
+113            // ready on incoming connections, or ALPN negotiation completing on outgoing
+114            // connections.
+115            let have_server_name = match self.inner {
+116                Connection::Client(_) => false,
+117                Connection::Server(ref session) => session.server_name().is_some(),
+118            };
+119            if self.inner.alpn_protocol().is_some() || have_server_name || !self.is_handshaking() {
+120                self.got_handshake_data = true;
+121                return Ok(true);
+122            }
+123        }
+124        Ok(false)
+125    }
+126
+127    fn transport_parameters(&self) -> Result<Option<TransportParameters>, TransportError> {
+128        match self.inner.quic_transport_parameters() {
+129            None => Ok(None),
+130            Some(buf) => match TransportParameters::read(self.side(), &mut io::Cursor::new(buf)) {
+131                Ok(params) => Ok(Some(params)),
+132                Err(e) => Err(e.into()),
+133            },
+134        }
+135    }
+136
+137    fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys> {
+138        let keys = match self.inner.write_hs(buf)? {
+139            KeyChange::Handshake { keys } => keys,
+140            KeyChange::OneRtt { keys, next } => {
+141                self.next_secrets = Some(next);
+142                keys
+143            }
+144        };
+145
+146        Some(Keys {
+147            header: KeyPair {
+148                local: Box::new(keys.local.header),
+149                remote: Box::new(keys.remote.header),
+150            },
+151            packet: KeyPair {
+152                local: Box::new(keys.local.packet),
+153                remote: Box::new(keys.remote.packet),
+154            },
+155        })
+156    }
+157
+158    fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn crypto::PacketKey>>> {
+159        let secrets = self.next_secrets.as_mut()?;
+160        let keys = secrets.next_packet_keys();
+161        Some(KeyPair {
+162            local: Box::new(keys.local),
+163            remote: Box::new(keys.remote),
+164        })
+165    }
+166
+167    fn is_valid_retry(&self, orig_dst_cid: ConnectionId, header: &[u8], payload: &[u8]) -> bool {
+168        if payload.len() < 16 {
+169            return false;
+170        }
+171
+172        let mut pseudo_packet =
+173            Vec::with_capacity(header.len() + payload.len() + orig_dst_cid.len() + 1);
+174        pseudo_packet.push(orig_dst_cid.len() as u8);
+175        pseudo_packet.extend_from_slice(&orig_dst_cid);
+176        pseudo_packet.extend_from_slice(header);
+177        pseudo_packet.extend_from_slice(payload);
+178
+179        let (nonce, key) = match self.version {
+180            Version::V1 => (&RETRY_INTEGRITY_NONCE_V1, &RETRY_INTEGRITY_KEY_V1),
+181            Version::V1Draft => (&RETRY_INTEGRITY_NONCE_DRAFT, &RETRY_INTEGRITY_KEY_DRAFT),
+182            _ => unreachable!(),
+183        };
+184
+185        let Some((aad, tag)) = pseudo_packet.split_last_chunk::<16>() else {
+186            return false; // But length is actually already checked above.
+187        };
+188
+189        // This implements https://www.rfc-editor.org/rfc/rfc9001#name-retry-packet-integrity
+190        let key = aes_gcm::Key::<aes_gcm::Aes128Gcm>::from_slice(key);
+191        let nonce = aes_gcm::Nonce::from_slice(nonce);
+192        let tag = aes_gcm::Tag::from_slice(tag);
+193        aes_gcm::Aes128Gcm::new(key)
+194            .decrypt_in_place_detached(nonce, aad, &mut [], tag)
+195            .is_ok()
+196    }
+197
+198    fn export_keying_material(
+199        &self,
+200        output: &mut [u8],
+201        label: &[u8],
+202        context: &[u8],
+203    ) -> Result<(), ExportKeyingMaterialError> {
+204        self.inner
+205            .export_keying_material(output, label, Some(context))
+206            .map_err(|_| ExportKeyingMaterialError)?;
+207        Ok(())
+208    }
+209}
+210
+211const RETRY_INTEGRITY_KEY_DRAFT: [u8; 16] = [
+212    0xcc, 0xce, 0x18, 0x7e, 0xd0, 0x9a, 0x09, 0xd0, 0x57, 0x28, 0x15, 0x5a, 0x6c, 0xb9, 0x6b, 0xe1,
+213];
+214const RETRY_INTEGRITY_NONCE_DRAFT: [u8; 12] = [
+215    0xe5, 0x49, 0x30, 0xf9, 0x7f, 0x21, 0x36, 0xf0, 0x53, 0x0a, 0x8c, 0x1c,
+216];
+217
+218const RETRY_INTEGRITY_KEY_V1: [u8; 16] = [
+219    0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57, 0x5a, 0x1d, 0x76, 0x6b, 0x54, 0xe3, 0x68, 0xc8, 0x4e,
+220];
+221const RETRY_INTEGRITY_NONCE_V1: [u8; 12] = [
+222    0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb,
+223];
+224
+225impl crypto::HeaderKey for Box<dyn HeaderProtectionKey> {
+226    fn decrypt(&self, pn_offset: usize, packet: &mut [u8]) {
+227        let (header, sample) = packet.split_at_mut(pn_offset + 4);
+228        let (first, rest) = header.split_at_mut(1);
+229        let pn_end = Ord::min(pn_offset + 3, rest.len());
+230        self.decrypt_in_place(
+231            &sample[..self.sample_size()],
+232            &mut first[0],
+233            &mut rest[pn_offset - 1..pn_end],
+234        )
+235        .unwrap();
+236    }
+237
+238    fn encrypt(&self, pn_offset: usize, packet: &mut [u8]) {
+239        let (header, sample) = packet.split_at_mut(pn_offset + 4);
+240        let (first, rest) = header.split_at_mut(1);
+241        let pn_end = Ord::min(pn_offset + 3, rest.len());
+242        self.encrypt_in_place(
+243            &sample[..self.sample_size()],
+244            &mut first[0],
+245            &mut rest[pn_offset - 1..pn_end],
+246        )
+247        .unwrap();
+248    }
+249
+250    fn sample_size(&self) -> usize {
+251        self.sample_len()
+252    }
+253}
+254
+255/// Authentication data for (rustls) TLS session
+256#[non_exhaustive]
+257pub struct HandshakeData {
+258    /// The negotiated application protocol, if ALPN is in use
+259    ///
+260    /// Guaranteed to be set if a nonempty list of protocols was specified for this connection.
+261    pub protocol: Option<Vec<u8>>,
+262    /// The server name specified by the client, if any
+263    ///
+264    /// Always `None` for outgoing connections
+265    pub server_name: Option<String>,
+266    /// The key exchange group negotiated with the peer. Useful for logging and metrics.
+267    ///
+268    /// `None` until the handshake has completed.
+269    pub negotiated_key_exchange_group: Option<rustls::NamedGroup>,
+270}
+271
+272/// A QUIC-compatible TLS client configuration
+273///
+274/// noq implicitly constructs a `QuicClientConfig` with reasonable defaults within
+275/// [`ClientConfig::with_root_certificates()`][root_certs] and [`ClientConfig::try_with_platform_verifier()`][platform].
+276/// Alternatively, `QuicClientConfig`'s [`TryFrom`] implementation can be used to wrap around a
+277/// custom [`rustls::ClientConfig`], in which case care should be taken around certain points:
+278///
+279/// - If `enable_early_data` is not set to true, then sending 0-RTT data will not be possible on
+280///   outgoing connections.
+281/// - The [`rustls::ClientConfig`] must have TLS 1.3 support enabled for conversion to succeed.
+282///
+283/// The object in the `resumption` field of the inner [`rustls::ClientConfig`] determines whether
+284/// calling `into_0rtt` on outgoing connections returns `Ok` or `Err`. It typically allows
+285/// `into_0rtt` to proceed if it recognizes the server name, and defaults to an in-memory cache of
+286/// 256 server names.
+287///
+288/// [root_certs]: crate::config::ClientConfig::with_root_certificates()
+289/// [platform]: crate::config::ClientConfig::try_with_platform_verifier()
+290#[derive(Clone)] // cheap clone: Only one arc clone and some copied pointers
+291pub struct QuicClientConfig {
+292    pub(crate) inner: Arc<rustls::ClientConfig>,
+293    initial: Suite,
+294}
+295
+296impl QuicClientConfig {
+297    #[cfg(all(
+298        feature = "platform-verifier",
+299        any(feature = "aws-lc-rs", feature = "ring")
+300    ))]
+301    pub(crate) fn with_platform_verifier() -> Result<Self, Error> {
+302        use rustls_platform_verifier::BuilderVerifierExt;
+303
+304        // Keep in sync with `inner()` below
+305        let mut inner = rustls::ClientConfig::builder_with_provider(configured_provider())
+306            .with_protocol_versions(&[&rustls::version::TLS13])
+307            .unwrap() // The default providers support TLS 1.3
+308            .with_platform_verifier()?
+309            .with_no_client_auth();
+310
+311        inner.enable_early_data = true;
+312        Ok(Self {
+313            // We're confident that the *ring* default provider contains TLS13_AES_128_GCM_SHA256
+314            initial: initial_suite_from_provider(inner.crypto_provider())
+315                .expect("no initial cipher suite found"),
+316            inner: Arc::new(inner),
+317        })
+318    }
+319
+320    /// Initialize a sane QUIC-compatible TLS client configuration
+321    ///
+322    /// QUIC requires that TLS 1.3 be enabled. Advanced users can use any [`rustls::ClientConfig`] that
+323    /// satisfies this requirement.
+324    #[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
+325    pub(crate) fn new(verifier: Arc<dyn ServerCertVerifier>) -> Self {
+326        let inner = Self::inner(verifier);
+327        Self {
+328            // We're confident that the *ring* default provider contains TLS13_AES_128_GCM_SHA256
+329            initial: initial_suite_from_provider(inner.crypto_provider())
+330                .expect("no initial cipher suite found"),
+331            inner: Arc::new(inner),
+332        }
+333    }
+334
+335    /// Initialize a QUIC-compatible TLS client configuration with a separate initial cipher suite
+336    ///
+337    /// This is useful if you want to avoid the initial cipher suite for traffic encryption.
+338    pub fn with_initial(
+339        inner: Arc<rustls::ClientConfig>,
+340        initial: Suite,
+341    ) -> Result<Self, NoInitialCipherSuite> {
+342        match initial.suite.common.suite {
+343            CipherSuite::TLS13_AES_128_GCM_SHA256 => Ok(Self { inner, initial }),
+344            _ => Err(NoInitialCipherSuite { specific: true }),
+345        }
+346    }
+347
+348    /// Updates the set of ALPN protocols configured in the client config.
+349    pub fn set_alpn_protocols(&mut self, alpn_protocols: Vec<Vec<u8>>) {
+350        let config = Arc::make_mut(&mut self.inner);
+351        config.alpn_protocols = alpn_protocols;
+352    }
+353
+354    #[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
+355    pub(crate) fn inner(verifier: Arc<dyn ServerCertVerifier>) -> rustls::ClientConfig {
+356        // Keep in sync with `with_platform_verifier()` above
+357        let mut config = rustls::ClientConfig::builder_with_provider(configured_provider())
+358            .with_protocol_versions(&[&rustls::version::TLS13])
+359            .unwrap() // The default providers support TLS 1.3
+360            .dangerous()
+361            .with_custom_certificate_verifier(verifier)
+362            .with_no_client_auth();
+363
+364        config.enable_early_data = true;
+365        config
+366    }
+367}
+368
+369impl crypto::ClientConfig for QuicClientConfig {
+370    fn start_session(
+371        &self,
+372        version: u32,
+373        server_name: &str,
+374        params: &TransportParameters,
+375    ) -> Result<Box<dyn crypto::Session>, ConnectError> {
+376        let version = interpret_version(version)?;
+377        Ok(Box::new(TlsSession {
+378            version,
+379            got_handshake_data: false,
+380            next_secrets: None,
+381            inner: rustls::quic::Connection::Client(
+382                rustls::quic::ClientConnection::new(
+383                    self.inner.clone(),
+384                    version,
+385                    ServerName::try_from(server_name)
+386                        .map_err(|_| ConnectError::InvalidServerName(server_name.into()))?
+387                        .to_owned(),
+388                    to_vec(params),
+389                )
+390                .unwrap(),
+391            ),
+392            suite: self.initial,
+393        }))
+394    }
+395}
+396
+397impl TryFrom<rustls::ClientConfig> for QuicClientConfig {
+398    type Error = NoInitialCipherSuite;
+399
+400    fn try_from(inner: rustls::ClientConfig) -> Result<Self, Self::Error> {
+401        Arc::new(inner).try_into()
+402    }
+403}
+404
+405impl TryFrom<Arc<rustls::ClientConfig>> for QuicClientConfig {
+406    type Error = NoInitialCipherSuite;
+407
+408    fn try_from(inner: Arc<rustls::ClientConfig>) -> Result<Self, Self::Error> {
+409        Ok(Self {
+410            initial: initial_suite_from_provider(inner.crypto_provider())
+411                .ok_or(NoInitialCipherSuite { specific: false })?,
+412            inner,
+413        })
+414    }
+415}
+416
+417/// The initial cipher suite (AES-128-GCM-SHA256) is not available
+418///
+419/// When the cipher suite is supplied `with_initial()`, it must be
+420/// [`CipherSuite::TLS13_AES_128_GCM_SHA256`]. When the cipher suite is derived from a config's
+421/// [`CryptoProvider`][provider], that provider must reference a cipher suite with the same ID.
+422///
+423/// [provider]: rustls::crypto::CryptoProvider
+424#[derive(Clone, Debug)]
+425pub struct NoInitialCipherSuite {
+426    /// Whether the initial cipher suite was supplied by the caller
+427    specific: bool,
+428}
+429
+430impl std::fmt::Display for NoInitialCipherSuite {
+431    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+432        f.write_str(match self.specific {
+433            true => "invalid cipher suite specified",
+434            false => "no initial cipher suite found",
+435        })
+436    }
+437}
+438
+439impl std::error::Error for NoInitialCipherSuite {}
+440
+441/// A QUIC-compatible TLS server configuration
+442///
+443/// noq implicitly constructs a `QuicServerConfig` with reasonable defaults within
+444/// [`ServerConfig::with_single_cert()`][single]. Alternatively, `QuicServerConfig`'s [`TryFrom`]
+445/// implementation or `with_initial` method can be used to wrap around a custom
+446/// [`rustls::ServerConfig`], in which case care should be taken around certain points:
+447///
+448/// - If `max_early_data_size` is not set to `u32::MAX`, the server will not be able to accept
+449///   incoming 0-RTT data. QUIC prohibits `max_early_data_size` values other than 0 or `u32::MAX`.
+450/// - The `rustls::ServerConfig` must have TLS 1.3 support enabled for conversion to succeed.
+451///
+452/// [single]: crate::config::ServerConfig::with_single_cert()
+453#[derive(Clone)] // clone is cheap: Just an Arc clone and some copied pointers
+454pub struct QuicServerConfig {
+455    inner: Arc<rustls::ServerConfig>,
+456    initial: Suite,
+457}
+458
+459impl QuicServerConfig {
+460    #[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
+461    pub(crate) fn new(
+462        cert_chain: Vec<CertificateDer<'static>>,
+463        key: PrivateKeyDer<'static>,
+464    ) -> Result<Self, rustls::Error> {
+465        let inner = Self::inner(cert_chain, key)?;
+466        Ok(Self {
+467            // We're confident that the *ring* default provider contains TLS13_AES_128_GCM_SHA256
+468            initial: initial_suite_from_provider(inner.crypto_provider())
+469                .expect("no initial cipher suite found"),
+470            inner: Arc::new(inner),
+471        })
+472    }
+473
+474    /// Initialize a QUIC-compatible TLS client configuration with a separate initial cipher suite
+475    ///
+476    /// This is useful if you want to avoid the initial cipher suite for traffic encryption.
+477    pub fn with_initial(
+478        inner: Arc<rustls::ServerConfig>,
+479        initial: Suite,
+480    ) -> Result<Self, NoInitialCipherSuite> {
+481        match initial.suite.common.suite {
+482            CipherSuite::TLS13_AES_128_GCM_SHA256 => Ok(Self { inner, initial }),
+483            _ => Err(NoInitialCipherSuite { specific: true }),
+484        }
+485    }
+486
+487    /// Updates the set of ALPN protocols configured in the server config.
+488    pub fn set_alpn_protocols(&mut self, alpn_protocols: Vec<Vec<u8>>) {
+489        let config = Arc::make_mut(&mut self.inner);
+490        config.alpn_protocols = alpn_protocols;
+491    }
+492
+493    /// Initialize a sane QUIC-compatible TLS server configuration
+494    ///
+495    /// QUIC requires that TLS 1.3 be enabled, and that the maximum early data size is either 0 or
+496    /// `u32::MAX`. Advanced users can use any [`rustls::ServerConfig`] that satisfies these
+497    /// requirements.
+498    #[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
+499    pub(crate) fn inner(
+500        cert_chain: Vec<CertificateDer<'static>>,
+501        key: PrivateKeyDer<'static>,
+502    ) -> Result<rustls::ServerConfig, rustls::Error> {
+503        let mut inner = rustls::ServerConfig::builder_with_provider(configured_provider())
+504            .with_protocol_versions(&[&rustls::version::TLS13])
+505            .unwrap() // The *ring* default provider supports TLS 1.3
+506            .with_no_client_auth()
+507            .with_single_cert(cert_chain, key)?;
+508
+509        inner.max_early_data_size = u32::MAX;
+510        Ok(inner)
+511    }
+512}
+513
+514impl TryFrom<rustls::ServerConfig> for QuicServerConfig {
+515    type Error = NoInitialCipherSuite;
+516
+517    fn try_from(inner: rustls::ServerConfig) -> Result<Self, Self::Error> {
+518        Arc::new(inner).try_into()
+519    }
+520}
+521
+522impl TryFrom<Arc<rustls::ServerConfig>> for QuicServerConfig {
+523    type Error = NoInitialCipherSuite;
+524
+525    fn try_from(inner: Arc<rustls::ServerConfig>) -> Result<Self, Self::Error> {
+526        Ok(Self {
+527            initial: initial_suite_from_provider(inner.crypto_provider())
+528                .ok_or(NoInitialCipherSuite { specific: false })?,
+529            inner,
+530        })
+531    }
+532}
+533
+534impl crypto::ServerConfig for QuicServerConfig {
+535    fn start_session(
+536        &self,
+537        version: u32,
+538        params: &TransportParameters,
+539    ) -> Box<dyn crypto::Session> {
+540        // Safe: `start_session()` is never called if `initial_keys()` rejected `version`
+541        let version = interpret_version(version).unwrap();
+542        Box::new(TlsSession {
+543            version,
+544            got_handshake_data: false,
+545            next_secrets: None,
+546            inner: rustls::quic::Connection::Server(
+547                rustls::quic::ServerConnection::new(self.inner.clone(), version, to_vec(params))
+548                    .unwrap(),
+549            ),
+550            suite: self.initial,
+551        })
+552    }
+553
+554    fn initial_keys(
+555        &self,
+556        version: u32,
+557        dst_cid: ConnectionId,
+558    ) -> Result<Keys, UnsupportedVersion> {
+559        let version = interpret_version(version)?;
+560        Ok(initial_keys(version, dst_cid, Side::Server, &self.initial))
+561    }
+562
+563    fn retry_tag(&self, version: u32, orig_dst_cid: ConnectionId, packet: &[u8]) -> [u8; 16] {
+564        // Safe: `start_session()` is never called if `initial_keys()` rejected `version`
+565        let version = interpret_version(version).unwrap();
+566        let (nonce, key) = match version {
+567            Version::V1 => (&RETRY_INTEGRITY_NONCE_V1, &RETRY_INTEGRITY_KEY_V1),
+568            Version::V1Draft => (&RETRY_INTEGRITY_NONCE_DRAFT, &RETRY_INTEGRITY_KEY_DRAFT),
+569            _ => unreachable!(),
+570        };
+571
+572        let mut pseudo_packet = Vec::with_capacity(packet.len() + orig_dst_cid.len() + 1);
+573        pseudo_packet.push(orig_dst_cid.len() as u8);
+574        pseudo_packet.extend_from_slice(&orig_dst_cid);
+575        pseudo_packet.extend_from_slice(packet);
+576
+577        let nonce = aes_gcm::Nonce::from_slice(nonce);
+578        let key = aes_gcm::Key::<aes_gcm::Aes128Gcm>::from_slice(key);
+579        let tag = aes_gcm::Aes128Gcm::new(key)
+580            .encrypt_in_place_detached(nonce, &pseudo_packet, &mut [])
+581            .unwrap();
+582        tag.into()
+583    }
+584}
+585
+586pub(crate) fn initial_suite_from_provider(
+587    provider: &Arc<rustls::crypto::CryptoProvider>,
+588) -> Option<Suite> {
+589    provider
+590        .cipher_suites
+591        .iter()
+592        .find_map(|cs| match (cs.suite(), cs.tls13()) {
+593            (rustls::CipherSuite::TLS13_AES_128_GCM_SHA256, Some(suite)) => {
+594                Some(suite.quic_suite())
+595            }
+596            _ => None,
+597        })
+598        .flatten()
+599}
+600
+601#[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+602pub(crate) fn configured_provider() -> Arc<rustls::crypto::CryptoProvider> {
+603    Arc::new(rustls::crypto::aws_lc_rs::default_provider())
+604}
+605
+606#[cfg(feature = "ring")]
+607pub(crate) fn configured_provider() -> Arc<rustls::crypto::CryptoProvider> {
+608    Arc::new(rustls::crypto::ring::default_provider())
+609}
+610
+611fn to_vec(params: &TransportParameters) -> Vec<u8> {
+612    let mut bytes = Vec::new();
+613    params.write(&mut bytes);
+614    bytes
+615}
+616
+617pub(crate) fn initial_keys(
+618    version: Version,
+619    dst_cid: ConnectionId,
+620    side: Side,
+621    suite: &Suite,
+622) -> Keys {
+623    let keys = suite.keys(&dst_cid, side.into(), version);
+624    Keys {
+625        header: KeyPair {
+626            local: Box::new(keys.local.header),
+627            remote: Box::new(keys.remote.header),
+628        },
+629        packet: KeyPair {
+630            local: Box::new(keys.local.packet),
+631            remote: Box::new(keys.remote.packet),
+632        },
+633    }
+634}
+635
+636impl crypto::PacketKey for Box<dyn PacketKey> {
+637    fn encrypt(&self, path_id: PathId, packet: u64, buf: &mut [u8], header_len: usize) {
+638        let (header, payload_tag) = buf.split_at_mut(header_len);
+639        let (payload, tag_storage) = payload_tag.split_at_mut(payload_tag.len() - self.tag_len());
+640        let tag = self
+641            .encrypt_in_place_for_path(path_id.as_u32(), packet, &*header, payload)
+642            .unwrap();
+643        tag_storage.copy_from_slice(tag.as_ref());
+644    }
+645
+646    fn decrypt(
+647        &self,
+648        path_id: PathId,
+649        packet: u64,
+650        header: &[u8],
+651        payload: &mut BytesMut,
+652    ) -> Result<(), CryptoError> {
+653        let plain = self
+654            .decrypt_in_place_for_path(path_id.as_u32(), packet, header, payload.as_mut())
+655            .map_err(|_| CryptoError)?;
+656        let plain_len = plain.len();
+657        payload.truncate(plain_len);
+658        Ok(())
+659    }
+660
+661    fn tag_len(&self) -> usize {
+662        (**self).tag_len()
+663    }
+664
+665    fn confidentiality_limit(&self) -> u64 {
+666        (**self).confidentiality_limit()
+667    }
+668
+669    fn integrity_limit(&self) -> u64 {
+670        (**self).integrity_limit()
+671    }
+672}
+673
+674fn interpret_version(version: u32) -> Result<Version, UnsupportedVersion> {
+675    match version {
+676        0xff00_001d..=0xff00_0020 => Ok(Version::V1Draft),
+677        0x0000_0001 | 0xff00_0021..=0xff00_0022 => Ok(Version::V1),
+678        _ => Err(UnsupportedVersion),
+679    }
+680}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/endpoint.rs.html b/pr/672/docs/src/noq_proto/endpoint.rs.html new file mode 100644 index 000000000..b1d9ea877 --- /dev/null +++ b/pr/672/docs/src/noq_proto/endpoint.rs.html @@ -0,0 +1,1648 @@ +endpoint.rs - source

noq_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::{Buf, BufMut, Bytes, BytesMut};
+11use rand::{Rng, RngExt, 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, Decodable, Encodable, UnexpectedEnd},
+22    config::{ClientConfig, EndpointConfig, ServerConfig},
+23    connection::{Connection, ConnectionError, SideArgs},
+24    crypto::{self, Keys, UnsupportedVersion},
+25    frame,
+26    packet::{
+27        FixedLengthConnectionIdParser, Header, InitialHeader, InitialPacket, PacketDecodeError,
+28        PacketNumber, PartialDecode, ProtectedInitialHeader,
+29    },
+30    shared::{
+31        ConnectionEvent, ConnectionEventInner, ConnectionId, DatagramConnectionEvent, EcnCodepoint,
+32        EndpointEvent, EndpointEventInner, IssuedCid,
+33    },
+34    token::{IncomingToken, InvalidRetryTokenError, Token, TokenPayload},
+35    transport_parameters::{PreferredAddress, TransportParameters},
+36};
+37
+38/// The main entry point to the library
+39///
+40/// This object performs no I/O whatsoever. Instead, it consumes incoming packets and
+41/// connection-generated events via `handle` and `handle_event`.
+42pub struct Endpoint {
+43    rng: StdRng,
+44    index: ConnectionIndex,
+45    connections: Slab<ConnectionMeta>,
+46    local_cid_generator: Box<dyn ConnectionIdGenerator>,
+47    config: Arc<EndpointConfig>,
+48    server_config: Option<Arc<ServerConfig>>,
+49    /// Whether the underlying UDP socket promises not to fragment packets
+50    allow_mtud: bool,
+51    /// Time at which a stateless reset was most recently sent
+52    last_stateless_reset: Option<Instant>,
+53    /// Buffered Initial and 0-RTT messages for pending incoming connections
+54    incoming_buffers: Slab<IncomingBuffer>,
+55    all_incoming_buffers_total_bytes: u64,
+56}
+57
+58impl Endpoint {
+59    /// Create a new endpoint
+60    ///
+61    /// `allow_mtud` enables path MTU detection when requested by `Connection` configuration for
+62    /// better performance. This requires that outgoing packets are never fragmented, which can be
+63    /// achieved via e.g. the `IPV6_DONTFRAG` socket option.
+64    pub fn new(
+65        config: Arc<EndpointConfig>,
+66        server_config: Option<Arc<ServerConfig>>,
+67        allow_mtud: bool,
+68    ) -> Self {
+69        Self {
+70            rng: config
+71                .rng_seed
+72                .map_or_else(|| StdRng::from_rng(&mut rand::rng()), StdRng::from_seed),
+73            index: ConnectionIndex::default(),
+74            connections: Slab::new(),
+75            local_cid_generator: (config.connection_id_generator_factory.as_ref())(),
+76            config,
+77            server_config,
+78            allow_mtud,
+79            last_stateless_reset: None,
+80            incoming_buffers: Slab::new(),
+81            all_incoming_buffers_total_bytes: 0,
+82        }
+83    }
+84
+85    /// Replace the server configuration, affecting new incoming connections only
+86    pub fn set_server_config(&mut self, server_config: Option<Arc<ServerConfig>>) {
+87        self.server_config = server_config;
+88    }
+89
+90    /// Process `EndpointEvent`s emitted from related `Connection`s
+91    ///
+92    /// In turn, processing this event may return a `ConnectionEvent` for the same `Connection`.
+93    pub fn handle_event(
+94        &mut self,
+95        ch: ConnectionHandle,
+96        event: EndpointEvent,
+97    ) -> Option<ConnectionEvent> {
+98        use EndpointEventInner::*;
+99        match event.0 {
+100            NeedIdentifiers(path_id, now, n) => {
+101                return Some(self.send_new_identifiers(path_id, now, ch, n));
+102            }
+103            ResetToken(path_id, remote, token) => {
+104                if let Some(old) = self.connections[ch]
+105                    .reset_token
+106                    .insert(path_id, (remote, token))
+107                {
+108                    self.index.connection_reset_tokens.remove(old.0, old.1);
+109                }
+110                if self.index.connection_reset_tokens.insert(remote, token, ch) {
+111                    warn!("duplicate reset token");
+112                }
+113            }
+114            RetireResetToken(path_id) => {
+115                if let Some(old) = self.connections[ch].reset_token.remove(&path_id) {
+116                    self.index.connection_reset_tokens.remove(old.0, old.1);
+117                }
+118            }
+119            RetireConnectionId(now, path_id, seq, allow_more_cids) => {
+120                if let Some(cid) = self.connections[ch]
+121                    .local_cids
+122                    .get_mut(&path_id)
+123                    .and_then(|pcid| pcid.cids.remove(&seq))
+124                {
+125                    trace!(%path_id, "local CID retired {}: {}", seq, cid);
+126                    self.index.retire(cid);
+127                    if allow_more_cids {
+128                        return Some(self.send_new_identifiers(path_id, now, ch, 1));
+129                    }
+130                }
+131            }
+132            Draining => {
+133                // Nothing to do.
+134            }
+135            Drained => {
+136                if let Some(conn) = self.connections.try_remove(ch.0) {
+137                    self.index.remove(&conn);
+138                } else {
+139                    // This indicates a bug in downstream code, which could cause spurious
+140                    // connection loss instead of this error if the CID was (re)allocated prior to
+141                    // the illegal call.
+142                    error!(id = ch.0, "unknown connection drained");
+143                }
+144            }
+145        }
+146        None
+147    }
+148
+149    /// Process an incoming UDP datagram
+150    pub fn handle(
+151        &mut self,
+152        now: Instant,
+153        network_path: FourTuple,
+154        ecn: Option<EcnCodepoint>,
+155        data: BytesMut,
+156        buf: &mut Vec<u8>,
+157    ) -> Option<DatagramEvent> {
+158        // Partially decode packet or short-circuit if unable
+159        let datagram_len = data.len();
+160        let mut event = match PartialDecode::new(
+161            data,
+162            &FixedLengthConnectionIdParser::new(self.local_cid_generator.cid_len()),
+163            &self.config.supported_versions,
+164            self.config.grease_quic_bit,
+165        ) {
+166            Ok((first_decode, remaining)) => DatagramConnectionEvent {
+167                now,
+168                network_path,
+169                path_id: PathId::ZERO, // Corrected later for existing paths
+170                ecn,
+171                first_decode,
+172                remaining,
+173            },
+174            Err(PacketDecodeError::UnsupportedVersion {
+175                src_cid,
+176                dst_cid,
+177                version,
+178            }) => {
+179                if self.server_config.is_none() {
+180                    debug!("dropping packet with unsupported version");
+181                    return None;
+182                }
+183                trace!("sending version negotiation");
+184                // Negotiate versions
+185                Header::VersionNegotiate {
+186                    random: self.rng.random::<u8>() | 0x40,
+187                    src_cid: dst_cid,
+188                    dst_cid: src_cid,
+189                }
+190                .encode(buf);
+191                // Grease with a reserved version
+192                buf.write::<u32>(match version {
+193                    0x0a1a_2a3a => 0x0a1a_2a4a,
+194                    _ => 0x0a1a_2a3a,
+195                });
+196                for &version in &self.config.supported_versions {
+197                    buf.write(version);
+198                }
+199                return Some(DatagramEvent::Response(Transmit {
+200                    destination: network_path.remote,
+201                    ecn: None,
+202                    size: buf.len(),
+203                    segment_size: None,
+204                    src_ip: network_path.local_ip,
+205                }));
+206            }
+207            Err(e) => {
+208                trace!("malformed header: {}", e);
+209                return None;
+210            }
+211        };
+212
+213        let dst_cid = event.first_decode.dst_cid();
+214
+215        if let Some(route_to) = self.index.get(&network_path, &event.first_decode) {
+216            event.path_id = match route_to {
+217                RouteDatagramTo::Incoming(_) => PathId::ZERO,
+218                RouteDatagramTo::Connection(_, path_id) => path_id,
+219            };
+220            match route_to {
+221                RouteDatagramTo::Incoming(incoming_idx) => {
+222                    let incoming_buffer = &mut self.incoming_buffers[incoming_idx];
+223                    let config = &self.server_config.as_ref().unwrap();
+224
+225                    if incoming_buffer
+226                        .total_bytes
+227                        .checked_add(datagram_len as u64)
+228                        .is_some_and(|n| n <= config.incoming_buffer_size)
+229                        && self
+230                            .all_incoming_buffers_total_bytes
+231                            .checked_add(datagram_len as u64)
+232                            .is_some_and(|n| n <= config.incoming_buffer_size_total)
+233                    {
+234                        incoming_buffer.datagrams.push(event);
+235                        incoming_buffer.total_bytes += datagram_len as u64;
+236                        self.all_incoming_buffers_total_bytes += datagram_len as u64;
+237                    }
+238
+239                    None
+240                }
+241                RouteDatagramTo::Connection(ch, _path_id) => Some(DatagramEvent::ConnectionEvent(
+242                    ch,
+243                    ConnectionEvent(ConnectionEventInner::Datagram(event)),
+244                )),
+245            }
+246        } else if event.first_decode.initial_header().is_some() {
+247            // Potentially create a new connection
+248
+249            self.handle_first_packet(datagram_len, event, network_path, buf)
+250        } else if event.first_decode.has_long_header() {
+251            debug!(
+252                "ignoring non-initial packet for unknown connection {}",
+253                dst_cid
+254            );
+255            None
+256        } else if !event.first_decode.is_initial()
+257            && self.local_cid_generator.validate(dst_cid).is_err()
+258        {
+259            debug!("dropping packet with invalid CID");
+260            None
+261        } else if dst_cid.is_empty() {
+262            trace!("dropping unrecognized short packet without ID");
+263            None
+264        } else {
+265            // If we got this far, we're receiving a seemingly valid packet for an unknown
+266            // connection. Send a stateless reset if possible.
+267            self.stateless_reset(now, datagram_len, network_path, dst_cid, buf)
+268                .map(DatagramEvent::Response)
+269        }
+270    }
+271
+272    /// Builds a stateless reset packet to respond with
+273    fn stateless_reset(
+274        &mut self,
+275        now: Instant,
+276        inciting_dgram_len: usize,
+277        network_path: FourTuple,
+278        dst_cid: ConnectionId,
+279        buf: &mut Vec<u8>,
+280    ) -> Option<Transmit> {
+281        if self
+282            .last_stateless_reset
+283            .is_some_and(|last| last + self.config.min_reset_interval > now)
+284        {
+285            debug!("ignoring unexpected packet within minimum stateless reset interval");
+286            return None;
+287        }
+288
+289        /// Minimum amount of padding for the stateless reset to look like a short-header packet
+290        const MIN_PADDING_LEN: usize = 5;
+291
+292        // Prevent amplification attacks and reset loops by ensuring we pad to at most 1 byte
+293        // smaller than the inciting packet.
+294        let max_padding_len = match inciting_dgram_len.checked_sub(RESET_TOKEN_SIZE) {
+295            Some(headroom) if headroom > MIN_PADDING_LEN => headroom - 1,
+296            _ => {
+297                debug!(
+298                    "ignoring unexpected {} byte packet: not larger than minimum stateless reset size",
+299                    inciting_dgram_len
+300                );
+301                return None;
+302            }
+303        };
+304
+305        debug!(%dst_cid, %network_path.remote, "sending stateless reset");
+306        self.last_stateless_reset = Some(now);
+307        // Resets with at least this much padding can't possibly be distinguished from real packets
+308        const IDEAL_MIN_PADDING_LEN: usize = MIN_PADDING_LEN + MAX_CID_SIZE;
+309        let padding_len = if max_padding_len <= IDEAL_MIN_PADDING_LEN {
+310            max_padding_len
+311        } else {
+312            self.rng
+313                .random_range(IDEAL_MIN_PADDING_LEN..max_padding_len)
+314        };
+315        buf.reserve(padding_len + RESET_TOKEN_SIZE);
+316        buf.resize(padding_len, 0);
+317        self.rng.fill_bytes(&mut buf[0..padding_len]);
+318        buf[0] = 0b0100_0000 | (buf[0] >> 2);
+319        buf.extend_from_slice(&ResetToken::new(&*self.config.reset_key, dst_cid));
+320
+321        debug_assert!(buf.len() < inciting_dgram_len);
+322
+323        Some(Transmit {
+324            destination: network_path.remote,
+325            ecn: None,
+326            size: buf.len(),
+327            segment_size: None,
+328            src_ip: network_path.local_ip,
+329        })
+330    }
+331
+332    /// Initiate a connection
+333    pub fn connect(
+334        &mut self,
+335        now: Instant,
+336        config: ClientConfig,
+337        remote: SocketAddr,
+338        server_name: &str,
+339    ) -> Result<(ConnectionHandle, Connection), ConnectError> {
+340        if self.cids_exhausted() {
+341            return Err(ConnectError::CidsExhausted);
+342        }
+343        if remote.port() == 0 || remote.ip().is_unspecified() {
+344            return Err(ConnectError::InvalidRemoteAddress(remote));
+345        }
+346        if !self.config.supported_versions.contains(&config.version) {
+347            return Err(ConnectError::UnsupportedVersion);
+348        }
+349
+350        let remote_id = (config.initial_dst_cid_provider)();
+351        trace!(initial_dcid = %remote_id);
+352
+353        let ch = ConnectionHandle(self.connections.vacant_key());
+354        let local_cid = self.new_cid(ch, PathId::ZERO);
+355        let params = TransportParameters::new(
+356            &config.transport,
+357            &self.config,
+358            self.local_cid_generator.as_ref(),
+359            local_cid,
+360            None,
+361            &mut self.rng,
+362        );
+363        let tls = config
+364            .crypto
+365            .start_session(config.version, server_name, &params)?;
+366
+367        let conn = self.add_connection(
+368            ch,
+369            config.version,
+370            remote_id,
+371            local_cid,
+372            remote_id,
+373            FourTuple {
+374                remote,
+375                local_ip: None,
+376            },
+377            now,
+378            tls,
+379            config.transport,
+380            SideArgs::Client {
+381                token_store: config.token_store,
+382                server_name: server_name.into(),
+383            },
+384            &params,
+385        );
+386        Ok((ch, conn))
+387    }
+388
+389    /// Generates new CIDs and creates message to send to the connection state
+390    fn send_new_identifiers(
+391        &mut self,
+392        path_id: PathId,
+393        now: Instant,
+394        ch: ConnectionHandle,
+395        num: u64,
+396    ) -> ConnectionEvent {
+397        let mut ids = vec![];
+398        for _ in 0..num {
+399            let id = self.new_cid(ch, path_id);
+400            let cid_meta = self.connections[ch].local_cids.entry(path_id).or_default();
+401            let sequence = cid_meta.issued;
+402            cid_meta.issued += 1;
+403            cid_meta.cids.insert(sequence, id);
+404            ids.push(IssuedCid {
+405                path_id,
+406                sequence,
+407                id,
+408                reset_token: ResetToken::new(&*self.config.reset_key, id),
+409            });
+410        }
+411        ConnectionEvent(ConnectionEventInner::NewIdentifiers(
+412            ids,
+413            now,
+414            self.local_cid_generator.cid_len(),
+415            self.local_cid_generator.cid_lifetime(),
+416        ))
+417    }
+418
+419    /// Generate a connection ID for `ch`
+420    fn new_cid(&mut self, ch: ConnectionHandle, path_id: PathId) -> ConnectionId {
+421        loop {
+422            let cid = self.local_cid_generator.generate_cid();
+423            if cid.is_empty() {
+424                // Zero-length CID; nothing to track
+425                debug_assert_eq!(self.local_cid_generator.cid_len(), 0);
+426                return cid;
+427            }
+428            if let hash_map::Entry::Vacant(e) = self.index.connection_ids.entry(cid) {
+429                e.insert((ch, path_id));
+430                break cid;
+431            }
+432        }
+433    }
+434
+435    fn handle_first_packet(
+436        &mut self,
+437        datagram_len: usize,
+438        event: DatagramConnectionEvent,
+439        network_path: FourTuple,
+440        buf: &mut Vec<u8>,
+441    ) -> Option<DatagramEvent> {
+442        let dst_cid = event.first_decode.dst_cid();
+443        let header = event.first_decode.initial_header().unwrap();
+444
+445        let Some(server_config) = &self.server_config else {
+446            debug!("packet for unrecognized connection {}", dst_cid);
+447            return self
+448                .stateless_reset(event.now, datagram_len, network_path, dst_cid, buf)
+449                .map(DatagramEvent::Response);
+450        };
+451
+452        if datagram_len < MIN_INITIAL_SIZE as usize {
+453            debug!("ignoring short initial for connection {}", dst_cid);
+454            return None;
+455        }
+456
+457        let crypto = match server_config.crypto.initial_keys(header.version, dst_cid) {
+458            Ok(keys) => keys,
+459            Err(UnsupportedVersion) => {
+460                // This probably indicates that the user set supported_versions incorrectly in
+461                // `EndpointConfig`.
+462                debug!(
+463                    "ignoring initial packet version {:#x} unsupported by cryptographic layer",
+464                    header.version
+465                );
+466                return None;
+467            }
+468        };
+469
+470        if let Err(reason) = self.early_validate_first_packet(header) {
+471            return Some(DatagramEvent::Response(self.initial_close(
+472                header.version,
+473                network_path,
+474                &crypto,
+475                header.src_cid,
+476                reason,
+477                buf,
+478            )));
+479        }
+480
+481        let packet = match event.first_decode.finish(Some(&*crypto.header.remote)) {
+482            Ok(packet) => packet,
+483            Err(e) => {
+484                trace!("unable to decode initial packet: {}", e);
+485                return None;
+486            }
+487        };
+488
+489        if !packet.reserved_bits_valid() {
+490            debug!("dropping connection attempt with invalid reserved bits");
+491            return None;
+492        }
+493
+494        let Header::Initial(header) = packet.header else {
+495            panic!("non-initial packet in handle_first_packet()");
+496        };
+497
+498        let server_config = self.server_config.as_ref().unwrap().clone();
+499
+500        let token = match IncomingToken::from_header(&header, &server_config, network_path.remote) {
+501            Ok(token) => token,
+502            Err(InvalidRetryTokenError) => {
+503                debug!("rejecting invalid retry token");
+504                return Some(DatagramEvent::Response(self.initial_close(
+505                    header.version,
+506                    network_path,
+507                    &crypto,
+508                    header.src_cid,
+509                    TransportError::INVALID_TOKEN(""),
+510                    buf,
+511                )));
+512            }
+513        };
+514
+515        let incoming_idx = self.incoming_buffers.insert(IncomingBuffer::default());
+516        self.index
+517            .insert_initial_incoming(header.dst_cid, incoming_idx);
+518
+519        Some(DatagramEvent::NewConnection(Incoming {
+520            received_at: event.now,
+521            network_path,
+522            ecn: event.ecn,
+523            packet: InitialPacket {
+524                header,
+525                header_data: packet.header_data,
+526                payload: packet.payload,
+527            },
+528            rest: event.remaining,
+529            crypto,
+530            token,
+531            incoming_idx,
+532            improper_drop_warner: IncomingImproperDropWarner,
+533        }))
+534    }
+535
+536    /// Attempt to accept this incoming connection (an error may still occur)
+537    // box err to avoid clippy::result_large_err
+538    pub fn accept(
+539        &mut self,
+540        mut incoming: Incoming,
+541        now: Instant,
+542        buf: &mut Vec<u8>,
+543        server_config: Option<Arc<ServerConfig>>,
+544    ) -> Result<(ConnectionHandle, Connection), Box<AcceptError>> {
+545        let remote_address_validated = incoming.remote_address_validated();
+546        incoming.improper_drop_warner.dismiss();
+547        let incoming_buffer = self.incoming_buffers.remove(incoming.incoming_idx);
+548        self.all_incoming_buffers_total_bytes -= incoming_buffer.total_bytes;
+549
+550        let packet_number = incoming.packet.header.number.expand(0);
+551        let InitialHeader {
+552            src_cid,
+553            dst_cid,
+554            version,
+555            ..
+556        } = incoming.packet.header;
+557        let server_config =
+558            server_config.unwrap_or_else(|| self.server_config.as_ref().unwrap().clone());
+559
+560        if server_config
+561            .transport
+562            .max_idle_timeout
+563            .is_some_and(|timeout| {
+564                incoming.received_at + Duration::from_millis(timeout.into()) <= now
+565            })
+566        {
+567            debug!("abandoning accept of stale initial");
+568            self.index.remove_initial(dst_cid);
+569            return Err(Box::new(AcceptError {
+570                cause: ConnectionError::TimedOut,
+571                response: None,
+572            }));
+573        }
+574
+575        if self.cids_exhausted() {
+576            debug!("refusing connection");
+577            self.index.remove_initial(dst_cid);
+578            return Err(Box::new(AcceptError {
+579                cause: ConnectionError::CidsExhausted,
+580                response: Some(self.initial_close(
+581                    version,
+582                    incoming.network_path,
+583                    &incoming.crypto,
+584                    src_cid,
+585                    TransportError::CONNECTION_REFUSED(""),
+586                    buf,
+587                )),
+588            }));
+589        }
+590
+591        if incoming
+592            .crypto
+593            .packet
+594            .remote
+595            .decrypt(
+596                PathId::ZERO,
+597                packet_number,
+598                &incoming.packet.header_data,
+599                &mut incoming.packet.payload,
+600            )
+601            .is_err()
+602        {
+603            debug!(packet_number, "failed to authenticate initial packet");
+604            self.index.remove_initial(dst_cid);
+605            return Err(Box::new(AcceptError {
+606                cause: TransportError::PROTOCOL_VIOLATION("authentication failed").into(),
+607                response: None,
+608            }));
+609        };
+610
+611        let ch = ConnectionHandle(self.connections.vacant_key());
+612        let local_cid = self.new_cid(ch, PathId::ZERO);
+613        let mut params = TransportParameters::new(
+614            &server_config.transport,
+615            &self.config,
+616            self.local_cid_generator.as_ref(),
+617            local_cid,
+618            Some(&server_config),
+619            &mut self.rng,
+620        );
+621        params.stateless_reset_token = Some(ResetToken::new(&*self.config.reset_key, local_cid));
+622        params.original_dst_cid = Some(incoming.token.orig_dst_cid);
+623        params.retry_src_cid = incoming.token.retry_src_cid;
+624        let mut pref_addr_cid = None;
+625        if server_config.has_preferred_address() {
+626            let cid = self.new_cid(ch, PathId::ZERO);
+627            pref_addr_cid = Some(cid);
+628            params.preferred_address = Some(PreferredAddress {
+629                address_v4: server_config.preferred_address_v4,
+630                address_v6: server_config.preferred_address_v6,
+631                connection_id: cid,
+632                stateless_reset_token: ResetToken::new(&*self.config.reset_key, cid),
+633            });
+634        }
+635
+636        let tls = server_config.crypto.start_session(version, &params);
+637        let transport_config = server_config.transport.clone();
+638        let mut conn = self.add_connection(
+639            ch,
+640            version,
+641            dst_cid,
+642            local_cid,
+643            src_cid,
+644            incoming.network_path,
+645            incoming.received_at,
+646            tls,
+647            transport_config,
+648            SideArgs::Server {
+649                server_config,
+650                pref_addr_cid,
+651                path_validated: remote_address_validated,
+652            },
+653            &params,
+654        );
+655        self.index.insert_initial(dst_cid, ch);
+656
+657        match conn.handle_first_packet(
+658            incoming.received_at,
+659            incoming.network_path,
+660            incoming.ecn,
+661            packet_number,
+662            incoming.packet,
+663            incoming.rest,
+664        ) {
+665            Ok(()) => {
+666                trace!(
+667                    id = ch.0,
+668                    icid = %dst_cid,
+669                    network_path = %incoming.network_path,
+670                    "new connection",
+671                );
+672
+673                for event in incoming_buffer.datagrams {
+674                    conn.handle_event(ConnectionEvent(ConnectionEventInner::Datagram(event)))
+675                }
+676
+677                Ok((ch, conn))
+678            }
+679            Err(e) => {
+680                debug!("handshake failed: {}", e);
+681                self.handle_event(ch, EndpointEvent(EndpointEventInner::Drained));
+682                let response = match e {
+683                    ConnectionError::TransportError(ref e) => Some(self.initial_close(
+684                        version,
+685                        incoming.network_path,
+686                        &incoming.crypto,
+687                        src_cid,
+688                        e.clone(),
+689                        buf,
+690                    )),
+691                    _ => None,
+692                };
+693                Err(Box::new(AcceptError { cause: e, response }))
+694            }
+695        }
+696    }
+697
+698    /// Check if we should refuse a connection attempt regardless of the packet's contents
+699    fn early_validate_first_packet(
+700        &mut self,
+701        header: &ProtectedInitialHeader,
+702    ) -> Result<(), TransportError> {
+703        let config = &self.server_config.as_ref().unwrap();
+704        if self.cids_exhausted() || self.incoming_buffers.len() >= config.max_incoming {
+705            return Err(TransportError::CONNECTION_REFUSED(""));
+706        }
+707
+708        // RFC9000 §7.2 dictates that initial (client-chosen) destination CIDs must be at least 8
+709        // bytes. If this is a Retry packet, then the length must instead match our usual CID
+710        // length. If we ever issue non-Retry address validation tokens via `NEW_TOKEN`, then we'll
+711        // also need to validate CID length for those after decoding the token.
+712        if header.dst_cid.len() < 8
+713            && (header.token_pos.is_empty()
+714                || header.dst_cid.len() != self.local_cid_generator.cid_len())
+715        {
+716            debug!(
+717                "rejecting connection due to invalid DCID length {}",
+718                header.dst_cid.len()
+719            );
+720            return Err(TransportError::PROTOCOL_VIOLATION(
+721                "invalid destination CID length",
+722            ));
+723        }
+724
+725        Ok(())
+726    }
+727
+728    /// Reject this incoming connection attempt
+729    pub fn refuse(&mut self, incoming: Incoming, buf: &mut Vec<u8>) -> Transmit {
+730        self.clean_up_incoming(&incoming);
+731        incoming.improper_drop_warner.dismiss();
+732
+733        self.initial_close(
+734            incoming.packet.header.version,
+735            incoming.network_path,
+736            &incoming.crypto,
+737            incoming.packet.header.src_cid,
+738            TransportError::CONNECTION_REFUSED(""),
+739            buf,
+740        )
+741    }
+742
+743    /// Respond with a retry packet, requiring the client to retry with address validation
+744    ///
+745    /// Errors if `incoming.may_retry()` is false.
+746    pub fn retry(&mut self, incoming: Incoming, buf: &mut Vec<u8>) -> Result<Transmit, RetryError> {
+747        if !incoming.may_retry() {
+748            return Err(RetryError(Box::new(incoming)));
+749        }
+750
+751        self.clean_up_incoming(&incoming);
+752        incoming.improper_drop_warner.dismiss();
+753
+754        let server_config = self.server_config.as_ref().unwrap();
+755
+756        // First Initial
+757        // The peer will use this as the DCID of its following Initials. Initial DCIDs are
+758        // looked up separately from Handshake/Data DCIDs, so there is no risk of collision
+759        // with established connections. In the unlikely event that a collision occurs
+760        // between two connections in the initial phase, both will fail fast and may be
+761        // retried by the application layer.
+762        let local_cid = self.local_cid_generator.generate_cid();
+763
+764        let payload = TokenPayload::Retry {
+765            address: incoming.network_path.remote,
+766            orig_dst_cid: incoming.packet.header.dst_cid,
+767            issued: server_config.time_source.now(),
+768        };
+769        let token = Token::new(payload, &mut self.rng).encode(&*server_config.token_key);
+770
+771        let header = Header::Retry {
+772            src_cid: local_cid,
+773            dst_cid: incoming.packet.header.src_cid,
+774            version: incoming.packet.header.version,
+775        };
+776
+777        let encode = header.encode(buf);
+778        buf.put_slice(&token);
+779        buf.extend_from_slice(&server_config.crypto.retry_tag(
+780            incoming.packet.header.version,
+781            incoming.packet.header.dst_cid,
+782            buf,
+783        ));
+784        encode.finish(buf, &*incoming.crypto.header.local, None);
+785
+786        Ok(Transmit {
+787            destination: incoming.network_path.remote,
+788            ecn: None,
+789            size: buf.len(),
+790            segment_size: None,
+791            src_ip: incoming.network_path.local_ip,
+792        })
+793    }
+794
+795    /// Ignore this incoming connection attempt, not sending any packet in response
+796    ///
+797    /// Doing this actively, rather than merely dropping the [`Incoming`], is necessary to prevent
+798    /// memory leaks due to state within [`Endpoint`] tracking the incoming connection.
+799    pub fn ignore(&mut self, incoming: Incoming) {
+800        self.clean_up_incoming(&incoming);
+801        incoming.improper_drop_warner.dismiss();
+802    }
+803
+804    /// Clean up endpoint data structures associated with an `Incoming`.
+805    fn clean_up_incoming(&mut self, incoming: &Incoming) {
+806        self.index.remove_initial(incoming.packet.header.dst_cid);
+807        let incoming_buffer = self.incoming_buffers.remove(incoming.incoming_idx);
+808        self.all_incoming_buffers_total_bytes -= incoming_buffer.total_bytes;
+809    }
+810
+811    fn add_connection(
+812        &mut self,
+813        ch: ConnectionHandle,
+814        version: u32,
+815        init_cid: ConnectionId,
+816        local_cid: ConnectionId,
+817        remote_cid: ConnectionId,
+818        network_path: FourTuple,
+819        now: Instant,
+820        tls: Box<dyn crypto::Session>,
+821        transport_config: Arc<TransportConfig>,
+822        side_args: SideArgs,
+823        // Only used for qlog.
+824        params: &TransportParameters,
+825    ) -> Connection {
+826        let mut rng_seed = [0; 32];
+827        self.rng.fill_bytes(&mut rng_seed);
+828        let side = side_args.side();
+829        let pref_addr_cid = side_args.pref_addr_cid();
+830
+831        let qlog =
+832            transport_config.create_qlog_sink(side_args.side(), network_path.remote, init_cid, now);
+833
+834        qlog.emit_connection_started(
+835            now,
+836            local_cid,
+837            remote_cid,
+838            network_path.remote,
+839            network_path.local_ip,
+840            params,
+841        );
+842
+843        let conn = Connection::new(
+844            self.config.clone(),
+845            transport_config,
+846            init_cid,
+847            local_cid,
+848            remote_cid,
+849            network_path,
+850            tls,
+851            self.local_cid_generator.as_ref(),
+852            now,
+853            version,
+854            self.allow_mtud,
+855            rng_seed,
+856            side_args,
+857            qlog,
+858        );
+859
+860        let mut path_cids = PathLocalCids::default();
+861        path_cids.cids.insert(path_cids.issued, local_cid);
+862        path_cids.issued += 1;
+863
+864        if let Some(cid) = pref_addr_cid {
+865            debug_assert_eq!(path_cids.issued, 1, "preferred address cid seq must be 1");
+866            path_cids.cids.insert(path_cids.issued, cid);
+867            path_cids.issued += 1;
+868        }
+869
+870        let id = self.connections.insert(ConnectionMeta {
+871            init_cid,
+872            local_cids: FxHashMap::from_iter([(PathId::ZERO, path_cids)]),
+873            network_path,
+874            side,
+875            reset_token: Default::default(),
+876        });
+877        debug_assert_eq!(id, ch.0, "connection handle allocation out of sync");
+878
+879        self.index.insert_conn(network_path, local_cid, ch, side);
+880
+881        conn
+882    }
+883
+884    fn initial_close(
+885        &mut self,
+886        version: u32,
+887        network_path: FourTuple,
+888        crypto: &Keys,
+889        remote_id: ConnectionId,
+890        reason: TransportError,
+891        buf: &mut Vec<u8>,
+892    ) -> Transmit {
+893        // We don't need to worry about CID collisions in initial closes because the peer
+894        // shouldn't respond, and if it does, and the CID collides, we'll just drop the
+895        // unexpected response.
+896        let local_id = self.local_cid_generator.generate_cid();
+897        let number = PacketNumber::U8(0);
+898        let header = Header::Initial(InitialHeader {
+899            dst_cid: remote_id,
+900            src_cid: local_id,
+901            number,
+902            token: Bytes::new(),
+903            version,
+904        });
+905
+906        let partial_encode = header.encode(buf);
+907        let max_len =
+908            INITIAL_MTU as usize - partial_encode.header_len - crypto.packet.local.tag_len();
+909        frame::Close::from(reason).encoder(max_len).encode(buf);
+910        buf.resize(buf.len() + crypto.packet.local.tag_len(), 0);
+911        partial_encode.finish(
+912            buf,
+913            &*crypto.header.local,
+914            Some((0, Default::default(), &*crypto.packet.local)),
+915        );
+916        Transmit {
+917            destination: network_path.remote,
+918            ecn: None,
+919            size: buf.len(),
+920            segment_size: None,
+921            src_ip: network_path.local_ip,
+922        }
+923    }
+924
+925    /// Access the configuration used by this endpoint
+926    pub fn config(&self) -> &EndpointConfig {
+927        &self.config
+928    }
+929
+930    /// Number of connections that are currently open
+931    pub fn open_connections(&self) -> usize {
+932        self.connections.len()
+933    }
+934
+935    /// Counter for the number of bytes currently used
+936    /// in the buffers for Initial and 0-RTT messages for pending incoming connections
+937    pub fn incoming_buffer_bytes(&self) -> u64 {
+938        self.all_incoming_buffers_total_bytes
+939    }
+940
+941    #[cfg(test)]
+942    pub(crate) fn known_connections(&self) -> usize {
+943        let x = self.connections.len();
+944        debug_assert_eq!(x, self.index.connection_ids_initial.len());
+945        // Not all connections have known reset tokens
+946        debug_assert!(x >= self.index.connection_reset_tokens.0.len());
+947        // Not all connections have unique remotes, and 0-length CIDs might not be in use.
+948        debug_assert!(x >= self.index.incoming_connection_remotes.len());
+949        debug_assert!(x >= self.index.outgoing_connection_remotes.len());
+950        x
+951    }
+952
+953    #[cfg(test)]
+954    pub(crate) fn known_cids(&self) -> usize {
+955        self.index.connection_ids.len()
+956    }
+957
+958    /// Whether we've used up 3/4 of the available CID space
+959    ///
+960    /// We leave some space unused so that `new_cid` can be relied upon to finish quickly. We don't
+961    /// bother to check when CID longer than 4 bytes are used because 2^40 connections is a lot.
+962    fn cids_exhausted(&self) -> bool {
+963        let cid_len = self.local_cid_generator.cid_len();
+964        if cid_len == 0 || cid_len > 4 {
+965            return false;
+966        }
+967
+968        // Keep this architecture-independent: on 32-bit targets, 2usize.pow(32) overflows.
+969        let bits = (cid_len * 8) as u32;
+970        let space = 1u64 << bits;
+971        let reserve = 1u64 << (bits - 2);
+972        let len = self.index.connection_ids.len() as u64;
+973
+974        len > (space - reserve)
+975    }
+976}
+977
+978impl fmt::Debug for Endpoint {
+979    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+980        fmt.debug_struct("Endpoint")
+981            .field("rng", &self.rng)
+982            .field("index", &self.index)
+983            .field("connections", &self.connections)
+984            .field("config", &self.config)
+985            .field("server_config", &self.server_config)
+986            // incoming_buffers too large
+987            .field("incoming_buffers.len", &self.incoming_buffers.len())
+988            .field(
+989                "all_incoming_buffers_total_bytes",
+990                &self.all_incoming_buffers_total_bytes,
+991            )
+992            .finish()
+993    }
+994}
+995
+996/// Buffered Initial and 0-RTT messages for a pending incoming connection
+997#[derive(Default)]
+998struct IncomingBuffer {
+999    datagrams: Vec<DatagramConnectionEvent>,
+1000    total_bytes: u64,
+1001}
+1002
+1003/// Part of protocol state incoming datagrams can be routed to
+1004#[derive(Copy, Clone, Debug)]
+1005enum RouteDatagramTo {
+1006    Incoming(usize),
+1007    Connection(ConnectionHandle, PathId),
+1008}
+1009
+1010/// Maps packets to existing connections
+1011#[derive(Default, Debug)]
+1012struct ConnectionIndex {
+1013    /// Identifies connections based on the initial DCID the peer utilized
+1014    ///
+1015    /// Uses a standard `HashMap` to protect against hash collision attacks.
+1016    ///
+1017    /// Used by the server, not the client.
+1018    connection_ids_initial: HashMap<ConnectionId, RouteDatagramTo>,
+1019    /// Identifies connections based on locally created CIDs
+1020    ///
+1021    /// Uses a cheaper hash function since keys are locally created
+1022    connection_ids: FxHashMap<ConnectionId, (ConnectionHandle, PathId)>,
+1023    /// Identifies incoming connections with zero-length CIDs
+1024    ///
+1025    /// Uses a standard `HashMap` to protect against hash collision attacks.
+1026    incoming_connection_remotes: HashMap<FourTuple, ConnectionHandle>,
+1027    /// Identifies outgoing connections with zero-length CIDs
+1028    ///
+1029    /// We don't yet support explicit source addresses for client connections, and zero-length CIDs
+1030    /// require a unique 4-tuple, so at most one client connection with zero-length local CIDs
+1031    /// may be established per remote. We must omit the local address from the key because we don't
+1032    /// necessarily know what address we're sending from, and hence receiving at.
+1033    ///
+1034    /// Uses a standard `HashMap` to protect against hash collision attacks.
+1035    // TODO(matheus23): It's possible this could be changed now that we track the full 4-tuple on the client side, too.
+1036    outgoing_connection_remotes: HashMap<SocketAddr, ConnectionHandle>,
+1037    /// Reset tokens provided by the peer for the CID each connection is currently sending to
+1038    ///
+1039    /// Incoming stateless resets do not have correct CIDs, so we need this to identify the correct
+1040    /// recipient, if any.
+1041    connection_reset_tokens: ResetTokenTable,
+1042}
+1043
+1044impl ConnectionIndex {
+1045    /// Associate an incoming connection with its initial destination CID
+1046    fn insert_initial_incoming(&mut self, dst_cid: ConnectionId, incoming_key: usize) {
+1047        if dst_cid.is_empty() {
+1048            return;
+1049        }
+1050        self.connection_ids_initial
+1051            .insert(dst_cid, RouteDatagramTo::Incoming(incoming_key));
+1052    }
+1053
+1054    /// Remove an association with an initial destination CID
+1055    fn remove_initial(&mut self, dst_cid: ConnectionId) {
+1056        if dst_cid.is_empty() {
+1057            return;
+1058        }
+1059        let removed = self.connection_ids_initial.remove(&dst_cid);
+1060        debug_assert!(removed.is_some());
+1061    }
+1062
+1063    /// Associate a connection with its initial destination CID
+1064    fn insert_initial(&mut self, dst_cid: ConnectionId, connection: ConnectionHandle) {
+1065        if dst_cid.is_empty() {
+1066            return;
+1067        }
+1068        self.connection_ids_initial.insert(
+1069            dst_cid,
+1070            RouteDatagramTo::Connection(connection, PathId::ZERO),
+1071        );
+1072    }
+1073
+1074    /// Associate a connection with its first locally-chosen destination CID if used, or otherwise
+1075    /// its current 4-tuple
+1076    fn insert_conn(
+1077        &mut self,
+1078        network_path: FourTuple,
+1079        dst_cid: ConnectionId,
+1080        connection: ConnectionHandle,
+1081        side: Side,
+1082    ) {
+1083        match dst_cid.len() {
+1084            0 => match side {
+1085                Side::Server => {
+1086                    self.incoming_connection_remotes
+1087                        .insert(network_path, connection);
+1088                }
+1089                Side::Client => {
+1090                    self.outgoing_connection_remotes
+1091                        .insert(network_path.remote, connection);
+1092                }
+1093            },
+1094            _ => {
+1095                self.connection_ids
+1096                    .insert(dst_cid, (connection, PathId::ZERO));
+1097            }
+1098        }
+1099    }
+1100
+1101    /// Discard a connection ID
+1102    fn retire(&mut self, dst_cid: ConnectionId) {
+1103        self.connection_ids.remove(&dst_cid);
+1104    }
+1105
+1106    /// Remove all references to a connection
+1107    fn remove(&mut self, conn: &ConnectionMeta) {
+1108        if conn.side.is_server() {
+1109            self.remove_initial(conn.init_cid);
+1110        }
+1111        for cid in conn
+1112            .local_cids
+1113            .values()
+1114            .flat_map(|pcids| pcids.cids.values())
+1115        {
+1116            self.connection_ids.remove(cid);
+1117        }
+1118        self.incoming_connection_remotes.remove(&conn.network_path);
+1119        self.outgoing_connection_remotes
+1120            .remove(&conn.network_path.remote);
+1121        for (remote, token) in conn.reset_token.values() {
+1122            self.connection_reset_tokens.remove(*remote, *token);
+1123        }
+1124    }
+1125
+1126    /// Find the existing connection that `datagram` should be routed to, if any
+1127    fn get(&self, network_path: &FourTuple, datagram: &PartialDecode) -> Option<RouteDatagramTo> {
+1128        if !datagram.dst_cid().is_empty()
+1129            && let Some(&(ch, path_id)) = self.connection_ids.get(&datagram.dst_cid())
+1130        {
+1131            return Some(RouteDatagramTo::Connection(ch, path_id));
+1132        }
+1133        if (datagram.is_initial() || datagram.is_0rtt())
+1134            && let Some(&ch) = self.connection_ids_initial.get(&datagram.dst_cid())
+1135        {
+1136            return Some(ch);
+1137        }
+1138        if datagram.dst_cid().is_empty() {
+1139            if let Some(&ch) = self.incoming_connection_remotes.get(network_path) {
+1140                // Never multipath because QUIC-MULTIPATH 1.1 mandates the use of non-zero
+1141                // length CIDs.  So this is always PathId::ZERO.
+1142                return Some(RouteDatagramTo::Connection(ch, PathId::ZERO));
+1143            }
+1144            if let Some(&ch) = self.outgoing_connection_remotes.get(&network_path.remote) {
+1145                // Like above, QUIC-MULTIPATH 1.1 mandates the use of non-zero length CIDs.
+1146                return Some(RouteDatagramTo::Connection(ch, PathId::ZERO));
+1147            }
+1148        }
+1149        let data = datagram.data();
+1150        if data.len() < RESET_TOKEN_SIZE {
+1151            return None;
+1152        }
+1153        // For stateless resets the PathId is meaningless since it closes the entire
+1154        // connection regardless of path.  So use PathId::ZERO.
+1155        self.connection_reset_tokens
+1156            .get(network_path.remote, &data[data.len() - RESET_TOKEN_SIZE..])
+1157            .cloned()
+1158            .map(|ch| RouteDatagramTo::Connection(ch, PathId::ZERO))
+1159    }
+1160}
+1161
+1162#[derive(Debug)]
+1163pub(crate) struct ConnectionMeta {
+1164    init_cid: ConnectionId,
+1165    /// Locally issues CIDs for each path
+1166    local_cids: FxHashMap<PathId, PathLocalCids>,
+1167    /// Remote/local addresses the connection began with
+1168    ///
+1169    /// Only needed to support connections with zero-length CIDs, which cannot migrate, so we don't
+1170    /// bother keeping it up to date.
+1171    network_path: FourTuple,
+1172    side: Side,
+1173    /// Reset tokens provided by the peer for CIDs we're currently sending to
+1174    ///
+1175    /// Since each reset token is for a CID, it is also for a fixed remote address which is
+1176    /// also stored. This allows us to look up which reset tokens we might expect from a
+1177    /// given remote address, see [`ResetTokenTable`].
+1178    ///
+1179    /// Each path has its own active CID. We use the [`PathId`] as a unique index, allowing
+1180    /// us to retire the reset token when a path is abandoned.
+1181    // TODO(matheus23): Should be migrated to make reset tokens per 4-tuple instead of per remote addr
+1182    reset_token: FxHashMap<PathId, (SocketAddr, ResetToken)>,
+1183}
+1184
+1185/// Local connection IDs for a single path
+1186#[derive(Debug, Default)]
+1187struct PathLocalCids {
+1188    /// Number of connection IDs that have been issued in (PATH_)NEW_CONNECTION_ID frames
+1189    ///
+1190    /// Another way of saying this is that this is the next sequence number to be issued.
+1191    issued: u64,
+1192    /// Issues CIDs indexed by their sequence number.
+1193    cids: FxHashMap<u64, ConnectionId>,
+1194}
+1195
+1196/// Internal identifier for a `Connection` currently associated with an endpoint
+1197#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
+1198pub struct ConnectionHandle(pub usize);
+1199
+1200impl From<ConnectionHandle> for usize {
+1201    fn from(x: ConnectionHandle) -> Self {
+1202        x.0
+1203    }
+1204}
+1205
+1206impl Index<ConnectionHandle> for Slab<ConnectionMeta> {
+1207    type Output = ConnectionMeta;
+1208    fn index(&self, ch: ConnectionHandle) -> &ConnectionMeta {
+1209        &self[ch.0]
+1210    }
+1211}
+1212
+1213impl IndexMut<ConnectionHandle> for Slab<ConnectionMeta> {
+1214    fn index_mut(&mut self, ch: ConnectionHandle) -> &mut ConnectionMeta {
+1215        &mut self[ch.0]
+1216    }
+1217}
+1218
+1219/// Event resulting from processing a single datagram
+1220pub enum DatagramEvent {
+1221    /// The datagram is redirected to its `Connection`
+1222    ConnectionEvent(ConnectionHandle, ConnectionEvent),
+1223    /// The datagram may result in starting a new `Connection`
+1224    NewConnection(Incoming),
+1225    /// Response generated directly by the endpoint
+1226    Response(Transmit),
+1227}
+1228
+1229/// An incoming connection for which the server has not yet begun its part of the handshake.
+1230#[derive(derive_more::Debug)]
+1231pub struct Incoming {
+1232    #[debug(skip)]
+1233    received_at: Instant,
+1234    network_path: FourTuple,
+1235    ecn: Option<EcnCodepoint>,
+1236    #[debug(skip)]
+1237    packet: InitialPacket,
+1238    #[debug(skip)]
+1239    rest: Option<BytesMut>,
+1240    #[debug(skip)]
+1241    crypto: Keys,
+1242    token: IncomingToken,
+1243    incoming_idx: usize,
+1244    #[debug(skip)]
+1245    improper_drop_warner: IncomingImproperDropWarner,
+1246}
+1247
+1248impl Incoming {
+1249    /// The local IP address which was used when the peer established the connection
+1250    pub fn local_ip(&self) -> Option<IpAddr> {
+1251        self.network_path.local_ip
+1252    }
+1253
+1254    /// The peer's UDP address
+1255    pub fn remote_address(&self) -> SocketAddr {
+1256        self.network_path.remote
+1257    }
+1258
+1259    /// Whether the socket address that is initiating this connection has been validated
+1260    ///
+1261    /// This means that the sender of the initial packet has proved that they can receive traffic
+1262    /// sent to `self.remote_address()`.
+1263    ///
+1264    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
+1265    /// The inverse is not guaranteed.
+1266    pub fn remote_address_validated(&self) -> bool {
+1267        self.token.validated
+1268    }
+1269
+1270    /// Whether it is legal to respond with a retry packet
+1271    ///
+1272    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
+1273    /// The inverse is not guaranteed.
+1274    pub fn may_retry(&self) -> bool {
+1275        self.token.retry_src_cid.is_none()
+1276    }
+1277
+1278    /// The original destination connection ID sent by the client
+1279    pub fn orig_dst_cid(&self) -> ConnectionId {
+1280        self.token.orig_dst_cid
+1281    }
+1282
+1283    /// Decrypt the Initial packet payload
+1284    ///
+1285    /// This clones and decrypts the packet payload (~1200 bytes).
+1286    /// Can be used to extract information from the TLS ClientHello without completing the handshake.
+1287    pub fn decrypt(&self) -> Option<DecryptedInitial> {
+1288        let packet_number = self.packet.header.number.expand(0);
+1289        let mut payload = self.packet.payload.clone();
+1290        self.crypto
+1291            .packet
+1292            .remote
+1293            .decrypt(
+1294                PathId::ZERO,
+1295                packet_number,
+1296                &self.packet.header_data,
+1297                &mut payload,
+1298            )
+1299            .ok()?;
+1300        Some(DecryptedInitial(payload.freeze()))
+1301    }
+1302}
+1303
+1304/// Decrypted payload of a QUIC Initial packet
+1305///
+1306/// Obtained via [`Incoming::decrypt`]. Can be used to extract information from
+1307/// the TLS ClientHello without completing the handshake.
+1308pub struct DecryptedInitial(Bytes);
+1309
+1310impl DecryptedInitial {
+1311    /// Best-effort extraction of the ALPN protocols from the TLS ClientHello
+1312    ///
+1313    /// Parses the CRYPTO frames to extract the ALPN extension. This is intended
+1314    /// for routing and filtering; it is not guaranteed to succeed if the
+1315    /// ClientHello spans multiple packets. Returns `None` if parsing fails.
+1316    pub fn alpns(&self) -> Option<IncomingAlpns> {
+1317        let frames = frame::Iter::new(self.0.clone()).ok()?;
+1318        let mut first = None;
+1319        let mut rest = Vec::new();
+1320        for frame in frames {
+1321            match frame {
+1322                Ok(frame::Frame::Crypto(crypto)) => match first {
+1323                    None => first = Some(crypto),
+1324                    Some(_) => rest.push(crypto),
+1325                },
+1326                Err(_) => return None,
+1327                _ => {}
+1328            }
+1329        }
+1330        let first = first?;
+1331
+1332        // Fast path: single CRYPTO frame at offset 0 (no extra allocation)
+1333        if rest.is_empty() && first.offset == 0 {
+1334            let data = find_alpn_data(&first.data).ok()?;
+1335            return Some(IncomingAlpns { data, pos: 0 });
+1336        }
+1337
+1338        // Slow path: reassemble multiple CRYPTO frames
+1339        rest.push(first);
+1340        let source = assemble_crypto_frames(&mut rest)?;
+1341        let data = find_alpn_data(&source).ok()?;
+1342        Some(IncomingAlpns { data, pos: 0 })
+1343    }
+1344}
+1345
+1346/// TLS handshake type for ClientHello messages
+1347/// <https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2>
+1348const TLS_HANDSHAKE_TYPE_CLIENT_HELLO: u8 = 0x01;
+1349/// TLS extension type for Application-Layer Protocol Negotiation
+1350/// <https://www.rfc-editor.org/rfc/rfc7301#section-3.1>
+1351const TLS_EXTENSION_TYPE_ALPN: u16 = 0x0010;
+1352/// Size of the fixed-length fields in a ClientHello (client_version + random)
+1353/// <https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2>
+1354const TLS_CLIENT_HELLO_FIXED_LEN: usize = 2 + 32;
+1355
+1356/// Iterator over ALPN protocol names from a TLS ClientHello
+1357///
+1358/// Yields protocol names as [`Bytes`] slices. On the common fast path (single
+1359/// CRYPTO frame), the only allocation is the payload clone for decryption.
+1360pub struct IncomingAlpns {
+1361    data: Bytes,
+1362    pos: usize,
+1363}
+1364
+1365impl Iterator for IncomingAlpns {
+1366    type Item = Result<Bytes, UnexpectedEnd>;
+1367
+1368    fn next(&mut self) -> Option<Self::Item> {
+1369        if self.pos >= self.data.len() {
+1370            return None;
+1371        }
+1372        let len = self.data[self.pos] as usize;
+1373        self.pos += 1;
+1374        if self.pos + len > self.data.len() {
+1375            return Some(Err(UnexpectedEnd));
+1376        }
+1377        let proto = self.data.slice(self.pos..self.pos + len);
+1378        self.pos += len;
+1379        Some(Ok(proto))
+1380    }
+1381}
+1382
+1383/// Sort CRYPTO frames by offset and concatenate into a contiguous `Bytes`
+1384///
+1385/// Returns `None` if there are gaps in the stream.
+1386fn assemble_crypto_frames(frames: &mut [frame::Crypto]) -> Option<Bytes> {
+1387    frames.sort_by_key(|f| f.offset);
+1388    let capacity = frames.iter().map(|f| f.data.len()).sum();
+1389    let mut buf = Vec::with_capacity(capacity);
+1390    for f in frames.iter() {
+1391        let start = f.offset as usize;
+1392        if start > buf.len() {
+1393            return None;
+1394        }
+1395        let end = start + f.data.len();
+1396        if end > buf.len() {
+1397            buf.extend_from_slice(&f.data[buf.len() - start..]);
+1398        }
+1399    }
+1400    Some(Bytes::from(buf))
+1401}
+1402
+1403/// Locate the raw ALPN protocol list data within a TLS ClientHello message
+1404///
+1405/// Parses the ClientHello in `source` and returns a [`Bytes`] containing the
+1406/// u8-length-prefixed protocol names (after the outer ProtocolNameList u16
+1407/// length prefix). The returned `Bytes` is a zero-copy slice of `source`.
+1408fn find_alpn_data(source: &Bytes) -> Result<Bytes, UnexpectedEnd> {
+1409    let mut r = &**source;
+1410
+1411    if u8::decode(&mut r)? != TLS_HANDSHAKE_TYPE_CLIENT_HELLO {
+1412        return Err(UnexpectedEnd);
+1413    }
+1414
+1415    // Handshake message length (u24), scopes the remainder
+1416    let len = decode_u24(&mut r)?;
+1417    let mut body = take(&mut r, len)?;
+1418
+1419    // Client version + random
+1420    skip(&mut body, TLS_CLIENT_HELLO_FIXED_LEN)?;
+1421
+1422    // Session ID, cipher suites, compression methods
+1423    skip_u8_prefixed(&mut body)?;
+1424    skip_u16_prefixed(&mut body)?;
+1425    skip_u8_prefixed(&mut body)?;
+1426
+1427    // Extensions
+1428    let mut exts = take_u16_prefixed(&mut body)?;
+1429    while exts.has_remaining() {
+1430        let ext_type = u16::decode(&mut exts)?;
+1431        let ext_data = take_u16_prefixed(&mut exts)?;
+1432        if ext_type == TLS_EXTENSION_TYPE_ALPN {
+1433            let list = take_u16_prefixed(&mut &*ext_data)?;
+1434            return Ok(source.slice_ref(list));
+1435        }
+1436    }
+1437    Err(UnexpectedEnd)
+1438}
+1439
+1440/// Decode a big-endian u24 as usize
+1441fn decode_u24(r: &mut &[u8]) -> Result<usize, UnexpectedEnd> {
+1442    let a = u8::decode(r)?;
+1443    let b = u8::decode(r)?;
+1444    let c = u8::decode(r)?;
+1445    Ok(u32::from_be_bytes([0, a, b, c]) as usize)
+1446}
+1447
+1448/// Take `len` bytes from the front and return them as a sub-slice
+1449fn take<'a>(r: &mut &'a [u8], len: usize) -> Result<&'a [u8], UnexpectedEnd> {
+1450    if r.remaining() < len {
+1451        return Err(UnexpectedEnd);
+1452    }
+1453    let data = &r[..len];
+1454    r.advance(len);
+1455    Ok(data)
+1456}
+1457
+1458/// Read a u16 length prefix and return the sub-slice it covers
+1459fn take_u16_prefixed<'a>(r: &mut &'a [u8]) -> Result<&'a [u8], UnexpectedEnd> {
+1460    let len = u16::decode(r)? as usize;
+1461    take(r, len)
+1462}
+1463
+1464/// Advance past `n` bytes
+1465fn skip(r: &mut &[u8], len: usize) -> Result<(), UnexpectedEnd> {
+1466    take(r, len)?;
+1467    Ok(())
+1468}
+1469
+1470/// Skip a u8-length-prefixed field
+1471fn skip_u8_prefixed(r: &mut &[u8]) -> Result<(), UnexpectedEnd> {
+1472    let len = u8::decode(r)? as usize;
+1473    skip(r, len)
+1474}
+1475
+1476/// Skip a u16-length-prefixed field
+1477fn skip_u16_prefixed(r: &mut &[u8]) -> Result<(), UnexpectedEnd> {
+1478    let len = u16::decode(r)? as usize;
+1479    skip(r, len)
+1480}
+1481
+1482struct IncomingImproperDropWarner;
+1483
+1484impl IncomingImproperDropWarner {
+1485    fn dismiss(self) {
+1486        mem::forget(self);
+1487    }
+1488}
+1489
+1490impl Drop for IncomingImproperDropWarner {
+1491    fn drop(&mut self) {
+1492        warn!(
+1493            "noq_proto::Incoming dropped without passing to Endpoint::accept/refuse/retry/ignore \
+1494               (may cause memory leak and eventual inability to accept new connections)"
+1495        );
+1496    }
+1497}
+1498
+1499/// Errors in the parameters being used to create a new connection
+1500///
+1501/// These arise before any I/O has been performed.
+1502#[derive(Debug, Error, Clone, PartialEq, Eq)]
+1503pub enum ConnectError {
+1504    /// The endpoint can no longer create new connections
+1505    ///
+1506    /// Indicates that a necessary component of the endpoint has been dropped or otherwise disabled.
+1507    #[error("endpoint stopping")]
+1508    EndpointStopping,
+1509    /// The connection could not be created because not enough of the CID space is available
+1510    ///
+1511    /// Try using longer connection IDs
+1512    #[error("CIDs exhausted")]
+1513    CidsExhausted,
+1514    /// The given server name was malformed
+1515    #[error("invalid server name: {0}")]
+1516    InvalidServerName(String),
+1517    /// The remote [`SocketAddr`] supplied was malformed
+1518    ///
+1519    /// Examples include attempting to connect to port 0, or using an inappropriate address family.
+1520    #[error("invalid remote address: {0}")]
+1521    InvalidRemoteAddress(SocketAddr),
+1522    /// No default client configuration was set up
+1523    ///
+1524    /// Use `Endpoint::connect_with` to specify a client configuration.
+1525    #[error("no default client config")]
+1526    NoDefaultClientConfig,
+1527    /// The local endpoint does not support the QUIC version specified in the client configuration
+1528    #[error("unsupported QUIC version")]
+1529    UnsupportedVersion,
+1530}
+1531
+1532/// Error type for attempting to accept an [`Incoming`]
+1533#[derive(Debug)]
+1534pub struct AcceptError {
+1535    /// Underlying error describing reason for failure
+1536    pub cause: ConnectionError,
+1537    /// Optional response to transmit back
+1538    pub response: Option<Transmit>,
+1539}
+1540
+1541/// Error for attempting to retry an [`Incoming`] which already bears a token from a previous retry
+1542#[derive(Debug, Error)]
+1543#[error("retry() with validated Incoming")]
+1544pub struct RetryError(Box<Incoming>);
+1545
+1546impl RetryError {
+1547    /// Get the [`Incoming`]
+1548    pub fn into_incoming(self) -> Incoming {
+1549        *self.0
+1550    }
+1551}
+1552
+1553/// Reset Tokens which are associated with peer socket addresses
+1554///
+1555/// The standard `HashMap` is used since both `SocketAddr` and `ResetToken` are
+1556/// peer generated and might be usable for hash collision attacks.
+1557#[derive(Default, Debug)]
+1558struct ResetTokenTable(HashMap<SocketAddr, HashMap<ResetToken, ConnectionHandle>>);
+1559
+1560impl ResetTokenTable {
+1561    fn insert(&mut self, remote: SocketAddr, token: ResetToken, ch: ConnectionHandle) -> bool {
+1562        self.0
+1563            .entry(remote)
+1564            .or_default()
+1565            .insert(token, ch)
+1566            .is_some()
+1567    }
+1568
+1569    fn remove(&mut self, remote: SocketAddr, token: ResetToken) {
+1570        use std::collections::hash_map::Entry;
+1571        match self.0.entry(remote) {
+1572            Entry::Vacant(_) => {}
+1573            Entry::Occupied(mut e) => {
+1574                e.get_mut().remove(&token);
+1575                if e.get().is_empty() {
+1576                    e.remove_entry();
+1577                }
+1578            }
+1579        }
+1580    }
+1581
+1582    fn get(&self, remote: SocketAddr, token: &[u8]) -> Option<&ConnectionHandle> {
+1583        let token = ResetToken::from(<[u8; RESET_TOKEN_SIZE]>::try_from(token).ok()?);
+1584        self.0.get(&remote)?.get(&token)
+1585    }
+1586}
+1587
+1588#[cfg(test)]
+1589mod tests {
+1590    use super::*;
+1591
+1592    #[test]
+1593    fn assemble_contiguous() {
+1594        let data = b"hello world";
+1595        let mut frames = vec![
+1596            frame::Crypto {
+1597                offset: 0,
+1598                data: Bytes::from_static(&data[..5]),
+1599            },
+1600            frame::Crypto {
+1601                offset: 5,
+1602                data: Bytes::from_static(&data[5..]),
+1603            },
+1604        ];
+1605        assert_eq!(&assemble_crypto_frames(&mut frames).unwrap()[..], &data[..]);
+1606    }
+1607
+1608    #[test]
+1609    fn assemble_out_of_order() {
+1610        let data = b"hello world";
+1611        let mut frames = vec![
+1612            frame::Crypto {
+1613                offset: 5,
+1614                data: Bytes::from_static(&data[5..]),
+1615            },
+1616            frame::Crypto {
+1617                offset: 0,
+1618                data: Bytes::from_static(&data[..5]),
+1619            },
+1620        ];
+1621        assert_eq!(&assemble_crypto_frames(&mut frames).unwrap()[..], &data[..]);
+1622    }
+1623
+1624    #[test]
+1625    fn assemble_with_overlap() {
+1626        let data = b"hello world";
+1627        let mut frames = vec![
+1628            frame::Crypto {
+1629                offset: 0,
+1630                data: Bytes::from_static(&data[..7]),
+1631            },
+1632            frame::Crypto {
+1633                offset: 5,
+1634                data: Bytes::from_static(&data[5..]),
+1635            },
+1636        ];
+1637        assert_eq!(&assemble_crypto_frames(&mut frames).unwrap()[..], &data[..]);
+1638    }
+1639
+1640    #[test]
+1641    fn assemble_with_gap() {
+1642        let mut frames = vec![frame::Crypto {
+1643            offset: 10,
+1644            data: Bytes::from_static(b"world"),
+1645        }];
+1646        assert!(assemble_crypto_frames(&mut frames).is_none());
+1647    }
+1648}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/frame.rs.html b/pr/672/docs/src/noq_proto/frame.rs.html new file mode 100644 index 000000000..3a12df1b4 --- /dev/null +++ b/pr/672/docs/src/noq_proto/frame.rs.html @@ -0,0 +1,2867 @@ +frame.rs - source

noq_proto/
frame.rs

1use std::{
+2    borrow::Cow,
+3    fmt::{self, Display},
+4    mem,
+5    net::{IpAddr, SocketAddr},
+6    ops::{Range, RangeInclusive},
+7};
+8
+9use bytes::{Buf, BufMut, Bytes};
+10use tinyvec::TinyVec;
+11
+12use crate::{
+13    Dir, MAX_CID_SIZE, RESET_TOKEN_SIZE, ResetToken, StreamId, TransportError, TransportErrorCode,
+14    VarInt,
+15    coding::{self, BufExt, BufMutExt, Decodable, Encodable, UnexpectedEnd},
+16    connection::PathId,
+17    range_set::ArrayRangeSet,
+18    shared::{ConnectionId, EcnCodepoint},
+19};
+20
+21#[cfg(feature = "qlog")]
+22use super::connection::qlog::ToQlog;
+23
+24#[cfg(test)]
+25use crate::varint::varint_u64;
+26#[cfg(test)]
+27use proptest::{collection, prelude::any, strategy::Strategy};
+28#[cfg(test)]
+29use test_strategy::Arbitrary;
+30
+31#[derive(
+32    Copy, Clone, Eq, PartialEq, derive_more::Debug, derive_more::Display, enum_assoc::Assoc,
+33)]
+34#[cfg_attr(test, derive(Arbitrary))]
+35#[display(rename_all = "SCREAMING_SNAKE_CASE")]
+36#[allow(missing_docs)]
+37#[func(
+38    pub(crate) const fn to_u64(self) -> u64,
+39    const fn from_u64(rev: u64) -> Option<Self>,
+40)]
+41pub enum FrameType {
+42    #[assoc(to_u64 = 0x00)]
+43    Padding,
+44    #[assoc(to_u64 = 0x01)]
+45    Ping,
+46    #[assoc(to_u64 = 0x02)]
+47    Ack,
+48    #[assoc(to_u64 = 0x03)]
+49    AckEcn,
+50    #[assoc(to_u64 = 0x04)]
+51    ResetStream,
+52    #[assoc(to_u64 = 0x05)]
+53    StopSending,
+54    #[assoc(to_u64 = 0x06)]
+55    Crypto,
+56    #[assoc(to_u64 = 0x07)]
+57    NewToken,
+58    // STREAM
+59    #[assoc(to_u64 = _0.to_u64())]
+60    Stream(StreamInfo),
+61    #[assoc(to_u64 = 0x10)]
+62    MaxData,
+63    #[assoc(to_u64 = 0x11)]
+64    MaxStreamData,
+65    #[assoc(to_u64 = 0x12)]
+66    MaxStreamsBidi,
+67    #[assoc(to_u64 = 0x13)]
+68    MaxStreamsUni,
+69    #[assoc(to_u64 = 0x14)]
+70    DataBlocked,
+71    #[assoc(to_u64 = 0x15)]
+72    StreamDataBlocked,
+73    #[assoc(to_u64 = 0x16)]
+74    StreamsBlockedBidi,
+75    #[assoc(to_u64 = 0x17)]
+76    StreamsBlockedUni,
+77    #[assoc(to_u64 = 0x18)]
+78    NewConnectionId,
+79    #[assoc(to_u64 = 0x19)]
+80    RetireConnectionId,
+81    #[assoc(to_u64 = 0x1a)]
+82    PathChallenge,
+83    #[assoc(to_u64 = 0x1b)]
+84    PathResponse,
+85    #[assoc(to_u64 = 0x1c)]
+86    ConnectionClose,
+87    #[assoc(to_u64 = 0x1d)]
+88    ApplicationClose,
+89    #[assoc(to_u64 = 0x1e)]
+90    HandshakeDone,
+91    // ACK Frequency
+92    #[assoc(to_u64 = 0xaf)]
+93    AckFrequency,
+94    #[assoc(to_u64 = 0x1f)]
+95    ImmediateAck,
+96    // DATAGRAM
+97    #[assoc(to_u64 = _0.to_u64())]
+98    Datagram(DatagramInfo),
+99    // ADDRESS DISCOVERY REPORT
+100    #[assoc(to_u64 = 0x9f81a6)]
+101    ObservedIpv4Addr,
+102    #[assoc(to_u64 = 0x9f81a7)]
+103    ObservedIpv6Addr,
+104    // Multipath
+105    #[assoc(to_u64 = 0x3e)]
+106    PathAck,
+107    #[assoc(to_u64 = 0x3f)]
+108    PathAckEcn,
+109    #[assoc(to_u64 = 0x3e75)]
+110    PathAbandon,
+111    #[assoc(to_u64 = 0x3e76)]
+112    PathStatusBackup,
+113    #[assoc(to_u64 = 0x3e77)]
+114    PathStatusAvailable,
+115    #[assoc(to_u64 = 0x3e78)]
+116    PathNewConnectionId,
+117    #[assoc(to_u64 = 0x3e79)]
+118    PathRetireConnectionId,
+119    #[assoc(to_u64 = 0x3e7a)]
+120    MaxPathId,
+121    #[assoc(to_u64 = 0x3e7b)]
+122    PathsBlocked,
+123    #[assoc(to_u64 = 0x3e7c)]
+124    PathCidsBlocked,
+125    // IROH'S NAT TRAVERSAL
+126    #[assoc(to_u64 = 0x3d7f90)]
+127    AddIpv4Address,
+128    #[assoc(to_u64 = 0x3d7f91)]
+129    AddIpv6Address,
+130    #[assoc(to_u64 = 0x3d7f92)]
+131    ReachOutAtIpv4,
+132    #[assoc(to_u64 = 0x3d7f93)]
+133    ReachOutAtIpv6,
+134    #[assoc(to_u64 = 0x3d7f94)]
+135    RemoveAddress,
+136}
+137
+138/// Encounter a frame ID that was not valid.
+139#[derive(Debug, PartialEq, Eq, thiserror::Error)]
+140#[error("Invalid frame identifier {_0:02x}")]
+141pub struct InvalidFrameId(u64);
+142
+143impl TryFrom<u64> for FrameType {
+144    type Error = InvalidFrameId;
+145    fn try_from(value: u64) -> Result<Self, Self::Error> {
+146        match Self::from_u64(value) {
+147            Some(t) => Ok(t),
+148            None => {
+149                if DatagramInfo::VALUES.contains(&value) {
+150                    return Ok(Self::Datagram(DatagramInfo(value as u8)));
+151                }
+152                if StreamInfo::VALUES.contains(&value) {
+153                    return Ok(Self::Stream(StreamInfo(value as u8)));
+154                }
+155                Err(InvalidFrameId(value))
+156            }
+157        }
+158    }
+159}
+160
+161impl FrameType {
+162    /// The encoded size of this [`FrameType`].
+163    const fn size(&self) -> usize {
+164        VarInt(self.to_u64()).size()
+165    }
+166}
+167
+168impl Decodable for FrameType {
+169    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+170        Self::try_from(buf.get_var()?).map_err(|_| coding::UnexpectedEnd)
+171    }
+172}
+173
+174impl Encodable for FrameType {
+175    fn encode<B: BufMut>(&self, buf: &mut B) {
+176        buf.write_var(self.to_u64());
+177    }
+178}
+179
+180/// Wrapper type for the encodable frames.
+181///
+182/// This includes some "encoder" types instead of the actual read frame, when writing directly to
+183/// a buffer is more efficient than building the Frame itself.
+184#[derive(derive_more::From, enum_assoc::Assoc, derive_more::Display)]
+185#[func(fn encode_inner<B: BufMut>(&self, buf: &mut B) {_0.encode(buf)})]
+186#[func(pub(crate) const fn get_type(&self) -> FrameType {_0.get_type()})]
+187#[cfg_attr(feature = "qlog", func(pub(crate) fn to_qlog(&self) -> qlog::events::quic::QuicFrame {_0.to_qlog()}))]
+188pub(super) enum EncodableFrame<'a> {
+189    PathAck(PathAckEncoder<'a>),
+190    Ack(AckEncoder<'a>),
+191    Close(CloseEncoder<'a>),
+192    PathResponse(PathResponse),
+193    HandshakeDone(HandshakeDone),
+194    ReachOut(ReachOut),
+195    ObservedAddr(ObservedAddr),
+196    Ping(Ping),
+197    ImmediateAck(ImmediateAck),
+198    AckFrequency(AckFrequency),
+199    PathChallenge(PathChallenge),
+200    Crypto(Crypto),
+201    PathAbandon(PathAbandon),
+202    PathStatusAvailable(PathStatusAvailable),
+203    PathStatusBackup(PathStatusBackup),
+204    MaxPathId(MaxPathId),
+205    PathsBlocked(PathsBlocked),
+206    PathCidsBlocked(PathCidsBlocked),
+207    ResetStream(ResetStream),
+208    StopSending(StopSending),
+209    NewConnectionId(NewConnectionId),
+210    RetireConnectionId(RetireConnectionId),
+211    Datagram(Datagram),
+212    NewToken(NewToken),
+213    AddAddress(AddAddress),
+214    RemoveAddress(RemoveAddress),
+215    StreamMeta(StreamMetaEncoder),
+216    MaxData(MaxData),
+217    MaxStreamData(MaxStreamData),
+218    MaxStreams(MaxStreams),
+219    StreamsBlocked(StreamsBlocked),
+220}
+221
+222impl<'a> EncodableFrame<'a> {
+223    /// Whether this is an ACK-eliciting frame.
+224    pub(crate) fn is_ack_eliciting(&self) -> bool {
+225        match self {
+226            EncodableFrame::PathAck(_) | EncodableFrame::Ack(_) | EncodableFrame::Close(_) => false,
+227            EncodableFrame::PathResponse(_)
+228            | EncodableFrame::HandshakeDone(_)
+229            | EncodableFrame::ReachOut(_)
+230            | EncodableFrame::ObservedAddr(_)
+231            | EncodableFrame::Ping(_)
+232            | EncodableFrame::ImmediateAck(_)
+233            | EncodableFrame::AckFrequency(_)
+234            | EncodableFrame::PathChallenge(_)
+235            | EncodableFrame::Crypto(_)
+236            | EncodableFrame::PathAbandon(_)
+237            | EncodableFrame::PathStatusAvailable(_)
+238            | EncodableFrame::PathStatusBackup(_)
+239            | EncodableFrame::MaxPathId(_)
+240            | EncodableFrame::PathsBlocked(_)
+241            | EncodableFrame::PathCidsBlocked(_)
+242            | EncodableFrame::ResetStream(_)
+243            | EncodableFrame::StopSending(_)
+244            | EncodableFrame::NewConnectionId(_)
+245            | EncodableFrame::RetireConnectionId(_)
+246            | EncodableFrame::Datagram(_)
+247            | EncodableFrame::NewToken(_)
+248            | EncodableFrame::AddAddress(_)
+249            | EncodableFrame::RemoveAddress(_)
+250            | EncodableFrame::StreamMeta(_)
+251            | EncodableFrame::MaxData(_)
+252            | EncodableFrame::MaxStreamData(_)
+253            | EncodableFrame::MaxStreams(_)
+254            | EncodableFrame::StreamsBlocked(_) => true,
+255        }
+256    }
+257}
+258
+259impl<'a> Encodable for EncodableFrame<'a> {
+260    fn encode<B: BufMut>(&self, buf: &mut B) {
+261        self.encode_inner(buf)
+262    }
+263}
+264
+265pub(crate) trait FrameStruct {
+266    /// Smallest number of bytes this type of frame is guaranteed to fit within.
+267    const SIZE_BOUND: usize;
+268}
+269
+270/// The type used to refer to [`FrameType`]s in closing and transport errors.
+271#[derive(Copy, Clone, Eq, PartialEq, derive_more::Debug, derive_more::Display)]
+272pub enum MaybeFrame {
+273    /// Not attributed to any particular [`FrameType`].
+274    None,
+275    /// Attributed to some frame type this implementation does not recognize.
+276    #[display("UNKNOWN({:02x})", _0)]
+277    #[debug("Unknown({:02x})", _0)]
+278    Unknown(u64),
+279    /// Attributed to a specific [`FrameType`], never [`FrameType::Padding`].
+280    Known(FrameType),
+281}
+282
+283impl MaybeFrame {
+284    /// Encoded size of this [`MaybeFrame`].
+285    const fn size(&self) -> usize {
+286        match self {
+287            Self::None => VarInt(0).size(),
+288            Self::Unknown(other) => VarInt(*other).size(),
+289            Self::Known(frame_type) => frame_type.size(),
+290        }
+291    }
+292}
+293
+294impl Decodable for MaybeFrame {
+295    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+296        match FrameType::try_from(buf.get_var()?) {
+297            Ok(FrameType::Padding) => Ok(Self::None),
+298            Ok(other_frame) => Ok(Self::Known(other_frame)),
+299            Err(InvalidFrameId(other)) => Ok(Self::Unknown(other)),
+300        }
+301    }
+302}
+303
+304impl Encodable for MaybeFrame {
+305    fn encode<B: BufMut>(&self, buf: &mut B) {
+306        match self {
+307            Self::None => buf.write_var(0u64),
+308            Self::Unknown(frame_id) => buf.write_var(*frame_id),
+309            Self::Known(frame_type) => buf.write(*frame_type),
+310        }
+311    }
+312}
+313
+314#[cfg(test)]
+315impl proptest::arbitrary::Arbitrary for MaybeFrame {
+316    type Parameters = ();
+317    type Strategy = proptest::strategy::BoxedStrategy<Self>;
+318
+319    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
+320        use proptest::prelude::*;
+321        prop_oneof![
+322            Just(Self::None),
+323            any::<VarInt>().prop_map(|v| Self::Unknown(v.0)),
+324            // do not generate padding frames here, since they are not allowed in MaybeFrame::Known
+325            any::<FrameType>()
+326                .prop_filter("not Padding", |ft| *ft != FrameType::Padding)
+327                .prop_map(MaybeFrame::Known),
+328        ]
+329        .boxed()
+330    }
+331}
+332
+333#[derive(derive_more::Display)]
+334#[cfg_attr(test, derive(Arbitrary, Debug, Clone, PartialEq, Eq))]
+335#[display("HANDSHAKE_DONE")]
+336pub(crate) struct HandshakeDone;
+337
+338impl HandshakeDone {
+339    const fn get_type(&self) -> FrameType {
+340        FrameType::HandshakeDone
+341    }
+342}
+343
+344impl Encodable for HandshakeDone {
+345    fn encode<B: BufMut>(&self, buf: &mut B) {
+346        FrameType::HandshakeDone.encode(buf);
+347    }
+348}
+349
+350#[derive(derive_more::Display)]
+351#[cfg_attr(test, derive(Arbitrary, Debug, Clone, PartialEq, Eq))]
+352#[display("PING")]
+353pub(crate) struct Ping;
+354
+355impl Ping {
+356    const fn get_type(&self) -> FrameType {
+357        FrameType::Ping
+358    }
+359}
+360
+361impl Encodable for Ping {
+362    fn encode<B: BufMut>(&self, buf: &mut B) {
+363        FrameType::Ping.encode(buf);
+364    }
+365}
+366
+367#[derive(derive_more::Display)]
+368#[cfg_attr(test, derive(Arbitrary, Debug, Clone, PartialEq, Eq))]
+369#[display("IMMEDIATE_ACK")]
+370pub(crate) struct ImmediateAck;
+371
+372impl ImmediateAck {
+373    const fn get_type(&self) -> FrameType {
+374        FrameType::ImmediateAck
+375    }
+376}
+377
+378impl Encodable for ImmediateAck {
+379    fn encode<B: BufMut>(&self, buf: &mut B) {
+380        FrameType::ImmediateAck.encode(buf);
+381    }
+382}
+383
+384#[allow(missing_docs)]
+385#[derive(Debug, Copy, Clone, Eq, PartialEq, derive_more::Display)]
+386#[display("STREAM")]
+387#[cfg_attr(test, derive(Arbitrary))]
+388pub struct StreamInfo(#[cfg_attr(test, strategy(0x08u8..=0x0f))] u8);
+389
+390impl StreamInfo {
+391    const VALUES: RangeInclusive<u64> = RangeInclusive::new(0x08, 0x0f);
+392    fn fin(self) -> bool {
+393        self.0 & 0x01 != 0
+394    }
+395    fn len(self) -> bool {
+396        self.0 & 0x02 != 0
+397    }
+398    fn off(self) -> bool {
+399        self.0 & 0x04 != 0
+400    }
+401
+402    const fn to_u64(self) -> u64 {
+403        self.0 as u64
+404    }
+405}
+406
+407#[allow(missing_docs)]
+408#[derive(Debug, Copy, Clone, Eq, PartialEq, derive_more::Display)]
+409#[display("DATAGRAM")]
+410#[cfg_attr(test, derive(Arbitrary))]
+411pub struct DatagramInfo(#[cfg_attr(test, strategy(0x30u8..=0x31))] u8);
+412
+413impl DatagramInfo {
+414    const VALUES: RangeInclusive<u64> = RangeInclusive::new(0x30, 0x31);
+415
+416    fn len(self) -> bool {
+417        self.0 & 0x01 != 0
+418    }
+419
+420    const fn to_u64(self) -> u64 {
+421        self.0 as u64
+422    }
+423}
+424
+425#[derive(Debug, derive_more::Display)]
+426#[cfg_attr(test, derive(Arbitrary))]
+427pub(crate) enum Frame {
+428    #[display("PADDING")]
+429    Padding,
+430    #[display("PING")]
+431    Ping,
+432    Ack(Ack),
+433    PathAck(PathAck),
+434    ResetStream(ResetStream),
+435    StopSending(StopSending),
+436    Crypto(Crypto),
+437    NewToken(NewToken),
+438    Stream(Stream),
+439    MaxData(MaxData),
+440    MaxStreamData(MaxStreamData),
+441    MaxStreams(MaxStreams),
+442    DataBlocked(DataBlocked),
+443    StreamDataBlocked(StreamDataBlocked),
+444    StreamsBlocked(StreamsBlocked),
+445    NewConnectionId(NewConnectionId),
+446    RetireConnectionId(RetireConnectionId),
+447    PathChallenge(PathChallenge),
+448    PathResponse(PathResponse),
+449    Close(Close),
+450    Datagram(Datagram),
+451    AckFrequency(AckFrequency),
+452    #[display("IMMEDIATE_ACK")]
+453    ImmediateAck,
+454    #[display("HANDSHAKE_DONE")]
+455    HandshakeDone,
+456    ObservedAddr(ObservedAddr),
+457    PathAbandon(PathAbandon),
+458    PathStatusAvailable(PathStatusAvailable),
+459    PathStatusBackup(PathStatusBackup),
+460    MaxPathId(MaxPathId),
+461    PathsBlocked(PathsBlocked),
+462    PathCidsBlocked(PathCidsBlocked),
+463    AddAddress(AddAddress),
+464    ReachOut(ReachOut),
+465    RemoveAddress(RemoveAddress),
+466}
+467
+468impl Frame {
+469    pub(crate) fn ty(&self) -> FrameType {
+470        use Frame::*;
+471        match &self {
+472            Padding => FrameType::Padding,
+473            ResetStream(_) => FrameType::ResetStream,
+474            Close(self::Close::Connection(_)) => FrameType::ConnectionClose,
+475            Close(self::Close::Application(_)) => FrameType::ConnectionClose,
+476            MaxData(_) => FrameType::MaxData,
+477            MaxStreamData(_) => FrameType::MaxStreamData,
+478            MaxStreams(max_streams) => max_streams.get_type(),
+479            Ping => FrameType::Ping,
+480            DataBlocked(_) => FrameType::DataBlocked,
+481            StreamDataBlocked(sdb) => sdb.get_type(),
+482            StreamsBlocked(sb) => sb.get_type(),
+483            StopSending { .. } => FrameType::StopSending,
+484            RetireConnectionId(retire_frame) => retire_frame.get_type(),
+485            Ack(ack) => ack.get_type(),
+486            PathAck(path_ack) => path_ack.get_type(),
+487            Stream(x) => {
+488                let mut ty = *StreamInfo::VALUES.start() as u8;
+489                if x.fin {
+490                    ty |= 0x01;
+491                }
+492                if x.offset != 0 {
+493                    ty |= 0x04;
+494                }
+495                // TODO(@divma): move all this to getframetype for Stream
+496                FrameType::Stream(StreamInfo(ty))
+497            }
+498            PathChallenge(_) => FrameType::PathChallenge,
+499            PathResponse(_) => FrameType::PathResponse,
+500            NewConnectionId(cid) => cid.get_type(),
+501            Crypto(_) => FrameType::Crypto,
+502            NewToken(_) => FrameType::NewToken,
+503            Datagram(_) => FrameType::Datagram(DatagramInfo(*DatagramInfo::VALUES.start() as u8)),
+504            AckFrequency(_) => FrameType::AckFrequency,
+505            ImmediateAck => FrameType::ImmediateAck,
+506            HandshakeDone => FrameType::HandshakeDone,
+507            ObservedAddr(observed) => observed.get_type(),
+508            PathAbandon(_) => FrameType::PathAbandon,
+509            PathStatusAvailable(_) => FrameType::PathStatusAvailable,
+510            PathStatusBackup(_) => FrameType::PathStatusBackup,
+511            MaxPathId(_) => FrameType::MaxPathId,
+512            PathsBlocked(_) => FrameType::PathsBlocked,
+513            PathCidsBlocked(_) => FrameType::PathCidsBlocked,
+514            AddAddress(frame) => frame.get_type(),
+515            ReachOut(frame) => frame.get_type(),
+516            RemoveAddress(_) => self::RemoveAddress::TYPE,
+517        }
+518    }
+519
+520    pub(crate) fn is_ack_eliciting(&self) -> bool {
+521        !matches!(
+522            *self,
+523            Self::Ack(_) | Self::PathAck(_) | Self::Padding | Self::Close(_)
+524        )
+525    }
+526
+527    /// Returns `true` if this frame MUST be sent in 1-RTT space
+528    pub(crate) fn is_1rtt(&self) -> bool {
+529        // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-4-1:
+530        // > All frames defined in this document MUST only be sent in 1-RTT packets.
+531        // > If an endpoint receives a multipath-specific frame in a different packet type, it MUST close the
+532        // > connection with an error of type PROTOCOL_VIOLATION.
+533
+534        self.is_multipath_frame() || self.is_qad_frame()
+535    }
+536
+537    fn is_qad_frame(&self) -> bool {
+538        matches!(*self, Self::ObservedAddr(_))
+539    }
+540
+541    fn is_multipath_frame(&self) -> bool {
+542        matches!(
+543            *self,
+544            Self::PathAck(_)
+545                | Self::PathAbandon(_)
+546                | Self::PathStatusBackup(_)
+547                | Self::PathStatusAvailable(_)
+548                | Self::MaxPathId(_)
+549                | Self::PathsBlocked(_)
+550                | Self::PathCidsBlocked(_)
+551                | Self::NewConnectionId(NewConnectionId {
+552                    path_id: Some(_),
+553                    ..
+554                })
+555                | Self::RetireConnectionId(RetireConnectionId {
+556                    path_id: Some(_),
+557                    ..
+558                })
+559        )
+560    }
+561}
+562
+563#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display)]
+564#[cfg_attr(test, derive(Arbitrary))]
+565#[display("PATH_CHALLENGE({_0:08x})")]
+566pub(crate) struct PathChallenge(pub(crate) u64);
+567
+568impl PathChallenge {
+569    pub(crate) const SIZE_BOUND: usize = 9;
+570
+571    const fn get_type(&self) -> FrameType {
+572        FrameType::PathChallenge
+573    }
+574}
+575
+576impl Decodable for PathChallenge {
+577    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+578        Ok(Self(buf.get()?))
+579    }
+580}
+581
+582impl Encodable for PathChallenge {
+583    fn encode<B: BufMut>(&self, buf: &mut B) {
+584        buf.write(FrameType::PathChallenge);
+585        buf.write(self.0);
+586    }
+587}
+588
+589#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Display)]
+590#[cfg_attr(test, derive(Arbitrary))]
+591#[display("PATH_RESPONSE({_0:08x})")]
+592pub(crate) struct PathResponse(pub(crate) u64);
+593
+594impl PathResponse {
+595    pub(crate) const SIZE_BOUND: usize = 9;
+596
+597    const fn get_type(&self) -> FrameType {
+598        FrameType::PathResponse
+599    }
+600}
+601
+602impl Decodable for PathResponse {
+603    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+604        Ok(Self(buf.get()?))
+605    }
+606}
+607
+608impl Encodable for PathResponse {
+609    fn encode<B: BufMut>(&self, buf: &mut B) {
+610        buf.write(FrameType::PathResponse);
+611        buf.write(self.0);
+612    }
+613}
+614
+615#[derive(Debug, Clone, Copy, derive_more::Display)]
+616#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+617#[display("DATA_BLOCKED offset: {_0}")]
+618pub(crate) struct DataBlocked(#[cfg_attr(test, strategy(varint_u64()))] pub(crate) u64);
+619
+620impl Encodable for DataBlocked {
+621    fn encode<B: BufMut>(&self, buf: &mut B) {
+622        buf.write(FrameType::DataBlocked);
+623        buf.write_var(self.0);
+624    }
+625}
+626
+627#[derive(Debug, Clone, Copy, derive_more::Display)]
+628#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+629#[display("STREAM_DATA_BLOCKED id: {id} offset: {offset}")]
+630pub(crate) struct StreamDataBlocked {
+631    pub(crate) id: StreamId,
+632    #[cfg_attr(test, strategy(varint_u64()))]
+633    pub(crate) offset: u64,
+634}
+635
+636impl StreamDataBlocked {
+637    const fn get_type(&self) -> FrameType {
+638        FrameType::StreamDataBlocked
+639    }
+640}
+641
+642impl Encodable for StreamDataBlocked {
+643    fn encode<B: BufMut>(&self, buf: &mut B) {
+644        buf.write(FrameType::StreamDataBlocked);
+645        buf.write(self.id);
+646        buf.write_var(self.offset);
+647    }
+648}
+649
+650#[derive(Debug, Clone, Copy, derive_more::Display)]
+651#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+652#[display("STREAMS_BLOCKED dir: {:?} limit: {limit}", dir)]
+653pub(crate) struct StreamsBlocked {
+654    pub(crate) dir: Dir,
+655    #[cfg_attr(test, strategy(varint_u64()))]
+656    pub(crate) limit: u64,
+657}
+658
+659impl StreamsBlocked {
+660    const fn get_type(&self) -> FrameType {
+661        match self.dir {
+662            Dir::Bi => FrameType::StreamsBlockedBidi,
+663            Dir::Uni => FrameType::StreamsBlockedUni,
+664        }
+665    }
+666}
+667
+668impl Encodable for StreamsBlocked {
+669    fn encode<B: BufMut>(&self, buf: &mut B) {
+670        buf.write(self.get_type());
+671        buf.write_var(self.limit);
+672    }
+673}
+674
+675#[derive(Debug, Clone, Copy, derive_more::Display)]
+676#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+677#[display("MAX_DATA({_0})")]
+678pub(crate) struct MaxData(pub(crate) VarInt);
+679
+680impl MaxData {
+681    const fn get_type(&self) -> FrameType {
+682        FrameType::MaxData
+683    }
+684}
+685
+686impl Decodable for MaxData {
+687    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+688        Ok(Self(buf.get()?))
+689    }
+690}
+691
+692impl Encodable for MaxData {
+693    fn encode<B: BufMut>(&self, buf: &mut B) {
+694        buf.write(FrameType::MaxData);
+695        buf.write(self.0);
+696    }
+697}
+698
+699#[derive(Debug, Clone, Copy, derive_more::Display)]
+700#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+701#[display("MAX_STREAM_DATA id: {id} max: {offset}")]
+702pub(crate) struct MaxStreamData {
+703    pub(crate) id: StreamId,
+704    #[cfg_attr(test, strategy(varint_u64()))]
+705    pub(crate) offset: u64,
+706}
+707
+708impl MaxStreamData {
+709    const fn get_type(&self) -> FrameType {
+710        FrameType::MaxStreamData
+711    }
+712}
+713
+714impl Decodable for MaxStreamData {
+715    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+716        Ok(Self {
+717            id: buf.get()?,
+718            offset: buf.get_var()?,
+719        })
+720    }
+721}
+722
+723impl Encodable for MaxStreamData {
+724    fn encode<B: BufMut>(&self, buf: &mut B) {
+725        buf.write(FrameType::MaxStreamData);
+726        buf.write(self.id);
+727        buf.write_var(self.offset);
+728    }
+729}
+730
+731#[derive(Debug, Clone, Copy, derive_more::Display)]
+732#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+733#[display("{} count: {count}", self.get_type())]
+734pub(crate) struct MaxStreams {
+735    pub(crate) dir: Dir,
+736    #[cfg_attr(test, strategy(varint_u64()))]
+737    pub(crate) count: u64,
+738}
+739
+740impl MaxStreams {
+741    const fn get_type(&self) -> FrameType {
+742        match self.dir {
+743            Dir::Bi => FrameType::MaxStreamsBidi,
+744            Dir::Uni => FrameType::MaxStreamsUni,
+745        }
+746    }
+747}
+748
+749impl Encodable for MaxStreams {
+750    fn encode<B: BufMut>(&self, buf: &mut B) {
+751        buf.write(self.get_type());
+752        buf.write_var(self.count);
+753    }
+754}
+755
+756#[derive(Debug, PartialEq, Eq, derive_more::Display)]
+757#[cfg_attr(test, derive(Arbitrary))]
+758#[display("{} {} seq: {sequence}", self.get_type(), DisplayOption::new("path_id", path_id.as_ref()))]
+759pub(crate) struct RetireConnectionId {
+760    pub(crate) path_id: Option<PathId>,
+761    #[cfg_attr(test, strategy(varint_u64()))]
+762    pub(crate) sequence: u64,
+763}
+764
+765impl RetireConnectionId {
+766    /// Maximum size of this frame when the frame type is [`FrameType::RetireConnectionId`]
+767    pub(crate) const SIZE_BOUND: usize = {
+768        let type_len = FrameType::RetireConnectionId.size();
+769        let seq_max_len = 8usize;
+770        type_len + seq_max_len
+771    };
+772
+773    /// Maximum size of this frame when the frame type is [`FrameType::PathRetireConnectionId`]
+774    pub(crate) const SIZE_BOUND_MULTIPATH: usize = {
+775        let type_len = FrameType::PathRetireConnectionId.size();
+776        let path_id_len = VarInt::from_u32(u32::MAX).size();
+777        let seq_max_len = 8usize;
+778        type_len + path_id_len + seq_max_len
+779    };
+780
+781    /// Decode [`Self`] from the buffer, provided that the frame type has been verified (either
+782    /// [`FrameType::PathRetireConnectionId`], or [`FrameType::RetireConnectionId`])
+783    pub(crate) fn decode<R: Buf>(bytes: &mut R, read_path: bool) -> coding::Result<Self> {
+784        Ok(Self {
+785            path_id: if read_path { Some(bytes.get()?) } else { None },
+786            sequence: bytes.get_var()?,
+787        })
+788    }
+789
+790    /// Get the [`FrameType`] for this [`RetireConnectionId`]
+791    const fn get_type(&self) -> FrameType {
+792        if self.path_id.is_some() {
+793            FrameType::PathRetireConnectionId
+794        } else {
+795            FrameType::RetireConnectionId
+796        }
+797    }
+798
+799    /// Returns the maximum encoded size on the wire
+800    ///
+801    /// `path_retire_cid` determines whether this frame is a multipath frame. This is a rough upper
+802    /// estimate, does not squeeze every last byte out.
+803    pub(crate) const fn size_bound(path_retire_cid: bool) -> usize {
+804        match path_retire_cid {
+805            true => Self::SIZE_BOUND_MULTIPATH,
+806            false => Self::SIZE_BOUND,
+807        }
+808    }
+809}
+810
+811impl Encodable for RetireConnectionId {
+812    fn encode<W: BufMut>(&self, buf: &mut W) {
+813        buf.write(self.get_type());
+814        if let Some(id) = self.path_id {
+815            buf.write(id);
+816        }
+817        buf.write_var(self.sequence);
+818    }
+819}
+820
+821#[cfg_attr(test, derive(Arbitrary))]
+822#[derive(Clone, Debug, derive_more::Display)]
+823#[display("CONNECTION_CLOSE error_code={} reason='{}'", self.error_code(), self.reason())]
+824pub(crate) enum Close {
+825    Connection(ConnectionClose),
+826    Application(ApplicationClose),
+827}
+828
+829impl Close {
+830    pub(crate) fn encoder(&self, max_len: usize) -> CloseEncoder<'_> {
+831        CloseEncoder {
+832            close: self,
+833            max_len,
+834        }
+835    }
+836
+837    pub(crate) fn is_transport_layer(&self) -> bool {
+838        matches!(*self, Self::Connection(_))
+839    }
+840
+841    fn error_code(&self) -> u64 {
+842        match self {
+843            Self::Connection(ConnectionClose { error_code, .. }) => u64::from(*error_code),
+844            Self::Application(ApplicationClose { error_code, .. }) => u64::from(*error_code),
+845        }
+846    }
+847
+848    fn reason(&self) -> Cow<'_, str> {
+849        match self {
+850            Self::Connection(ConnectionClose { reason, .. }) => String::from_utf8_lossy(reason),
+851            Self::Application(ApplicationClose { reason, .. }) => String::from_utf8_lossy(reason),
+852        }
+853    }
+854}
+855
+856#[derive(derive_more::Display)]
+857#[display("{close}")]
+858pub(crate) struct CloseEncoder<'a> {
+859    pub(crate) close: &'a Close,
+860    max_len: usize,
+861}
+862
+863impl<'a> CloseEncoder<'a> {
+864    const fn get_type(&self) -> FrameType {
+865        match self.close {
+866            Close::Connection(_) => FrameType::ConnectionClose,
+867            Close::Application(_) => FrameType::ApplicationClose,
+868        }
+869    }
+870}
+871
+872impl<'a> Encodable for CloseEncoder<'a> {
+873    fn encode<W: BufMut>(&self, out: &mut W) {
+874        match self.close {
+875            Close::Connection(x) => x.encode(out, self.max_len),
+876            Close::Application(x) => x.encode(out, self.max_len),
+877        }
+878    }
+879}
+880
+881impl From<TransportError> for Close {
+882    fn from(x: TransportError) -> Self {
+883        Self::Connection(x.into())
+884    }
+885}
+886impl From<ConnectionClose> for Close {
+887    fn from(x: ConnectionClose) -> Self {
+888        Self::Connection(x)
+889    }
+890}
+891impl From<ApplicationClose> for Close {
+892    fn from(x: ApplicationClose) -> Self {
+893        Self::Application(x)
+894    }
+895}
+896
+897/// Reason given by the transport for closing the connection
+898#[derive(Debug, Clone, PartialEq, Eq)]
+899#[cfg_attr(test, derive(Arbitrary))]
+900pub struct ConnectionClose {
+901    /// Class of error as encoded in the specification
+902    pub error_code: TransportErrorCode,
+903    /// Type of frame that caused the close
+904    pub frame_type: MaybeFrame,
+905    /// Human-readable reason for the close
+906    #[cfg_attr(test, strategy(proptest::collection::vec(any::<u8>(), 0..64).prop_map(Bytes::from)))]
+907    pub reason: Bytes,
+908}
+909
+910impl fmt::Display for ConnectionClose {
+911    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+912        self.error_code.fmt(f)?;
+913        if !self.reason.as_ref().is_empty() {
+914            f.write_str(": ")?;
+915            f.write_str(&String::from_utf8_lossy(&self.reason))?;
+916        }
+917        Ok(())
+918    }
+919}
+920
+921impl From<TransportError> for ConnectionClose {
+922    fn from(x: TransportError) -> Self {
+923        Self {
+924            error_code: x.code,
+925            frame_type: x.frame,
+926            reason: x.reason.into(),
+927        }
+928    }
+929}
+930
+931impl FrameStruct for ConnectionClose {
+932    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+933}
+934
+935impl ConnectionClose {
+936    pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) {
+937        out.write(FrameType::ConnectionClose); // 1 byte
+938        out.write(self.error_code); // <= 8 bytes
+939        out.write(self.frame_type); // <= 8 bytes
+940        let max_len = max_len
+941            - 3
+942            - self.frame_type.size()
+943            - VarInt::from_u64(self.reason.len() as u64).unwrap().size();
+944        let actual_len = self.reason.len().min(max_len);
+945        out.write_var(actual_len as u64); // <= 8 bytes
+946        out.put_slice(&self.reason[0..actual_len]); // whatever's left
+947    }
+948}
+949
+950/// Reason given by an application for closing the connection
+951#[derive(Debug, Clone, PartialEq, Eq)]
+952#[cfg_attr(test, derive(Arbitrary))]
+953pub struct ApplicationClose {
+954    /// Application-specific reason code
+955    pub error_code: VarInt,
+956    /// Human-readable reason for the close
+957    #[cfg_attr(test, strategy(proptest::collection::vec(any::<u8>(), 0..64).prop_map(Bytes::from)))]
+958    pub reason: Bytes,
+959}
+960
+961impl fmt::Display for ApplicationClose {
+962    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+963        if !self.reason.as_ref().is_empty() {
+964            f.write_str(&String::from_utf8_lossy(&self.reason))?;
+965            f.write_str(" (code ")?;
+966            self.error_code.fmt(f)?;
+967            f.write_str(")")?;
+968        } else {
+969            self.error_code.fmt(f)?;
+970        }
+971        Ok(())
+972    }
+973}
+974
+975impl FrameStruct for ApplicationClose {
+976    const SIZE_BOUND: usize = 1 + 8 + 8;
+977}
+978
+979impl ApplicationClose {
+980    pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) {
+981        out.write(FrameType::ApplicationClose); // 1 byte
+982        out.write(self.error_code); // <= 8 bytes
+983        let max_len = max_len - 3 - VarInt::from_u64(self.reason.len() as u64).unwrap().size();
+984        let actual_len = self.reason.len().min(max_len);
+985        out.write_var(actual_len as u64); // <= 8 bytes
+986        out.put_slice(&self.reason[0..actual_len]); // whatever's left
+987    }
+988}
+989
+990#[derive(Debug, Clone, Eq, PartialEq, derive_more::Display)]
+991#[display("{} path_id: {path_id} ranges: {ranges:?} delay: {delay}µs", self.get_type())]
+992pub(crate) struct PathAck {
+993    pub path_id: PathId,
+994    pub largest: u64,
+995    pub delay: u64,
+996    pub ranges: ArrayRangeSet,
+997    pub ecn: Option<EcnCounts>,
+998}
+999
+1000#[cfg(test)]
+1001impl proptest::arbitrary::Arbitrary for PathAck {
+1002    type Parameters = ();
+1003    type Strategy = proptest::strategy::BoxedStrategy<Self>;
+1004
+1005    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
+1006        use proptest::prelude::*;
+1007        (
+1008            any::<PathId>(),
+1009            varint_u64(),
+1010            any::<ArrayRangeSet>(),
+1011            any::<Option<EcnCounts>>(),
+1012        )
+1013            .prop_map(|(path_id, delay, ranges, ecn)| Self {
+1014                path_id,
+1015                largest: ranges.max().expect("ranges must be non empty"),
+1016                delay,
+1017                ranges,
+1018                ecn,
+1019            })
+1020            .boxed()
+1021    }
+1022}
+1023
+1024impl PathAck {
+1025    pub(crate) fn into_ack(self) -> (Ack, PathId) {
+1026        let ack = Ack {
+1027            largest: self.largest,
+1028            delay: self.delay,
+1029            ranges: self.ranges,
+1030            ecn: self.ecn,
+1031        };
+1032
+1033        (ack, self.path_id)
+1034    }
+1035
+1036    fn get_type(&self) -> FrameType {
+1037        if self.ecn.is_some() {
+1038            FrameType::PathAckEcn
+1039        } else {
+1040            FrameType::PathAck
+1041        }
+1042    }
+1043
+1044    pub(crate) fn encoder<'a>(
+1045        path_id: PathId,
+1046        delay: u64,
+1047        ranges: &'a ArrayRangeSet,
+1048        ecn: Option<&'a EcnCounts>,
+1049    ) -> PathAckEncoder<'a> {
+1050        PathAckEncoder {
+1051            path_id,
+1052            delay,
+1053            ranges,
+1054            ecn,
+1055        }
+1056    }
+1057}
+1058
+1059#[derive(derive_more::Display)]
+1060#[display("{} path_id: {path_id} ranges: {ranges:?} delay: {delay}µs", self.get_type())]
+1061pub(crate) struct PathAckEncoder<'a> {
+1062    pub(super) path_id: PathId,
+1063    pub(super) delay: u64,
+1064    pub(super) ranges: &'a ArrayRangeSet,
+1065    pub(super) ecn: Option<&'a EcnCounts>,
+1066}
+1067
+1068impl<'a> PathAckEncoder<'a> {
+1069    const fn get_type(&self) -> FrameType {
+1070        match self.ecn.is_some() {
+1071            true => FrameType::PathAckEcn,
+1072            false => FrameType::PathAck,
+1073        }
+1074    }
+1075}
+1076
+1077impl<'a> Encodable for PathAckEncoder<'a> {
+1078    /// Encode [`Self`] into the given buffer
+1079    ///
+1080    /// The [`FrameType`] will be either [`FrameType::PathAckEcn`] or [`FrameType::PathAck`]
+1081    /// depending on whether [`EcnCounts`] are provided.
+1082    ///
+1083    /// PANICS: if `ranges` is empty.
+1084    fn encode<W: BufMut>(&self, buf: &mut W) {
+1085        let PathAckEncoder {
+1086            path_id,
+1087            delay,
+1088            ranges,
+1089            ecn,
+1090        } = self;
+1091        let mut rest = ranges.iter().rev();
+1092        let first = rest
+1093            .next()
+1094            .expect("Caller has verified ranges is non empty");
+1095        let largest = first.end - 1;
+1096        let first_size = first.end - first.start;
+1097        let kind = match ecn.is_some() {
+1098            true => FrameType::PathAckEcn,
+1099            false => FrameType::PathAck,
+1100        };
+1101        buf.write(kind);
+1102        buf.write(*path_id);
+1103        buf.write_var(largest);
+1104        buf.write_var(*delay);
+1105        buf.write_var(ranges.len() as u64 - 1);
+1106        buf.write_var(first_size - 1);
+1107        let mut prev = first.start;
+1108        for block in rest {
+1109            let size = block.end - block.start;
+1110            buf.write_var(prev - block.end - 1);
+1111            buf.write_var(size - 1);
+1112            prev = block.start;
+1113        }
+1114        if let Some(x) = ecn {
+1115            x.encode(buf)
+1116        }
+1117    }
+1118}
+1119
+1120#[derive(Debug, Clone, Eq, PartialEq, derive_more::Display)]
+1121#[display("{} ranges: {ranges:?} delay: {delay}µs largest: {largest}", self.get_type())]
+1122pub(crate) struct Ack {
+1123    pub largest: u64,
+1124    pub delay: u64,
+1125    pub ranges: ArrayRangeSet,
+1126    pub ecn: Option<EcnCounts>,
+1127}
+1128
+1129#[cfg(test)]
+1130impl proptest::arbitrary::Arbitrary for Ack {
+1131    type Parameters = ();
+1132    type Strategy = proptest::strategy::BoxedStrategy<Self>;
+1133
+1134    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
+1135        use proptest::prelude::*;
+1136        (
+1137            varint_u64(),
+1138            any::<ArrayRangeSet>(),
+1139            any::<Option<EcnCounts>>(),
+1140        )
+1141            .prop_map(|(delay, ranges, ecn)| Self {
+1142                largest: ranges.max().expect("ranges must be non empty"),
+1143                delay,
+1144                ranges,
+1145                ecn,
+1146            })
+1147            .boxed()
+1148    }
+1149}
+1150
+1151impl Ack {
+1152    pub(crate) fn encoder<'a>(
+1153        delay: u64,
+1154        ranges: &'a ArrayRangeSet,
+1155        ecn: Option<&'a EcnCounts>,
+1156    ) -> AckEncoder<'a> {
+1157        AckEncoder { delay, ranges, ecn }
+1158    }
+1159
+1160    pub(crate) fn iter(&self) -> impl DoubleEndedIterator<Item = Range<u64>> + '_ {
+1161        self.ranges.iter()
+1162    }
+1163
+1164    pub(crate) const fn get_type(&self) -> FrameType {
+1165        if self.ecn.is_some() {
+1166            FrameType::AckEcn
+1167        } else {
+1168            FrameType::Ack
+1169        }
+1170    }
+1171}
+1172
+1173#[derive(derive_more::Display)]
+1174#[display("{} ranges: {ranges:?} delay: {delay}µs", self.get_type())]
+1175pub(crate) struct AckEncoder<'a> {
+1176    pub(crate) delay: u64,
+1177    pub(crate) ranges: &'a ArrayRangeSet,
+1178    pub(crate) ecn: Option<&'a EcnCounts>,
+1179}
+1180
+1181impl<'a> AckEncoder<'a> {
+1182    const fn get_type(&self) -> FrameType {
+1183        match self.ecn.is_some() {
+1184            true => FrameType::AckEcn,
+1185            false => FrameType::Ack,
+1186        }
+1187    }
+1188}
+1189
+1190impl<'a> Encodable for AckEncoder<'a> {
+1191    fn encode<W: BufMut>(&self, buf: &mut W) {
+1192        let AckEncoder { delay, ranges, ecn } = self;
+1193        let mut rest = ranges.iter().rev();
+1194        let first = rest.next().unwrap();
+1195        let largest = first.end - 1;
+1196        let first_size = first.end - first.start;
+1197        let kind = match ecn.is_some() {
+1198            true => FrameType::AckEcn,
+1199            false => FrameType::Ack,
+1200        };
+1201        buf.write(kind);
+1202        buf.write_var(largest);
+1203        buf.write_var(*delay);
+1204        buf.write_var(ranges.len() as u64 - 1);
+1205        buf.write_var(first_size - 1);
+1206        let mut prev = first.start;
+1207        for block in rest {
+1208            let size = block.end - block.start;
+1209            buf.write_var(prev - block.end - 1);
+1210            buf.write_var(size - 1);
+1211            prev = block.start;
+1212        }
+1213        if let Some(x) = ecn {
+1214            x.encode(buf)
+1215        }
+1216    }
+1217}
+1218
+1219#[cfg_attr(test, derive(Arbitrary))]
+1220#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+1221pub(crate) struct EcnCounts {
+1222    #[cfg_attr(test, strategy(varint_u64()))]
+1223    pub ect0: u64,
+1224    #[cfg_attr(test, strategy(varint_u64()))]
+1225    pub ect1: u64,
+1226    #[cfg_attr(test, strategy(varint_u64()))]
+1227    pub ce: u64,
+1228}
+1229
+1230impl std::ops::AddAssign<EcnCodepoint> for EcnCounts {
+1231    fn add_assign(&mut self, rhs: EcnCodepoint) {
+1232        match rhs {
+1233            EcnCodepoint::Ect0 => {
+1234                self.ect0 += 1;
+1235            }
+1236            EcnCodepoint::Ect1 => {
+1237                self.ect1 += 1;
+1238            }
+1239            EcnCodepoint::Ce => {
+1240                self.ce += 1;
+1241            }
+1242        }
+1243    }
+1244}
+1245
+1246impl EcnCounts {
+1247    pub(crate) const ZERO: Self = Self {
+1248        ect0: 0,
+1249        ect1: 0,
+1250        ce: 0,
+1251    };
+1252}
+1253
+1254impl Encodable for EcnCounts {
+1255    fn encode<W: BufMut>(&self, out: &mut W) {
+1256        out.write_var(self.ect0);
+1257        out.write_var(self.ect1);
+1258        out.write_var(self.ce);
+1259    }
+1260}
+1261
+1262#[derive(Debug, Clone, derive_more::Display)]
+1263#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+1264#[display("STREAM id: {id} off: {offset} len: {} fin: {fin}", data.len())]
+1265pub(crate) struct Stream {
+1266    pub(crate) id: StreamId,
+1267    #[cfg_attr(test, strategy(varint_u64()))]
+1268    pub(crate) offset: u64,
+1269    pub(crate) fin: bool,
+1270    #[cfg_attr(test, strategy(Strategy::prop_map(collection::vec(any::<u8>(), 0..100), Bytes::from)))]
+1271    pub(crate) data: Bytes,
+1272}
+1273
+1274impl FrameStruct for Stream {
+1275    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+1276}
+1277
+1278/// Metadata from a stream frame
+1279#[derive(Debug, Clone, derive_more::Display)]
+1280#[display("STREAM id: {id} off: {} len: {} fin: {fin}", offsets.start, offsets.end - offsets.start)]
+1281pub(crate) struct StreamMeta {
+1282    pub(crate) id: StreamId,
+1283    pub(crate) offsets: Range<u64>,
+1284    pub(crate) fin: bool,
+1285}
+1286
+1287// This manual implementation exists because `Default` is not implemented for `StreamId`
+1288impl Default for StreamMeta {
+1289    fn default() -> Self {
+1290        Self {
+1291            id: StreamId(0),
+1292            offsets: 0..0,
+1293            fin: false,
+1294        }
+1295    }
+1296}
+1297
+1298impl StreamMeta {
+1299    pub(crate) fn encoder(self, encode_length: bool) -> StreamMetaEncoder {
+1300        StreamMetaEncoder {
+1301            meta: self,
+1302            encode_length,
+1303        }
+1304    }
+1305
+1306    const fn get_type(&self, encode_length: bool) -> StreamInfo {
+1307        let mut ty = *StreamInfo::VALUES.start();
+1308        if self.offsets.start != 0 {
+1309            ty |= 0x04;
+1310        }
+1311        if encode_length {
+1312            ty |= 0x02;
+1313        }
+1314        if self.fin {
+1315            ty |= 0x01;
+1316        }
+1317        StreamInfo(ty as u8)
+1318    }
+1319}
+1320
+1321#[derive(derive_more::Display)]
+1322#[display("{meta}")]
+1323pub(crate) struct StreamMetaEncoder {
+1324    pub(crate) meta: StreamMeta,
+1325    encode_length: bool,
+1326}
+1327
+1328impl StreamMetaEncoder {
+1329    const fn get_type(&self) -> FrameType {
+1330        FrameType::Stream(self.meta.get_type(self.encode_length))
+1331    }
+1332}
+1333
+1334impl Encodable for StreamMetaEncoder {
+1335    fn encode<W: BufMut>(&self, out: &mut W) {
+1336        let Self {
+1337            meta,
+1338            encode_length,
+1339        } = self;
+1340        out.write_var(meta.get_type(*encode_length).0 as u64); // 1 byte
+1341        out.write(meta.id); // <=8 bytes
+1342        if meta.offsets.start != 0 {
+1343            out.write_var(meta.offsets.start); // <=8 bytes
+1344        }
+1345        if *encode_length {
+1346            out.write_var(meta.offsets.end - meta.offsets.start); // <=8 bytes
+1347        }
+1348    }
+1349}
+1350
+1351/// A vector of [`StreamMeta`] with optimization for the single element case
+1352pub(crate) type StreamMetaVec = TinyVec<[StreamMeta; 1]>;
+1353
+1354#[derive(Debug, Clone, derive_more::Display)]
+1355#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+1356#[display("CRYPTO off: {offset} len = {}", data.len())]
+1357pub(crate) struct Crypto {
+1358    #[cfg_attr(test, strategy(varint_u64()))]
+1359    pub(crate) offset: u64,
+1360    #[cfg_attr(test, strategy(Strategy::prop_map(collection::vec(any::<u8>(), 0..1024), Bytes::from)))]
+1361    pub(crate) data: Bytes,
+1362}
+1363
+1364impl Crypto {
+1365    pub(crate) const SIZE_BOUND: usize = 17;
+1366
+1367    const fn get_type(&self) -> FrameType {
+1368        FrameType::Crypto
+1369    }
+1370}
+1371
+1372impl Encodable for Crypto {
+1373    fn encode<W: BufMut>(&self, out: &mut W) {
+1374        out.write(FrameType::Crypto);
+1375        out.write_var(self.offset);
+1376        out.write_var(self.data.len() as u64);
+1377        out.put_slice(&self.data);
+1378    }
+1379}
+1380
+1381#[derive(Debug, Clone, derive_more::Display)]
+1382#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+1383#[display("NEW_TOKEN")]
+1384pub(crate) struct NewToken {
+1385    #[cfg_attr(test, strategy(Strategy::prop_map(collection::vec(any::<u8>(), 0..1024), Bytes::from)))]
+1386    pub(crate) token: Bytes,
+1387}
+1388
+1389impl Encodable for NewToken {
+1390    fn encode<W: BufMut>(&self, out: &mut W) {
+1391        out.write(FrameType::NewToken);
+1392        out.write_var(self.token.len() as u64);
+1393        out.put_slice(&self.token);
+1394    }
+1395}
+1396
+1397impl NewToken {
+1398    pub(crate) fn size(&self) -> usize {
+1399        1 + VarInt::from_u64(self.token.len() as u64).unwrap().size() + self.token.len()
+1400    }
+1401
+1402    const fn get_type(&self) -> FrameType {
+1403        FrameType::NewToken
+1404    }
+1405}
+1406
+1407#[derive(Debug, Clone, derive_more::Display)]
+1408#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+1409#[display("MAX_PATH_ID path_id: {_0}")]
+1410pub(crate) struct MaxPathId(pub(crate) PathId);
+1411
+1412impl MaxPathId {
+1413    pub(crate) const SIZE_BOUND: usize =
+1414        FrameType::MaxPathId.size() + VarInt(u32::MAX as u64).size();
+1415
+1416    const fn get_type(&self) -> FrameType {
+1417        FrameType::MaxPathId
+1418    }
+1419}
+1420
+1421impl Decodable for MaxPathId {
+1422    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+1423        Ok(Self(buf.get()?))
+1424    }
+1425}
+1426
+1427impl Encodable for MaxPathId {
+1428    fn encode<B: BufMut>(&self, buf: &mut B) {
+1429        buf.write(FrameType::MaxPathId);
+1430        buf.write(self.0);
+1431    }
+1432}
+1433
+1434#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
+1435#[cfg_attr(test, derive(Arbitrary))]
+1436#[display("PATHS_BLOCKED remote_max_path_id: {_0}")]
+1437pub(crate) struct PathsBlocked(pub(crate) PathId);
+1438
+1439impl PathsBlocked {
+1440    pub(crate) const SIZE_BOUND: usize =
+1441        FrameType::PathsBlocked.size() + VarInt(u32::MAX as u64).size();
+1442
+1443    const fn get_type(&self) -> FrameType {
+1444        FrameType::PathsBlocked
+1445    }
+1446}
+1447
+1448impl Encodable for PathsBlocked {
+1449    fn encode<B: BufMut>(&self, buf: &mut B) {
+1450        buf.write(FrameType::PathsBlocked);
+1451        buf.write(self.0);
+1452    }
+1453}
+1454
+1455impl Decodable for PathsBlocked {
+1456    /// Decode [`Self`] from the buffer, provided that the frame type has been verified
+1457    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+1458        Ok(Self(buf.get()?))
+1459    }
+1460}
+1461
+1462#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
+1463#[cfg_attr(test, derive(Arbitrary))]
+1464#[display("PATH_CIDS_BLOCKED path_id: {path_id} next_seq: {next_seq}")]
+1465pub(crate) struct PathCidsBlocked {
+1466    pub(crate) path_id: PathId,
+1467    pub(crate) next_seq: VarInt,
+1468}
+1469
+1470impl PathCidsBlocked {
+1471    pub(crate) const SIZE_BOUND: usize =
+1472        FrameType::PathCidsBlocked.size() + VarInt(u32::MAX as u64).size() + VarInt::MAX.size();
+1473
+1474    const fn get_type(&self) -> FrameType {
+1475        FrameType::PathCidsBlocked
+1476    }
+1477}
+1478
+1479impl Decodable for PathCidsBlocked {
+1480    fn decode<R: Buf>(buf: &mut R) -> coding::Result<Self> {
+1481        Ok(Self {
+1482            path_id: buf.get()?,
+1483            next_seq: buf.get()?,
+1484        })
+1485    }
+1486}
+1487
+1488impl Encodable for PathCidsBlocked {
+1489    fn encode<W: BufMut>(&self, buf: &mut W) {
+1490        buf.write(FrameType::PathCidsBlocked);
+1491        buf.write(self.path_id);
+1492        buf.write(self.next_seq);
+1493    }
+1494}
+1495
+1496pub(crate) struct Iter {
+1497    bytes: Bytes,
+1498    last_ty: MaybeFrame,
+1499}
+1500
+1501impl Iter {
+1502    pub(crate) fn new(payload: Bytes) -> Result<Self, TransportError> {
+1503        if payload.is_empty() {
+1504            // "An endpoint MUST treat receipt of a packet containing no frames as a
+1505            // connection error of type PROTOCOL_VIOLATION."
+1506            // https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types
+1507            return Err(TransportError::PROTOCOL_VIOLATION(
+1508                "packet payload is empty",
+1509            ));
+1510        }
+1511
+1512        Ok(Self {
+1513            bytes: payload,
+1514            last_ty: MaybeFrame::None,
+1515        })
+1516    }
+1517
+1518    fn take_len(&mut self) -> Result<Bytes, UnexpectedEnd> {
+1519        let len = self.bytes.get_var()?;
+1520        if len > self.bytes.remaining() as u64 {
+1521            return Err(UnexpectedEnd);
+1522        }
+1523        Ok(self.bytes.split_to(len as usize))
+1524    }
+1525
+1526    #[track_caller]
+1527    fn try_next(&mut self) -> Result<Frame, IterErr> {
+1528        self.last_ty = self.bytes.get()?;
+1529
+1530        let ty = match self.last_ty {
+1531            MaybeFrame::None => FrameType::Padding,
+1532            MaybeFrame::Unknown(_other) => return Err(IterErr::InvalidFrameId),
+1533            MaybeFrame::Known(frame_type) => frame_type,
+1534        };
+1535        Ok(match ty {
+1536            FrameType::Padding => Frame::Padding,
+1537            FrameType::ResetStream => Frame::ResetStream(ResetStream {
+1538                id: self.bytes.get()?,
+1539                error_code: self.bytes.get()?,
+1540                final_offset: self.bytes.get()?,
+1541            }),
+1542            FrameType::ConnectionClose => Frame::Close(Close::Connection(ConnectionClose {
+1543                error_code: self.bytes.get()?,
+1544                frame_type: self.bytes.get()?,
+1545                reason: self.take_len()?,
+1546            })),
+1547            FrameType::ApplicationClose => Frame::Close(Close::Application(ApplicationClose {
+1548                error_code: self.bytes.get()?,
+1549                reason: self.take_len()?,
+1550            })),
+1551            FrameType::MaxData => Frame::MaxData(self.bytes.get()?),
+1552            FrameType::MaxStreamData => Frame::MaxStreamData(self.bytes.get()?),
+1553            FrameType::MaxStreamsBidi => Frame::MaxStreams(MaxStreams {
+1554                dir: Dir::Bi,
+1555                count: self.bytes.get_var()?,
+1556            }),
+1557            FrameType::MaxStreamsUni => Frame::MaxStreams(MaxStreams {
+1558                dir: Dir::Uni,
+1559                count: self.bytes.get_var()?,
+1560            }),
+1561            FrameType::Ping => Frame::Ping,
+1562            FrameType::DataBlocked => Frame::DataBlocked(DataBlocked(self.bytes.get_var()?)),
+1563            FrameType::StreamDataBlocked => Frame::StreamDataBlocked(StreamDataBlocked {
+1564                id: self.bytes.get()?,
+1565                offset: self.bytes.get_var()?,
+1566            }),
+1567            FrameType::StreamsBlockedBidi => Frame::StreamsBlocked(StreamsBlocked {
+1568                dir: Dir::Bi,
+1569                limit: self.bytes.get_var()?,
+1570            }),
+1571            FrameType::StreamsBlockedUni => Frame::StreamsBlocked(StreamsBlocked {
+1572                dir: Dir::Uni,
+1573                limit: self.bytes.get_var()?,
+1574            }),
+1575            FrameType::StopSending => Frame::StopSending(StopSending {
+1576                id: self.bytes.get()?,
+1577                error_code: self.bytes.get()?,
+1578            }),
+1579            FrameType::RetireConnectionId | FrameType::PathRetireConnectionId => {
+1580                Frame::RetireConnectionId(RetireConnectionId::decode(
+1581                    &mut self.bytes,
+1582                    ty == FrameType::PathRetireConnectionId,
+1583                )?)
+1584            }
+1585            FrameType::Ack => {
+1586                let largest = self.bytes.get_var()?;
+1587                let delay = self.bytes.get_var()?;
+1588                let ranges = read_ack_blocks(&mut self.bytes, largest)?;
+1589                Frame::Ack(Ack {
+1590                    delay,
+1591                    largest,
+1592                    ranges,
+1593                    ecn: None,
+1594                })
+1595            }
+1596            FrameType::AckEcn => {
+1597                let largest = self.bytes.get_var()?;
+1598                let delay = self.bytes.get_var()?;
+1599                let ranges = read_ack_blocks(&mut self.bytes, largest)?;
+1600                let ecn = Some(EcnCounts {
+1601                    ect0: self.bytes.get_var()?,
+1602                    ect1: self.bytes.get_var()?,
+1603                    ce: self.bytes.get_var()?,
+1604                });
+1605
+1606                Frame::Ack(Ack {
+1607                    delay,
+1608                    largest,
+1609                    ranges,
+1610                    ecn,
+1611                })
+1612            }
+1613            FrameType::PathAck => {
+1614                let path_id = self.bytes.get()?;
+1615                let largest = self.bytes.get_var()?;
+1616                let delay = self.bytes.get_var()?;
+1617                let ranges = read_ack_blocks(&mut self.bytes, largest)?;
+1618                Frame::PathAck(PathAck {
+1619                    path_id,
+1620                    delay,
+1621                    largest,
+1622                    ranges,
+1623                    ecn: None,
+1624                })
+1625            }
+1626            FrameType::PathAckEcn => {
+1627                let path_id = self.bytes.get()?;
+1628                let largest = self.bytes.get_var()?;
+1629                let delay = self.bytes.get_var()?;
+1630                let ranges = read_ack_blocks(&mut self.bytes, largest)?;
+1631                let ecn = Some(EcnCounts {
+1632                    ect0: self.bytes.get_var()?,
+1633                    ect1: self.bytes.get_var()?,
+1634                    ce: self.bytes.get_var()?,
+1635                });
+1636                Frame::PathAck(PathAck {
+1637                    path_id,
+1638                    delay,
+1639                    largest,
+1640                    ranges,
+1641                    ecn,
+1642                })
+1643            }
+1644            FrameType::PathChallenge => Frame::PathChallenge(self.bytes.get()?),
+1645            FrameType::PathResponse => Frame::PathResponse(self.bytes.get()?),
+1646            FrameType::NewConnectionId | FrameType::PathNewConnectionId => {
+1647                let read_path = ty == FrameType::PathNewConnectionId;
+1648                Frame::NewConnectionId(NewConnectionId::read(&mut self.bytes, read_path)?)
+1649            }
+1650            FrameType::Crypto => Frame::Crypto(Crypto {
+1651                offset: self.bytes.get_var()?,
+1652                data: self.take_len()?,
+1653            }),
+1654            FrameType::NewToken => Frame::NewToken(NewToken {
+1655                token: self.take_len()?,
+1656            }),
+1657            FrameType::HandshakeDone => Frame::HandshakeDone,
+1658            FrameType::AckFrequency => Frame::AckFrequency(AckFrequency {
+1659                sequence: self.bytes.get()?,
+1660                ack_eliciting_threshold: self.bytes.get()?,
+1661                request_max_ack_delay: self.bytes.get()?,
+1662                reordering_threshold: self.bytes.get()?,
+1663            }),
+1664            FrameType::ImmediateAck => Frame::ImmediateAck,
+1665            FrameType::ObservedIpv4Addr | FrameType::ObservedIpv6Addr => {
+1666                let is_ipv6 = ty == FrameType::ObservedIpv6Addr;
+1667                let observed = ObservedAddr::read(&mut self.bytes, is_ipv6)?;
+1668                Frame::ObservedAddr(observed)
+1669            }
+1670            FrameType::PathAbandon => Frame::PathAbandon(PathAbandon::decode(&mut self.bytes)?),
+1671            FrameType::PathStatusAvailable => {
+1672                Frame::PathStatusAvailable(PathStatusAvailable::decode(&mut self.bytes)?)
+1673            }
+1674            FrameType::PathStatusBackup => {
+1675                Frame::PathStatusBackup(PathStatusBackup::decode(&mut self.bytes)?)
+1676            }
+1677            FrameType::MaxPathId => Frame::MaxPathId(MaxPathId::decode(&mut self.bytes)?),
+1678            FrameType::PathsBlocked => Frame::PathsBlocked(PathsBlocked::decode(&mut self.bytes)?),
+1679            FrameType::PathCidsBlocked => {
+1680                Frame::PathCidsBlocked(PathCidsBlocked::decode(&mut self.bytes)?)
+1681            }
+1682            FrameType::AddIpv4Address | FrameType::AddIpv6Address => {
+1683                let is_ipv6 = ty == FrameType::AddIpv6Address;
+1684                let add_address = AddAddress::read(&mut self.bytes, is_ipv6)?;
+1685                Frame::AddAddress(add_address)
+1686            }
+1687            FrameType::ReachOutAtIpv4 | FrameType::ReachOutAtIpv6 => {
+1688                let is_ipv6 = ty == FrameType::ReachOutAtIpv6;
+1689                let reach_out = ReachOut::read(&mut self.bytes, is_ipv6)?;
+1690                Frame::ReachOut(reach_out)
+1691            }
+1692            FrameType::RemoveAddress => Frame::RemoveAddress(RemoveAddress::read(&mut self.bytes)?),
+1693            FrameType::Stream(s) => Frame::Stream(Stream {
+1694                id: self.bytes.get()?,
+1695                offset: if s.off() { self.bytes.get_var()? } else { 0 },
+1696                fin: s.fin(),
+1697                data: if s.len() {
+1698                    self.take_len()?
+1699                } else {
+1700                    self.take_remaining()
+1701                },
+1702            }),
+1703            FrameType::Datagram(d) => Frame::Datagram(Datagram {
+1704                data: if d.len() {
+1705                    self.take_len()?
+1706                } else {
+1707                    self.take_remaining()
+1708                },
+1709            }),
+1710        })
+1711    }
+1712
+1713    pub(crate) fn take_remaining(&mut self) -> Bytes {
+1714        mem::take(&mut self.bytes)
+1715    }
+1716}
+1717
+1718impl Iterator for Iter {
+1719    type Item = Result<Frame, InvalidFrame>;
+1720    fn next(&mut self) -> Option<Self::Item> {
+1721        if !self.bytes.has_remaining() {
+1722            return None;
+1723        }
+1724        match self.try_next() {
+1725            Ok(x) => Some(Ok(x)),
+1726            Err(e) => {
+1727                // Corrupt frame, skip it and everything that follows
+1728                self.bytes.clear();
+1729                Some(Err(InvalidFrame {
+1730                    ty: self.last_ty,
+1731                    reason: e.reason(),
+1732                }))
+1733            }
+1734        }
+1735    }
+1736}
+1737
+1738#[derive(Debug)]
+1739pub(crate) struct InvalidFrame {
+1740    pub(crate) ty: MaybeFrame,
+1741    pub(crate) reason: &'static str,
+1742}
+1743
+1744impl From<InvalidFrame> for TransportError {
+1745    fn from(err: InvalidFrame) -> Self {
+1746        let mut te = Self::FRAME_ENCODING_ERROR(err.reason);
+1747        te.frame = err.ty;
+1748        te
+1749    }
+1750}
+1751
+1752/// Decodes the ACK Ranges from the given buffer.
+1753///  This means, reading these three values
+1754///
+1755/// - ACK Range Count
+1756/// - First ACK Range
+1757/// - ACK Ranges
+1758///
+1759/// Ref <https://www.rfc-editor.org/rfc/rfc9000.html#name-ack-ranges>
+1760fn read_ack_blocks(buf: &mut Bytes, mut largest: u64) -> Result<ArrayRangeSet, IterErr> {
+1761    // Ack Range Count
+1762    let num_blocks = buf.get_var()?;
+1763
+1764    let mut out = ArrayRangeSet::new();
+1765    let mut block_to_block;
+1766    let mut range;
+1767
+1768    for num_block in 0..num_blocks + 1 {
+1769        range = buf.get_var()?;
+1770        range += 1;
+1771
+1772        let start = (largest + 1).checked_sub(range).ok_or(IterErr::Malformed)?;
+1773
+1774        if range > 0 {
+1775            out.insert(start..largest + 1);
+1776        }
+1777
+1778        // no gap on the last "block"
+1779        if num_block < num_blocks {
+1780            // skip the gap
+1781            block_to_block = buf.get_var()?;
+1782            block_to_block += 1;
+1783            block_to_block += range;
+1784
+1785            largest = largest
+1786                .checked_sub(block_to_block)
+1787                .ok_or(IterErr::Malformed)?;
+1788        }
+1789    }
+1790
+1791    Ok(out)
+1792}
+1793
+1794#[derive(Debug)]
+1795enum IterErr {
+1796    UnexpectedEnd,
+1797    InvalidFrameId,
+1798    Malformed,
+1799}
+1800
+1801impl IterErr {
+1802    fn reason(&self) -> &'static str {
+1803        use IterErr::*;
+1804        match *self {
+1805            UnexpectedEnd => "unexpected end",
+1806            InvalidFrameId => "invalid frame ID",
+1807            Malformed => "malformed",
+1808        }
+1809    }
+1810}
+1811
+1812impl From<UnexpectedEnd> for IterErr {
+1813    fn from(_: UnexpectedEnd) -> Self {
+1814        Self::UnexpectedEnd
+1815    }
+1816}
+1817
+1818#[allow(unreachable_pub)] // fuzzing only
+1819#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
+1820#[cfg_attr(test, derive(Arbitrary))]
+1821#[derive(Debug, Copy, Clone, derive_more::Display)]
+1822#[display("RESET_STREAM id: {id}")]
+1823pub struct ResetStream {
+1824    pub(crate) id: StreamId,
+1825    pub(crate) error_code: VarInt,
+1826    pub(crate) final_offset: VarInt,
+1827}
+1828
+1829impl ResetStream {
+1830    const fn get_type(&self) -> FrameType {
+1831        FrameType::ResetStream
+1832    }
+1833}
+1834
+1835impl FrameStruct for ResetStream {
+1836    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+1837}
+1838
+1839impl Encodable for ResetStream {
+1840    fn encode<W: BufMut>(&self, out: &mut W) {
+1841        out.write(FrameType::ResetStream); // 1 byte
+1842        out.write(self.id); // <= 8 bytes
+1843        out.write(self.error_code); // <= 8 bytes
+1844        out.write(self.final_offset); // <= 8 bytes
+1845    }
+1846}
+1847
+1848#[cfg_attr(test, derive(Arbitrary))]
+1849#[derive(Debug, Copy, Clone, derive_more::Display)]
+1850#[display("STOP_SENDING id: {id}")]
+1851pub(crate) struct StopSending {
+1852    pub(crate) id: StreamId,
+1853    pub(crate) error_code: VarInt,
+1854}
+1855
+1856impl FrameStruct for StopSending {
+1857    const SIZE_BOUND: usize = 1 + 8 + 8;
+1858}
+1859
+1860impl StopSending {
+1861    const fn get_type(&self) -> FrameType {
+1862        FrameType::StopSending
+1863    }
+1864}
+1865
+1866impl Encodable for StopSending {
+1867    fn encode<W: BufMut>(&self, out: &mut W) {
+1868        out.write(FrameType::StopSending); // 1 byte
+1869        out.write(self.id); // <= 8 bytes
+1870        out.write(self.error_code) // <= 8 bytes
+1871    }
+1872}
+1873
+1874#[derive(Debug, Copy, Clone, PartialEq, Eq, derive_more::Display)]
+1875#[display("{} {} seq: {sequence} id: {id}", self.get_type(), DisplayOption::new("path_id", path_id.as_ref()))]
+1876pub(crate) struct NewConnectionId {
+1877    pub(crate) path_id: Option<PathId>,
+1878    pub(crate) sequence: u64,
+1879    pub(crate) retire_prior_to: u64,
+1880    pub(crate) id: ConnectionId,
+1881    pub(crate) reset_token: ResetToken,
+1882}
+1883
+1884#[cfg(test)]
+1885fn connection_id_and_reset_token() -> impl Strategy<Value = (crate::ConnectionId, ResetToken)> {
+1886    (any::<ConnectionId>(), any::<[u8; 64]>()).prop_map(|(id, reset_key)| {
+1887        #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+1888        use aws_lc_rs::hmac;
+1889        #[cfg(feature = "ring")]
+1890        use ring::hmac;
+1891        let key = hmac::Key::new(hmac::HMAC_SHA256, &reset_key);
+1892        (id, ResetToken::new(&key, id))
+1893    })
+1894}
+1895
+1896#[cfg(test)]
+1897impl proptest::arbitrary::Arbitrary for NewConnectionId {
+1898    type Parameters = ();
+1899    type Strategy = proptest::strategy::BoxedStrategy<Self>;
+1900
+1901    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
+1902        use proptest::prelude::*;
+1903        (
+1904            any::<Option<PathId>>(),
+1905            varint_u64(),
+1906            varint_u64(),
+1907            connection_id_and_reset_token(),
+1908        )
+1909            .prop_map(|(path_id, a, b, (id, reset_token))| {
+1910                let sequence = std::cmp::max(a, b);
+1911                let retire_prior_to = std::cmp::min(a, b);
+1912                Self {
+1913                    path_id,
+1914                    sequence,
+1915                    retire_prior_to,
+1916                    id,
+1917                    reset_token,
+1918                }
+1919            })
+1920            .boxed()
+1921    }
+1922}
+1923
+1924impl NewConnectionId {
+1925    /// Maximum size of this frame when the frame type is [`FrameType::NewConnectionId`],
+1926    pub(crate) const SIZE_BOUND: usize = {
+1927        let type_len = FrameType::NewConnectionId.size();
+1928        let seq_max_len = 8usize;
+1929        let retire_prior_to_max_len = 8usize;
+1930        let cid_len_len = 1;
+1931        let cid_len = 160;
+1932        let reset_token_len = 16;
+1933        type_len + seq_max_len + retire_prior_to_max_len + cid_len_len + cid_len + reset_token_len
+1934    };
+1935
+1936    /// Maximum size of this frame when the frame type is [`FrameType::PathNewConnectionId`],
+1937    pub(crate) const SIZE_BOUND_MULTIPATH: usize = {
+1938        let type_len = FrameType::PathNewConnectionId.size();
+1939        let path_id_len = VarInt::from_u32(u32::MAX).size();
+1940        let seq_max_len = 8usize;
+1941        let retire_prior_to_max_len = 8usize;
+1942        let cid_len_len = 1;
+1943        let cid_len = 160;
+1944        let reset_token_len = 16;
+1945        type_len
+1946            + path_id_len
+1947            + seq_max_len
+1948            + retire_prior_to_max_len
+1949            + cid_len_len
+1950            + cid_len
+1951            + reset_token_len
+1952    };
+1953
+1954    const fn get_type(&self) -> FrameType {
+1955        if self.path_id.is_some() {
+1956            FrameType::PathNewConnectionId
+1957        } else {
+1958            FrameType::NewConnectionId
+1959        }
+1960    }
+1961
+1962    /// Returns the maximum encoded size on the wire.
+1963    ///
+1964    /// This is a rough upper estimate, does not squeeze every last byte out.
+1965    pub(crate) const fn size_bound(path_new_cid: bool, cid_len: usize) -> usize {
+1966        let upper_bound = match path_new_cid {
+1967            true => Self::SIZE_BOUND_MULTIPATH,
+1968            false => Self::SIZE_BOUND,
+1969        };
+1970        // instead of using the maximum cid len, use the provided one
+1971        upper_bound - 160 + cid_len
+1972    }
+1973
+1974    fn read<R: Buf>(bytes: &mut R, read_path: bool) -> Result<Self, IterErr> {
+1975        let path_id = if read_path { Some(bytes.get()?) } else { None };
+1976        let sequence = bytes.get_var()?;
+1977        let retire_prior_to = bytes.get_var()?;
+1978        if retire_prior_to > sequence {
+1979            return Err(IterErr::Malformed);
+1980        }
+1981        let length = bytes.get::<u8>()? as usize;
+1982        if length > MAX_CID_SIZE || length == 0 {
+1983            return Err(IterErr::Malformed);
+1984        }
+1985        if length > bytes.remaining() {
+1986            return Err(IterErr::UnexpectedEnd);
+1987        }
+1988        let mut stage = [0; MAX_CID_SIZE];
+1989        bytes.copy_to_slice(&mut stage[0..length]);
+1990        let id = ConnectionId::new(&stage[..length]);
+1991        if bytes.remaining() < 16 {
+1992            return Err(IterErr::UnexpectedEnd);
+1993        }
+1994        let mut reset_token = [0; RESET_TOKEN_SIZE];
+1995        bytes.copy_to_slice(&mut reset_token);
+1996        Ok(Self {
+1997            path_id,
+1998            sequence,
+1999            retire_prior_to,
+2000            id,
+2001            reset_token: reset_token.into(),
+2002        })
+2003    }
+2004
+2005    pub(crate) fn issued(&self) -> crate::shared::IssuedCid {
+2006        crate::shared::IssuedCid {
+2007            path_id: self.path_id.unwrap_or_default(),
+2008            sequence: self.sequence,
+2009            id: self.id,
+2010            reset_token: self.reset_token,
+2011        }
+2012    }
+2013}
+2014
+2015impl Encodable for NewConnectionId {
+2016    fn encode<W: BufMut>(&self, out: &mut W) {
+2017        out.write(self.get_type());
+2018        if let Some(id) = self.path_id {
+2019            out.write(id);
+2020        }
+2021        out.write_var(self.sequence);
+2022        out.write_var(self.retire_prior_to);
+2023        out.write(self.id.len() as u8);
+2024        out.put_slice(&self.id);
+2025        out.put_slice(&self.reset_token);
+2026    }
+2027}
+2028
+2029impl FrameStruct for NewConnectionId {
+2030    const SIZE_BOUND: usize = 1 + 8 + 8 + 1 + MAX_CID_SIZE + RESET_TOKEN_SIZE;
+2031}
+2032
+2033/// An unreliable datagram
+2034#[derive(Debug, Clone, derive_more::Display)]
+2035#[cfg_attr(test, derive(Arbitrary))]
+2036#[display("DATAGRAM len: {}", data.len())]
+2037pub struct Datagram {
+2038    /// Payload
+2039    #[cfg_attr(test, strategy(Strategy::prop_map(collection::vec(any::<u8>(), 0..100), Bytes::from)))]
+2040    pub data: Bytes,
+2041}
+2042
+2043impl FrameStruct for Datagram {
+2044    const SIZE_BOUND: usize = 1 + 8;
+2045}
+2046
+2047impl Datagram {
+2048    pub(crate) fn size(&self, length: bool) -> usize {
+2049        1 + if length {
+2050            VarInt::from_u64(self.data.len() as u64).unwrap().size()
+2051        } else {
+2052            0
+2053        } + self.data.len()
+2054    }
+2055
+2056    const fn get_type(&self) -> FrameType {
+2057        FrameType::Datagram(DatagramInfo(*DatagramInfo::VALUES.start() as u8))
+2058    }
+2059}
+2060
+2061impl Encodable for Datagram {
+2062    fn encode<B: BufMut>(&self, out: &mut B) {
+2063        // A datagram is encoded only after this is verified.
+2064        const ENCODE_LEN: bool = true;
+2065        out.write(FrameType::Datagram(DatagramInfo(
+2066            *DatagramInfo::VALUES.start() as u8 | u8::from(ENCODE_LEN),
+2067        ))); // 1 byte
+2068        // Safe to unwrap because we check length sanity before queueing datagrams
+2069        out.write(VarInt::from_u64(self.data.len() as u64).unwrap()); // <= 8 bytes
+2070        out.put_slice(&self.data);
+2071    }
+2072}
+2073
+2074#[derive(Debug, Copy, Clone, PartialEq, Eq, derive_more::Display)]
+2075#[cfg_attr(test, derive(Arbitrary))]
+2076#[display("ACK_FREQUENCY max_ack_delay: {}µs", request_max_ack_delay.0)]
+2077pub(crate) struct AckFrequency {
+2078    pub(crate) sequence: VarInt,
+2079    pub(crate) ack_eliciting_threshold: VarInt,
+2080    pub(crate) request_max_ack_delay: VarInt,
+2081    pub(crate) reordering_threshold: VarInt,
+2082}
+2083
+2084impl AckFrequency {
+2085    const fn get_type(&self) -> FrameType {
+2086        FrameType::AckFrequency
+2087    }
+2088}
+2089
+2090impl Encodable for AckFrequency {
+2091    fn encode<W: BufMut>(&self, buf: &mut W) {
+2092        buf.write(FrameType::AckFrequency);
+2093        buf.write(self.sequence);
+2094        buf.write(self.ack_eliciting_threshold);
+2095        buf.write(self.request_max_ack_delay);
+2096        buf.write(self.reordering_threshold);
+2097    }
+2098}
+2099
+2100/* Address Discovery https://datatracker.ietf.org/doc/draft-seemann-quic-address-discovery/ */
+2101
+2102/// Conjunction of the information contained in the address discovery frames
+2103/// ([`FrameType::ObservedIpv4Addr`], [`FrameType::ObservedIpv6Addr`]).
+2104#[derive(Debug, PartialEq, Eq, Clone, derive_more::Display)]
+2105#[display("{} seq_no: {seq_no} addr: {}", self.get_type(), self.socket_addr())]
+2106#[cfg_attr(test, derive(Arbitrary))]
+2107pub(crate) struct ObservedAddr {
+2108    /// Monotonically increasing integer within the same connection.
+2109    pub(crate) seq_no: VarInt,
+2110    /// Reported observed address.
+2111    pub(crate) ip: IpAddr,
+2112    /// Reported observed port.
+2113    pub(crate) port: u16,
+2114}
+2115
+2116impl ObservedAddr {
+2117    pub(crate) fn new<N: Into<VarInt>>(remote: std::net::SocketAddr, seq_no: N) -> Self {
+2118        Self {
+2119            ip: remote.ip(),
+2120            port: remote.port(),
+2121            seq_no: seq_no.into(),
+2122        }
+2123    }
+2124
+2125    /// Get the [`FrameType`] for this frame.
+2126    const fn get_type(&self) -> FrameType {
+2127        if self.ip.is_ipv6() {
+2128            FrameType::ObservedIpv6Addr
+2129        } else {
+2130            FrameType::ObservedIpv4Addr
+2131        }
+2132    }
+2133
+2134    /// Compute the number of bytes needed to encode the frame.
+2135    pub(crate) fn size(&self) -> usize {
+2136        let type_size = self.get_type().size();
+2137        let req_id_bytes = self.seq_no.size();
+2138        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+2139        let port_bytes = 2;
+2140        type_size + req_id_bytes + ip_bytes + port_bytes
+2141    }
+2142
+2143    /// Reads the frame contents from the buffer.
+2144    ///
+2145    /// Should only be called when the frame type has been identified as
+2146    /// [`FrameType::ObservedIpv4Addr`] or [`FrameType::ObservedIpv6Addr`].
+2147    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+2148        let seq_no = bytes.get()?;
+2149        let ip = if is_ipv6 {
+2150            IpAddr::V6(bytes.get()?)
+2151        } else {
+2152            IpAddr::V4(bytes.get()?)
+2153        };
+2154        let port = bytes.get()?;
+2155        Ok(Self { seq_no, ip, port })
+2156    }
+2157
+2158    /// Gives the [`SocketAddr`] reported in the frame.
+2159    pub(crate) fn socket_addr(&self) -> SocketAddr {
+2160        (self.ip, self.port).into()
+2161    }
+2162}
+2163
+2164impl Encodable for ObservedAddr {
+2165    fn encode<W: BufMut>(&self, buf: &mut W) {
+2166        buf.write(self.get_type());
+2167        buf.write(self.seq_no);
+2168        match self.ip {
+2169            IpAddr::V4(ipv4_addr) => {
+2170                buf.write(ipv4_addr);
+2171            }
+2172            IpAddr::V6(ipv6_addr) => {
+2173                buf.write(ipv6_addr);
+2174            }
+2175        }
+2176        buf.write::<u16>(self.port);
+2177    }
+2178}
+2179
+2180/* Multipath <https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/> */
+2181
+2182#[derive(Debug, PartialEq, Eq, derive_more::Display)]
+2183#[cfg_attr(test, derive(Arbitrary))]
+2184#[display("PATH_ABANDON path_id: {path_id}")]
+2185pub(crate) struct PathAbandon {
+2186    pub(crate) path_id: PathId,
+2187    pub(crate) error_code: TransportErrorCode,
+2188}
+2189
+2190impl PathAbandon {
+2191    pub(crate) const SIZE_BOUND: usize = FrameType::PathAbandon.size() + 8 + 8;
+2192
+2193    const fn get_type(&self) -> FrameType {
+2194        FrameType::PathAbandon
+2195    }
+2196}
+2197
+2198impl Encodable for PathAbandon {
+2199    fn encode<W: BufMut>(&self, buf: &mut W) {
+2200        buf.write(FrameType::PathAbandon);
+2201        buf.write(self.path_id);
+2202        buf.write(self.error_code);
+2203    }
+2204}
+2205
+2206impl Decodable for PathAbandon {
+2207    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+2208        Ok(Self {
+2209            path_id: bytes.get()?,
+2210            error_code: bytes.get()?,
+2211        })
+2212    }
+2213}
+2214
+2215#[derive(Debug, PartialEq, Eq, derive_more::Display)]
+2216#[cfg_attr(test, derive(Arbitrary))]
+2217#[display("PATH_STATUS_AVAILABLE path_id: {path_id} seq_no: {status_seq_no}")]
+2218pub(crate) struct PathStatusAvailable {
+2219    pub(crate) path_id: PathId,
+2220    pub(crate) status_seq_no: VarInt,
+2221}
+2222
+2223impl PathStatusAvailable {
+2224    const TYPE: FrameType = FrameType::PathStatusAvailable;
+2225    pub(crate) const SIZE_BOUND: usize = FrameType::PathStatusAvailable.size() + 8 + 8;
+2226
+2227    const fn get_type(&self) -> FrameType {
+2228        FrameType::PathStatusAvailable
+2229    }
+2230}
+2231
+2232impl Encodable for PathStatusAvailable {
+2233    fn encode<W: BufMut>(&self, buf: &mut W) {
+2234        buf.write(Self::TYPE);
+2235        buf.write(self.path_id);
+2236        buf.write(self.status_seq_no);
+2237    }
+2238}
+2239
+2240impl Decodable for PathStatusAvailable {
+2241    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+2242        Ok(Self {
+2243            path_id: bytes.get()?,
+2244            status_seq_no: bytes.get()?,
+2245        })
+2246    }
+2247}
+2248
+2249#[derive(Debug, PartialEq, Eq, derive_more::Display)]
+2250#[cfg_attr(test, derive(Arbitrary))]
+2251#[display("PATH_STATUS_BACKUP path_id: {path_id} seq_no: {status_seq_no}")]
+2252pub(crate) struct PathStatusBackup {
+2253    pub(crate) path_id: PathId,
+2254    pub(crate) status_seq_no: VarInt,
+2255}
+2256
+2257impl PathStatusBackup {
+2258    const TYPE: FrameType = FrameType::PathStatusBackup;
+2259
+2260    const fn get_type(&self) -> FrameType {
+2261        FrameType::PathStatusBackup
+2262    }
+2263}
+2264
+2265impl Encodable for PathStatusBackup {
+2266    fn encode<W: BufMut>(&self, buf: &mut W) {
+2267        buf.write(Self::TYPE);
+2268        buf.write(self.path_id);
+2269        buf.write(self.status_seq_no);
+2270    }
+2271}
+2272
+2273impl Decodable for PathStatusBackup {
+2274    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+2275        Ok(Self {
+2276            path_id: bytes.get()?,
+2277            status_seq_no: bytes.get()?,
+2278        })
+2279    }
+2280}
+2281
+2282/* Nat traversal frames */
+2283
+2284/// Conjunction of the information contained in the add address frames
+2285/// ([`FrameType::AddIpv4Address`], [`FrameType::AddIpv6Address`]).
+2286#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord, derive_more::Display)]
+2287#[display("{} seq_no: {seq_no} addr: {}", self.get_type(), self.socket_addr())]
+2288#[cfg_attr(test, derive(Arbitrary))]
+2289pub(crate) struct AddAddress {
+2290    /// Monotonically increasing integer within the same connection
+2291    // TODO(@divma): both assumed, the draft has no mention of this but it's standard
+2292    pub(crate) seq_no: VarInt,
+2293    /// Address to include in the known set
+2294    pub(crate) ip: IpAddr,
+2295    /// Port to use with this address
+2296    pub(crate) port: u16,
+2297}
+2298
+2299// TODO(@divma): remove
+2300#[allow(dead_code)]
+2301impl AddAddress {
+2302    /// Smallest number of bytes this type of frame is guaranteed to fit within.
+2303    pub(crate) const SIZE_BOUND: usize = Self {
+2304        ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST),
+2305        port: u16::MAX,
+2306        seq_no: VarInt::MAX,
+2307    }
+2308    .size();
+2309
+2310    pub(crate) const fn new((ip, port): (IpAddr, u16), seq_no: VarInt) -> Self {
+2311        Self { ip, port, seq_no }
+2312    }
+2313
+2314    /// Get the [`FrameType`] for this frame.
+2315    const fn get_type(&self) -> FrameType {
+2316        if self.ip.is_ipv6() {
+2317            FrameType::AddIpv6Address
+2318        } else {
+2319            FrameType::AddIpv4Address
+2320        }
+2321    }
+2322
+2323    /// Compute the number of bytes needed to encode the frame
+2324    pub(crate) const fn size(&self) -> usize {
+2325        let type_size = self.get_type().size();
+2326        let seq_no_bytes = self.seq_no.size();
+2327        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+2328        let port_bytes = 2;
+2329        type_size + seq_no_bytes + ip_bytes + port_bytes
+2330    }
+2331
+2332    /// Read the frame contents from the buffer
+2333    ///
+2334    /// Should only be called when the frame type has been identified as
+2335    /// [`FrameType::AddIpv4Address`] or [`FrameType::AddIpv6Address`].
+2336    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+2337        let seq_no = bytes.get()?;
+2338        let ip = if is_ipv6 {
+2339            IpAddr::V6(bytes.get()?)
+2340        } else {
+2341            IpAddr::V4(bytes.get()?)
+2342        };
+2343        let port = bytes.get()?;
+2344        Ok(Self { seq_no, ip, port })
+2345    }
+2346
+2347    /// Give the [`SocketAddr`] encoded in the frame
+2348    pub(crate) fn socket_addr(&self) -> SocketAddr {
+2349        self.ip_port().into()
+2350    }
+2351
+2352    pub(crate) fn ip_port(&self) -> (IpAddr, u16) {
+2353        (self.ip, self.port)
+2354    }
+2355}
+2356
+2357impl Encodable for AddAddress {
+2358    fn encode<W: BufMut>(&self, buf: &mut W) {
+2359        buf.write(self.get_type());
+2360        buf.write(self.seq_no);
+2361        match self.ip {
+2362            IpAddr::V4(ipv4_addr) => {
+2363                buf.write(ipv4_addr);
+2364            }
+2365            IpAddr::V6(ipv6_addr) => {
+2366                buf.write(ipv6_addr);
+2367            }
+2368        }
+2369        buf.write::<u16>(self.port);
+2370    }
+2371}
+2372
+2373/// Conjunction of the information contained in the reach out frames.
+2374///
+2375/// ([`FrameType::ReachOutAtIpv4`], [`FrameType::ReachOutAtIpv6`])
+2376#[derive(Debug, PartialEq, Eq, Clone, derive_more::Display)]
+2377#[display("REACH_OUT round: {round} local_addr: {}", self.socket_addr())]
+2378#[cfg_attr(test, derive(Arbitrary))]
+2379pub(crate) struct ReachOut {
+2380    /// The sequence number of the NAT Traversal attempts
+2381    pub(crate) round: VarInt,
+2382    /// Address to use
+2383    pub(crate) ip: IpAddr,
+2384    /// Port to use with this address
+2385    pub(crate) port: u16,
+2386}
+2387
+2388impl ReachOut {
+2389    /// Get the [`FrameType`] for this frame
+2390    pub(crate) const fn get_type(&self) -> FrameType {
+2391        if self.ip.is_ipv6() {
+2392            FrameType::ReachOutAtIpv6
+2393        } else {
+2394            FrameType::ReachOutAtIpv4
+2395        }
+2396    }
+2397
+2398    /// Compute the number of bytes needed to encode the frame
+2399    pub(crate) const fn size(&self) -> usize {
+2400        let type_size = self.get_type().size();
+2401        let round_bytes = self.round.size();
+2402        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+2403        let port_bytes = 2;
+2404        type_size + round_bytes + ip_bytes + port_bytes
+2405    }
+2406
+2407    /// Read the frame contents from the buffer
+2408    ///
+2409    /// Should only be called when the frame type has been identified as
+2410    /// [`FrameType::ReachOutAtIpv4`] or [`FrameType::ReachOutAtIpv6`].
+2411    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+2412        let round = bytes.get()?;
+2413        let ip = if is_ipv6 {
+2414            IpAddr::V6(bytes.get()?)
+2415        } else {
+2416            IpAddr::V4(bytes.get()?)
+2417        };
+2418        let port = bytes.get()?;
+2419        Ok(Self { round, ip, port })
+2420    }
+2421
+2422    /// Give the [`SocketAddr`] encoded in the frame
+2423    pub(crate) fn socket_addr(&self) -> SocketAddr {
+2424        (self.ip, self.port).into()
+2425    }
+2426}
+2427
+2428impl Encodable for ReachOut {
+2429    fn encode<W: BufMut>(&self, buf: &mut W) {
+2430        buf.write(self.get_type());
+2431        buf.write(self.round);
+2432        match self.ip {
+2433            IpAddr::V4(ipv4_addr) => {
+2434                buf.write(ipv4_addr);
+2435            }
+2436            IpAddr::V6(ipv6_addr) => {
+2437                buf.write(ipv6_addr);
+2438            }
+2439        }
+2440        buf.write::<u16>(self.port);
+2441    }
+2442}
+2443
+2444/// Frame signaling an address is no longer being advertised
+2445#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord, derive_more::Display)]
+2446#[cfg_attr(test, derive(Arbitrary))]
+2447#[display("REMOVE_ADDRESS seq_no: {seq_no}")]
+2448pub(crate) struct RemoveAddress {
+2449    /// The sequence number of the address advertisement to be removed
+2450    pub(crate) seq_no: VarInt,
+2451}
+2452
+2453// TODO(@divma): remove
+2454#[allow(dead_code)]
+2455impl RemoveAddress {
+2456    /// [`FrameType`] of this frame
+2457    pub(crate) const TYPE: FrameType = FrameType::RemoveAddress;
+2458
+2459    /// Smallest number of bytes this type of frame is guaranteed to fit within
+2460    pub(crate) const SIZE_BOUND: usize = Self::new(VarInt::MAX).size();
+2461
+2462    pub(crate) const fn new(seq_no: VarInt) -> Self {
+2463        Self { seq_no }
+2464    }
+2465
+2466    /// Compute the number of bytes needed to encode the frame
+2467    pub(crate) const fn size(&self) -> usize {
+2468        let type_size = Self::TYPE.size();
+2469        let seq_no_bytes = self.seq_no.size();
+2470        type_size + seq_no_bytes
+2471    }
+2472
+2473    /// Read the frame contents from the buffer
+2474    ///
+2475    /// Should only be called when the frame type has been identified as
+2476    /// [`FrameType::RemoveAddress`].
+2477    pub(crate) fn read<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+2478        Ok(Self {
+2479            seq_no: bytes.get()?,
+2480        })
+2481    }
+2482
+2483    const fn get_type(&self) -> FrameType {
+2484        FrameType::RemoveAddress
+2485    }
+2486}
+2487
+2488impl Encodable for RemoveAddress {
+2489    fn encode<W: BufMut>(&self, buf: &mut W) {
+2490        buf.write(Self::TYPE);
+2491        buf.write(self.seq_no);
+2492    }
+2493}
+2494
+2495/// Helper struct for display implementations.
+2496// NOTE: Due to lifetimes in fmt::Arguments it's not possible to make this a simple function that
+2497// avoids allocations.
+2498struct DisplayOption<T: Display> {
+2499    field_name: &'static str,
+2500    op: Option<T>,
+2501}
+2502
+2503impl<T: Display> DisplayOption<T> {
+2504    fn new(field_name: &'static str, op: Option<T>) -> Self {
+2505        Self { field_name, op }
+2506    }
+2507}
+2508
+2509impl<T: Display> Display for DisplayOption<T> {
+2510    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+2511        if let Some(x) = self.op.as_ref() {
+2512            write!(f, "{}: {x}", self.field_name)
+2513        } else {
+2514            fmt::Result::Ok(())
+2515        }
+2516    }
+2517}
+2518
+2519#[cfg(test)]
+2520mod test {
+2521    use super::*;
+2522    use crate::coding::Encodable;
+2523    use assert_matches::assert_matches;
+2524
+2525    #[test]
+2526    fn frame_type() {
+2527        assert_eq!(
+2528            FrameType::try_from(FrameType::Padding.to_u64()),
+2529            Ok(FrameType::Padding),
+2530        );
+2531
+2532        assert_eq!(
+2533            FrameType::try_from(FrameType::Datagram(DatagramInfo(0x30)).to_u64()),
+2534            Ok(FrameType::Datagram(DatagramInfo(0x30))),
+2535        );
+2536
+2537        assert_eq!(
+2538            FrameType::try_from(FrameType::Stream(StreamInfo(0x08)).to_u64()),
+2539            Ok(FrameType::Stream(StreamInfo(0x08))),
+2540        );
+2541    }
+2542
+2543    #[track_caller]
+2544    fn frames(buf: Vec<u8>) -> Vec<Frame> {
+2545        Iter::new(Bytes::from(buf))
+2546            .unwrap()
+2547            .collect::<Result<Vec<_>, _>>()
+2548            .unwrap()
+2549    }
+2550
+2551    #[test]
+2552    fn ack_coding() {
+2553        const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14];
+2554        let mut ranges = ArrayRangeSet::new();
+2555        for &packet in PACKETS {
+2556            ranges.insert(packet..packet + 1);
+2557        }
+2558        let mut buf = Vec::new();
+2559        const ECN: EcnCounts = EcnCounts {
+2560            ect0: 42,
+2561            ect1: 24,
+2562            ce: 12,
+2563        };
+2564        Ack::encoder(42, &ranges, Some(&ECN)).encode(&mut buf);
+2565        let frames = frames(buf);
+2566        assert_eq!(frames.len(), 1);
+2567        match frames[0] {
+2568            Frame::Ack(ref ack) => {
+2569                let mut packets = ack.iter().flatten().collect::<Vec<_>>();
+2570                packets.sort_unstable();
+2571                assert_eq!(&packets[..], PACKETS);
+2572                assert_eq!(ack.ecn, Some(ECN));
+2573            }
+2574            ref x => panic!("incorrect frame {x:?}"),
+2575        }
+2576    }
+2577
+2578    #[test]
+2579    #[allow(clippy::range_plus_one)]
+2580    fn path_ack_coding_with_ecn() {
+2581        const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14];
+2582        let mut ranges = ArrayRangeSet::new();
+2583        for &packet in PACKETS {
+2584            ranges.insert(packet..packet + 1);
+2585        }
+2586        let mut buf = Vec::new();
+2587        const ECN: EcnCounts = EcnCounts {
+2588            ect0: 42,
+2589            ect1: 24,
+2590            ce: 12,
+2591        };
+2592        const PATH_ID: PathId = PathId::MAX;
+2593        PathAck::encoder(PATH_ID, 42, &ranges, Some(&ECN)).encode(&mut buf);
+2594        let frames = frames(buf);
+2595        assert_eq!(frames.len(), 1);
+2596        match frames[0] {
+2597            Frame::PathAck(ref ack) => {
+2598                assert_eq!(ack.path_id, PATH_ID);
+2599                let mut packets = ack.ranges.iter().flatten().collect::<Vec<_>>();
+2600                packets.sort_unstable();
+2601                assert_eq!(&packets[..], PACKETS);
+2602                assert_eq!(ack.ecn, Some(ECN));
+2603            }
+2604            ref x => panic!("incorrect frame {x:?}"),
+2605        }
+2606    }
+2607
+2608    #[test]
+2609    #[allow(clippy::range_plus_one)]
+2610    fn path_ack_coding_no_ecn() {
+2611        const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14];
+2612        let mut ranges = ArrayRangeSet::new();
+2613        for &packet in PACKETS {
+2614            ranges.insert(packet..packet + 1);
+2615        }
+2616        let mut buf = Vec::new();
+2617        const PATH_ID: PathId = PathId::MAX;
+2618        PathAck::encoder(PATH_ID, 42, &ranges, None).encode(&mut buf);
+2619        let frames = frames(buf);
+2620        assert_eq!(frames.len(), 1);
+2621        match frames[0] {
+2622            Frame::PathAck(ref ack) => {
+2623                assert_eq!(ack.path_id, PATH_ID);
+2624                let mut packets = ack.ranges.iter().flatten().collect::<Vec<_>>();
+2625                packets.sort_unstable();
+2626                assert_eq!(&packets[..], PACKETS);
+2627                assert_eq!(ack.ecn, None);
+2628            }
+2629            ref x => panic!("incorrect frame {x:?}"),
+2630        }
+2631    }
+2632
+2633    #[test]
+2634    fn ack_frequency_coding() {
+2635        let mut buf = Vec::new();
+2636        let original = AckFrequency {
+2637            sequence: VarInt(42),
+2638            ack_eliciting_threshold: VarInt(20),
+2639            request_max_ack_delay: VarInt(50_000),
+2640            reordering_threshold: VarInt(1),
+2641        };
+2642        original.encode(&mut buf);
+2643        let frames = frames(buf);
+2644        assert_eq!(frames.len(), 1);
+2645        match &frames[0] {
+2646            Frame::AckFrequency(decoded) => assert_eq!(decoded, &original),
+2647            x => panic!("incorrect frame {x:?}"),
+2648        }
+2649    }
+2650
+2651    #[test]
+2652    fn immediate_ack_coding() {
+2653        let mut buf = Vec::new();
+2654        FrameType::ImmediateAck.encode(&mut buf);
+2655        let frames = frames(buf);
+2656        assert_eq!(frames.len(), 1);
+2657        assert_matches!(&frames[0], Frame::ImmediateAck);
+2658    }
+2659
+2660    /// Test that encoding and decoding [`ObservedAddr`] produces the same result.
+2661    #[test]
+2662    fn test_observed_addr_roundrip() {
+2663        let observed_addr = ObservedAddr {
+2664            seq_no: VarInt(42),
+2665            ip: std::net::Ipv4Addr::LOCALHOST.into(),
+2666            port: 4242,
+2667        };
+2668        let mut buf = Vec::with_capacity(observed_addr.size());
+2669        observed_addr.encode(&mut buf);
+2670
+2671        assert_eq!(
+2672            observed_addr.size(),
+2673            buf.len(),
+2674            "expected written bytes and actual size differ"
+2675        );
+2676
+2677        let mut decoded = frames(buf);
+2678        assert_eq!(decoded.len(), 1);
+2679        match decoded.pop().expect("non empty") {
+2680            Frame::ObservedAddr(decoded) => assert_eq!(decoded, observed_addr),
+2681            x => panic!("incorrect frame {x:?}"),
+2682        }
+2683    }
+2684
+2685    #[test]
+2686    fn test_path_abandon_roundtrip() {
+2687        let abandon = PathAbandon {
+2688            path_id: PathId(42),
+2689            error_code: TransportErrorCode::NO_ERROR,
+2690        };
+2691        let mut buf = Vec::new();
+2692        abandon.encode(&mut buf);
+2693
+2694        let mut decoded = frames(buf);
+2695        assert_eq!(decoded.len(), 1);
+2696        match decoded.pop().expect("non empty") {
+2697            Frame::PathAbandon(decoded) => assert_eq!(decoded, abandon),
+2698            x => panic!("incorrect frame {x:?}"),
+2699        }
+2700    }
+2701
+2702    #[test]
+2703    fn test_path_status_available_roundtrip() {
+2704        let path_status_available = PathStatusAvailable {
+2705            path_id: PathId(42),
+2706            status_seq_no: VarInt(73),
+2707        };
+2708        let mut buf = Vec::new();
+2709        path_status_available.encode(&mut buf);
+2710
+2711        let mut decoded = frames(buf);
+2712        assert_eq!(decoded.len(), 1);
+2713        match decoded.pop().expect("non empty") {
+2714            Frame::PathStatusAvailable(decoded) => assert_eq!(decoded, path_status_available),
+2715            x => panic!("incorrect frame {x:?}"),
+2716        }
+2717    }
+2718
+2719    #[test]
+2720    fn test_path_status_backup_roundtrip() {
+2721        let path_status_backup = PathStatusBackup {
+2722            path_id: PathId(42),
+2723            status_seq_no: VarInt(73),
+2724        };
+2725        let mut buf = Vec::new();
+2726        path_status_backup.encode(&mut buf);
+2727
+2728        let mut decoded = frames(buf);
+2729        assert_eq!(decoded.len(), 1);
+2730        match decoded.pop().expect("non empty") {
+2731            Frame::PathStatusBackup(decoded) => assert_eq!(decoded, path_status_backup),
+2732            x => panic!("incorrect frame {x:?}"),
+2733        }
+2734    }
+2735
+2736    #[test]
+2737    fn test_path_new_connection_id_roundtrip() {
+2738        let cid = NewConnectionId {
+2739            path_id: Some(PathId(22)),
+2740            sequence: 31,
+2741            retire_prior_to: 13,
+2742            id: ConnectionId::new(&[0xAB; 8]),
+2743            reset_token: ResetToken::from([0xCD; crate::RESET_TOKEN_SIZE]),
+2744        };
+2745        let mut buf = Vec::new();
+2746        cid.encode(&mut buf);
+2747
+2748        let mut decoded = frames(buf);
+2749        assert_eq!(decoded.len(), 1);
+2750        match decoded.pop().expect("non empty") {
+2751            Frame::NewConnectionId(decoded) => assert_eq!(decoded, cid),
+2752            x => panic!("incorrect frame {x:?}"),
+2753        }
+2754    }
+2755
+2756    #[test]
+2757    fn test_path_retire_connection_id_roundtrip() {
+2758        let retire_cid = RetireConnectionId {
+2759            path_id: Some(PathId(22)),
+2760            sequence: 31,
+2761        };
+2762        let mut buf = Vec::new();
+2763        retire_cid.encode(&mut buf);
+2764
+2765        let mut decoded = frames(buf);
+2766        assert_eq!(decoded.len(), 1);
+2767        match decoded.pop().expect("non empty") {
+2768            Frame::RetireConnectionId(decoded) => assert_eq!(decoded, retire_cid),
+2769            x => panic!("incorrect frame {x:?}"),
+2770        }
+2771    }
+2772
+2773    #[test]
+2774    fn test_paths_blocked_path_cids_blocked_roundtrip() {
+2775        let mut buf = Vec::new();
+2776
+2777        let frame0 = PathsBlocked(PathId(22));
+2778        frame0.encode(&mut buf);
+2779        let frame1 = PathCidsBlocked {
+2780            path_id: PathId(23),
+2781            next_seq: VarInt(32),
+2782        };
+2783        frame1.encode(&mut buf);
+2784
+2785        let mut decoded = frames(buf);
+2786        assert_eq!(decoded.len(), 2);
+2787        match decoded.pop().expect("non empty") {
+2788            Frame::PathCidsBlocked(decoded) => assert_eq!(decoded, frame1),
+2789            x => panic!("incorrect frame {x:?}"),
+2790        }
+2791        match decoded.pop().expect("non empty") {
+2792            Frame::PathsBlocked(decoded) => assert_eq!(decoded, frame0),
+2793            x => panic!("incorrect frame {x:?}"),
+2794        }
+2795    }
+2796
+2797    /// Test that encoding and decoding [`AddAddress`] produces the same result
+2798    #[test]
+2799    fn test_add_address_roundrip() {
+2800        let add_address = AddAddress {
+2801            seq_no: VarInt(42),
+2802            ip: std::net::Ipv4Addr::LOCALHOST.into(),
+2803            port: 4242,
+2804        };
+2805        let mut buf = Vec::with_capacity(add_address.size());
+2806        add_address.encode(&mut buf);
+2807
+2808        assert_eq!(
+2809            add_address.size(),
+2810            buf.len(),
+2811            "expected written bytes and actual size differ"
+2812        );
+2813
+2814        let mut decoded = frames(buf);
+2815        assert_eq!(decoded.len(), 1);
+2816        match decoded.pop().expect("non empty") {
+2817            Frame::AddAddress(decoded) => assert_eq!(decoded, add_address),
+2818            x => panic!("incorrect frame {x:?}"),
+2819        }
+2820    }
+2821
+2822    /// Test that encoding and decoding [`AddAddress`] produces the same result
+2823    #[test]
+2824    fn test_reach_out_roundrip() {
+2825        let reach_out = ReachOut {
+2826            round: VarInt(42),
+2827            ip: std::net::Ipv6Addr::LOCALHOST.into(),
+2828            port: 4242,
+2829        };
+2830        let mut buf = Vec::with_capacity(reach_out.size());
+2831        reach_out.encode(&mut buf);
+2832
+2833        assert_eq!(
+2834            reach_out.size(),
+2835            buf.len(),
+2836            "expected written bytes and actual size differ"
+2837        );
+2838
+2839        let mut decoded = frames(buf);
+2840        assert_eq!(decoded.len(), 1);
+2841        match decoded.pop().expect("non empty") {
+2842            Frame::ReachOut(decoded) => assert_eq!(decoded, reach_out),
+2843            x => panic!("incorrect frame {x:?}"),
+2844        }
+2845    }
+2846
+2847    /// Test that encoding and decoding [`RemoveAddress`] produces the same result
+2848    #[test]
+2849    fn test_remove_address_roundrip() {
+2850        let remove_addr = RemoveAddress::new(VarInt(10));
+2851        let mut buf = Vec::with_capacity(remove_addr.size());
+2852        remove_addr.encode(&mut buf);
+2853
+2854        assert_eq!(
+2855            remove_addr.size(),
+2856            buf.len(),
+2857            "expected written bytes and actual size differ"
+2858        );
+2859
+2860        let mut decoded = frames(buf);
+2861        assert_eq!(decoded.len(), 1);
+2862        match decoded.pop().expect("non empty") {
+2863            Frame::RemoveAddress(decoded) => assert_eq!(decoded, remove_addr),
+2864            x => panic!("incorrect frame {x:?}"),
+2865        }
+2866    }
+2867}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/lib.rs.html b/pr/672/docs/src/noq_proto/lib.rs.html new file mode 100644 index 000000000..fc22197d7 --- /dev/null +++ b/pr/672/docs/src/noq_proto/lib.rs.html @@ -0,0 +1,484 @@ +lib.rs - source

noq_proto/
lib.rs

1//! Low-level protocol logic for the QUIC protoocol
+2//!
+3//! noq-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 noq API instead.
+6//!
+7//! The noq-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, feature = "rustls", any(feature = "aws-lc-rs", feature = "ring")))]
+33mod tests;
+34pub mod transport_parameters;
+35mod varint;
+36
+37pub use varint::{VarInt, VarIntBoundsExceeded};
+38
+39#[cfg(feature = "bloom")]
+40mod bloom_token_log;
+41#[cfg(feature = "bloom")]
+42pub use bloom_token_log::BloomTokenLog;
+43
+44pub(crate) mod connection;
+45pub use crate::connection::{
+46    Chunk, Chunks, ClosePathError, ClosedPath, ClosedStream, Connection, ConnectionError,
+47    ConnectionStats, Datagrams, Event, FinishError, FrameStats, MultipathNotNegotiated,
+48    NetworkChangeHint, PathAbandonReason, PathError, PathEvent, PathId, PathStats, PathStatus,
+49    ReadError, ReadableError, RecvStream, RttEstimator, SendDatagramError, SendStream,
+50    SetPathStatusError, ShouldTransmit, StreamEvent, Streams, UdpStats, WriteError,
+51};
+52#[cfg(test)]
+53use test_strategy::Arbitrary;
+54
+55#[cfg(feature = "rustls")]
+56pub use rustls;
+57
+58mod config;
+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::{
+70    ApplicationClose, ConnectionClose, Datagram, DatagramInfo, FrameType, InvalidFrameId,
+71    MaybeFrame, StreamInfo,
+72};
+73use crate::{
+74    coding::{Decodable, Encodable},
+75    frame::Frame,
+76};
+77
+78mod endpoint;
+79pub use crate::endpoint::{
+80    AcceptError, ConnectError, ConnectionHandle, DatagramEvent, DecryptedInitial, Endpoint,
+81    Incoming, IncomingAlpns, RetryError,
+82};
+83
+84mod packet;
+85pub use packet::{
+86    ConnectionIdParser, FixedLengthConnectionIdParser, LongType, PacketDecodeError, PartialDecode,
+87    ProtectedHeader, ProtectedInitialHeader,
+88};
+89
+90mod shared;
+91pub use crate::shared::{ConnectionEvent, ConnectionId, EcnCodepoint, EndpointEvent};
+92
+93mod transport_error;
+94pub use crate::transport_error::{Code as TransportErrorCode, Error as TransportError};
+95
+96pub mod congestion;
+97
+98mod cid_generator;
+99pub use crate::cid_generator::{
+100    ConnectionIdGenerator, HashedConnectionIdGenerator, InvalidCid, RandomConnectionIdGenerator,
+101};
+102
+103mod token;
+104use token::ResetToken;
+105pub use token::{NoneTokenLog, NoneTokenStore, TokenLog, TokenReuseError, TokenStore};
+106
+107mod address_discovery;
+108
+109mod token_memory_cache;
+110pub use token_memory_cache::TokenMemoryCache;
+111
+112pub mod n0_nat_traversal;
+113
+114// Deal with time
+115#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
+116pub(crate) use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
+117#[cfg(all(target_family = "wasm", target_os = "unknown"))]
+118pub(crate) use web_time::{Duration, Instant, SystemTime, UNIX_EPOCH};
+119
+120#[cfg(feature = "bench")]
+121pub mod bench_exports {
+122    //! Exports for benchmarks
+123    pub use crate::connection::send_buffer::send_buffer_benches;
+124}
+125
+126#[cfg(fuzzing)]
+127pub mod fuzzing {
+128    pub use crate::connection::{Retransmits, State as ConnectionState, StreamsState};
+129    pub use crate::frame::ResetStream;
+130    pub use crate::packet::PartialDecode;
+131    pub use crate::transport_parameters::TransportParameters;
+132    pub use bytes::{BufMut, BytesMut};
+133
+134    #[cfg(feature = "arbitrary")]
+135    use arbitrary::{Arbitrary, Result, Unstructured};
+136
+137    #[cfg(feature = "arbitrary")]
+138    impl<'arbitrary> Arbitrary<'arbitrary> for TransportParameters {
+139        fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self> {
+140            Ok(Self {
+141                initial_max_streams_bidi: u.arbitrary()?,
+142                initial_max_streams_uni: u.arbitrary()?,
+143                ack_delay_exponent: u.arbitrary()?,
+144                max_udp_payload_size: u.arbitrary()?,
+145                ..Self::default()
+146            })
+147        }
+148    }
+149
+150    #[derive(Debug)]
+151    pub struct PacketParams {
+152        pub local_cid_len: usize,
+153        pub buf: BytesMut,
+154        pub grease_quic_bit: bool,
+155    }
+156
+157    #[cfg(feature = "arbitrary")]
+158    impl<'arbitrary> Arbitrary<'arbitrary> for PacketParams {
+159        fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self> {
+160            let local_cid_len: usize = u.int_in_range(0..=crate::MAX_CID_SIZE)?;
+161            let bytes: Vec<u8> = Vec::arbitrary(u)?;
+162            let mut buf = BytesMut::new();
+163            buf.put_slice(&bytes[..]);
+164            Ok(Self {
+165                local_cid_len,
+166                buf,
+167                grease_quic_bit: bool::arbitrary(u)?,
+168            })
+169        }
+170    }
+171}
+172
+173/// The QUIC protocol version implemented.
+174pub const DEFAULT_SUPPORTED_VERSIONS: &[u32] = &[
+175    0x00000001,
+176    0xff00_001d,
+177    0xff00_001e,
+178    0xff00_001f,
+179    0xff00_0020,
+180    0xff00_0021,
+181    0xff00_0022,
+182];
+183
+184/// Whether an endpoint was the initiator of a connection
+185#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
+186#[cfg_attr(test, derive(Arbitrary))]
+187#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+188pub enum Side {
+189    /// The initiator of a connection
+190    Client = 0,
+191    /// The acceptor of a connection
+192    Server = 1,
+193}
+194
+195impl Side {
+196    #[inline]
+197    /// Shorthand for `self == Side::Client`
+198    pub fn is_client(self) -> bool {
+199        self == Self::Client
+200    }
+201
+202    #[inline]
+203    /// Shorthand for `self == Side::Server`
+204    pub fn is_server(self) -> bool {
+205        self == Self::Server
+206    }
+207}
+208
+209impl ops::Not for Side {
+210    type Output = Self;
+211    fn not(self) -> Self {
+212        match self {
+213            Self::Client => Self::Server,
+214            Self::Server => Self::Client,
+215        }
+216    }
+217}
+218
+219/// Whether a stream communicates data in both directions or only from the initiator
+220#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
+221#[cfg_attr(test, derive(Arbitrary))]
+222#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+223pub enum Dir {
+224    /// Data flows in both directions
+225    Bi = 0,
+226    /// Data flows only from the stream's initiator
+227    Uni = 1,
+228}
+229
+230impl Dir {
+231    fn iter() -> impl Iterator<Item = Self> {
+232        [Self::Bi, Self::Uni].iter().cloned()
+233    }
+234}
+235
+236impl fmt::Display for Dir {
+237    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+238        use Dir::*;
+239        f.pad(match *self {
+240            Bi => "bidirectional",
+241            Uni => "unidirectional",
+242        })
+243    }
+244}
+245
+246/// Identifier for a stream within a particular connection
+247#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+248#[cfg_attr(test, derive(Arbitrary))]
+249pub struct StreamId(#[cfg_attr(test, strategy(crate::varint::varint_u64()))] u64);
+250
+251impl fmt::Display for StreamId {
+252    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+253        let initiator = match self.initiator() {
+254            Side::Client => "client",
+255            Side::Server => "server",
+256        };
+257        let dir = match self.dir() {
+258            Dir::Uni => "uni",
+259            Dir::Bi => "bi",
+260        };
+261        write!(
+262            f,
+263            "{} {}directional stream {}",
+264            initiator,
+265            dir,
+266            self.index()
+267        )
+268    }
+269}
+270
+271impl StreamId {
+272    /// Create a new StreamId
+273    pub fn new(initiator: Side, dir: Dir, index: u64) -> Self {
+274        Self((index << 2) | ((dir as u64) << 1) | initiator as u64)
+275    }
+276    /// Which side of a connection initiated the stream
+277    pub fn initiator(self) -> Side {
+278        if self.0 & 0x1 == 0 {
+279            Side::Client
+280        } else {
+281            Side::Server
+282        }
+283    }
+284    /// Which directions data flows in
+285    pub fn dir(self) -> Dir {
+286        if self.0 & 0x2 == 0 { Dir::Bi } else { Dir::Uni }
+287    }
+288    /// Distinguishes streams of the same initiator and directionality
+289    pub fn index(self) -> u64 {
+290        self.0 >> 2
+291    }
+292}
+293
+294impl From<StreamId> for VarInt {
+295    fn from(x: StreamId) -> Self {
+296        unsafe { Self::from_u64_unchecked(x.0) }
+297    }
+298}
+299
+300impl From<VarInt> for StreamId {
+301    fn from(v: VarInt) -> Self {
+302        Self(v.0)
+303    }
+304}
+305
+306impl From<StreamId> for u64 {
+307    fn from(x: StreamId) -> Self {
+308        x.0
+309    }
+310}
+311
+312impl Decodable for StreamId {
+313    fn decode<B: bytes::Buf>(buf: &mut B) -> coding::Result<Self> {
+314        VarInt::decode(buf).map(|x| Self(x.into_inner()))
+315    }
+316}
+317
+318impl Encodable for StreamId {
+319    fn encode<B: bytes::BufMut>(&self, buf: &mut B) {
+320        VarInt::from_u64(self.0).unwrap().encode(buf);
+321    }
+322}
+323
+324#[cfg(feature = "arbitrary")]
+325impl<'arbitrary> arbitrary::Arbitrary<'arbitrary> for StreamId {
+326    fn arbitrary(u: &mut arbitrary::Unstructured<'arbitrary>) -> arbitrary::Result<Self> {
+327        Ok(VarInt::arbitrary(u)?.into())
+328    }
+329}
+330
+331/// An outgoing packet
+332#[derive(Debug)]
+333#[must_use]
+334pub struct Transmit {
+335    /// The socket this datagram should be sent to
+336    pub destination: SocketAddr,
+337    /// Explicit congestion notification bits to set on the packet
+338    pub ecn: Option<EcnCodepoint>,
+339    /// Amount of data written to the caller-supplied buffer
+340    pub size: usize,
+341    /// The segment size if this transmission contains multiple datagrams.
+342    /// This is `None` if the transmit only contains a single datagram
+343    pub segment_size: Option<usize>,
+344    /// Optional source IP address for the datagram
+345    pub src_ip: Option<IpAddr>,
+346}
+347
+348//
+349// Useful internal constants
+350//
+351
+352/// The maximum number of CIDs we bother to issue per path
+353const LOCAL_CID_COUNT: u64 = 12;
+354const RESET_TOKEN_SIZE: usize = 16;
+355const MAX_CID_SIZE: usize = 20;
+356const MIN_INITIAL_SIZE: u16 = 1200;
+357/// <https://www.rfc-editor.org/rfc/rfc9000.html#name-datagram-size>
+358const INITIAL_MTU: u16 = 1200;
+359const MAX_UDP_PAYLOAD: u16 = 65527;
+360const TIMER_GRANULARITY: Duration = Duration::from_millis(1);
+361/// Maximum number of streams that can be uniquely identified by a stream ID
+362const MAX_STREAM_COUNT: u64 = 1 << 60;
+363
+364/// Identifies a network path by the combination of remote and local addresses
+365///
+366/// Including the local ensures good behavior when the host has multiple IP addresses on the same
+367/// subnet and zero-length connection IDs are in use or when multipath is enabled and multiple
+368/// paths exist with the same remote, but different local IP interfaces.
+369///
+370/// `FourTuple` implements `From<SocketAddr>`, which expands to [`Self::from_remote`].
+371#[derive(Hash, Eq, PartialEq, Copy, Clone)]
+372pub struct FourTuple {
+373    /// The remote side of this tuple.
+374    remote: SocketAddr,
+375    /// The local side of this tuple.
+376    ///
+377    /// The socket is irrelevant for our intents and purposes:
+378    /// When we send, we can only specify the `src_ip`, not the source port.
+379    /// So even if we track the port, we won't be able to make use of it.
+380    local_ip: Option<IpAddr>,
+381}
+382
+383impl FourTuple {
+384    /// Creates a new [`FourTuple`].
+385    pub fn new(mut remote: SocketAddr, local_ip: Option<IpAddr>) -> Self {
+386        if let SocketAddr::V6(socket_addr) = &mut remote {
+387            // RFC3493 §3.3
+388            // > (…) applications should set this field to zero when constructing a sockaddr_in6,
+389            // > and ignore this field in a sockaddr_in6 structure constructed by the system.
+390            //
+391            // This is cleared so that comparisons of remotes are guaranteed to be meaningful: two
+392            // socket addresses with the same contents should not differ if only the flow label
+393            // differs
+394            socket_addr.set_flowinfo(0);
+395
+396            // NOTE: not all multicast addresses require a scope. Use `Ipv6Addr::multicast_scope`
+397            // when stabilized (<https://github.com/rust-lang/rust/issues/27709>)
+398            let requires_scope_id =
+399                socket_addr.ip().is_unicast_link_local() || socket_addr.ip().is_multicast();
+400            if !requires_scope_id {
+401                // Keep the scope id only when it might be relevant. This ensure network paths can
+402                // be compared meaningfully while keeping it when it's important (mainly link local
+403                // addresses)
+404                socket_addr.set_scope_id(0);
+405            }
+406        }
+407
+408        Self { remote, local_ip }
+409    }
+410
+411    /// Creates a new [`FourTuple`] without a known local address.
+412    pub fn from_remote(remote: SocketAddr) -> Self {
+413        Self::new(remote, None)
+414    }
+415
+416    /// Returns the remote address of the network path.
+417    pub fn remote(&self) -> SocketAddr {
+418        self.remote
+419    }
+420
+421    /// Returns the local address of the network path.
+422    pub fn local_ip(&self) -> Option<IpAddr> {
+423        self.local_ip
+424    }
+425
+426    /// Returns whether we think the other address probably represents the same path
+427    /// as ours.
+428    ///
+429    /// If we have a local IP set, then we're exact and only match if the 4-tuples are
+430    /// exactly equal.
+431    /// If we don't have a local IP set, then we only check the remote addresses for equality.
+432    ///
+433    /// Note that because of this, the following calls might differ:
+434    /// - `a.is_probably_same_path(b)`
+435    /// - `b.is_probably_same_path(a)`
+436    pub(crate) fn is_probably_same_path(&self, other: &Self) -> bool {
+437        self.remote == other.remote && (self.local_ip.is_none() || self.local_ip == other.local_ip)
+438    }
+439
+440    /// Updates this tuple's local address iff
+441    /// - it was unset before,
+442    /// - the other tuple has the same remote, and
+443    /// - the other tuple has a local address set.
+444    ///
+445    /// Returns whether this and the other remote are now fully equal.
+446    pub(crate) fn update_local_if_same_remote(&mut self, other: &Self) -> bool {
+447        if self.remote != other.remote {
+448            return false;
+449        }
+450        if self.local_ip.is_some() && self.local_ip != other.local_ip {
+451            return false;
+452        }
+453        self.local_ip = other.local_ip;
+454        true
+455    }
+456}
+457
+458impl fmt::Display for FourTuple {
+459    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+460        f.write_str("(local: ")?;
+461        if let Some(local_ip) = &self.local_ip {
+462            local_ip.fmt(f)?;
+463            f.write_str(", ")?;
+464        } else {
+465            f.write_str("<unspecified>, ")?;
+466        }
+467        f.write_str("remote: ")?;
+468        self.remote.fmt(f)?;
+469        f.write_str(")")
+470    }
+471}
+472
+473impl fmt::Debug for FourTuple {
+474    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+475        fmt::Display::fmt(&self, f)
+476    }
+477}
+478
+479/// Converts a [`SocketAddr`] to a [`FourTuple`] via [`FourTuple::from_remote`].
+480impl From<SocketAddr> for FourTuple {
+481    fn from(value: SocketAddr) -> Self {
+482        Self::from_remote(value)
+483    }
+484}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/n0_nat_traversal.rs.html b/pr/672/docs/src/noq_proto/n0_nat_traversal.rs.html new file mode 100644 index 000000000..4d2434121 --- /dev/null +++ b/pr/672/docs/src/noq_proto/n0_nat_traversal.rs.html @@ -0,0 +1,1075 @@ +n0_nat_traversal.rs - source

noq_proto/
n0_nat_traversal.rs

1//! n0's (<https://n0.computer>) NAT Traversal protocol implementation.
+2
+3use std::{
+4    collections::hash_map::Entry,
+5    fmt::Display,
+6    net::{IpAddr, SocketAddr},
+7    time::Duration,
+8};
+9
+10use rustc_hash::{FxHashMap, FxHashSet};
+11use tracing::{debug, trace};
+12
+13use crate::{
+14    FourTuple, Side, VarInt,
+15    connection::spaces::PendingReachOutFrames,
+16    frame::{AddAddress, ReachOut, RemoveAddress},
+17};
+18
+19/// Maximum number of times we send a NAT probe to the same remote address in a round.
+20///
+21/// This is a trade-off between several factors:
+22/// - Probe packets could be lost. This allows recovery.
+23/// - We may need two probes to reach the NAT firewall to get through.
+24/// - We may be sending probes to innocent bystanders on the internet.
+25/// - A round never "finishes": probing of remotes only stops when:
+26///   1. A new round is started.
+27///   2. A probe was successful.
+28///   3. This number of attempts is exhausted.
+29///
+30/// See [`State::retry_delay`] for the capped exponential backoff used. With this we send
+31/// probes for up to 4s by default.
+32pub(crate) const MAX_NAT_PROBE_ATTEMPTS: u8 = 9;
+33
+34/// An IP & port.
+35///
+36/// Invariant: This value should always be in the ip family that the local
+37/// socket operates in.
+38/// E.g. if the local socket is ipv4, then all `IpPort`s should only have
+39/// IPv4 addresses, and if the socket supports ipv6, then all `IpPort`s
+40/// should be IPv6 addresses or IPv6-mapped IPv4 addresses.
+41///
+42/// See also [`map_to_local_socket_family`], which powers this conversion.
+43type IpPort = (IpAddr, u16);
+44
+45/// An IP & port in canonical form.
+46///
+47/// Avoids using ipv6-mapped ipv4 addresses.
+48/// This is the primary type used to send ip addresses around remotely
+49/// and the primary type used to canonicalize received addresses.
+50#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+51pub(crate) struct CanonicalIpPort {
+52    canonical_ip: IpAddr,
+53    port: u16,
+54}
+55
+56impl CanonicalIpPort {
+57    pub(crate) fn ip(&self) -> IpAddr {
+58        self.canonical_ip
+59    }
+60
+61    pub(crate) fn port(&self) -> u16 {
+62        self.port
+63    }
+64
+65    /// Converts this into a local-socket-family-mapped IP & port.
+66    ///
+67    /// Instead of using ipv4 and ipv6 addresses, this tries to match `ipv6`, which
+68    /// should indicate whether the local socket supports ipv6 or not.
+69    ///
+70    /// If ipv6 is supported, all ipv4 addresses are mapped using ipv6-mapped ipv4
+71    /// addresses.
+72    /// If ipv6 is not supported, then this returns `None` for ipv6 addresses.
+73    ///
+74    /// See also [`map_to_local_socket_family`].
+75    pub(crate) fn as_local_socket_family(&self, ipv6: bool) -> Option<IpPort> {
+76        Some((
+77            map_to_local_socket_family(self.canonical_ip, ipv6)?,
+78            self.port,
+79        ))
+80    }
+81
+82    /// Returns this address as-is with the canonical IP used in a `SocketAddr`.
+83    pub(crate) fn as_canonical_addr(&self) -> SocketAddr {
+84        SocketAddr::new(self.canonical_ip, self.port)
+85    }
+86}
+87
+88impl Display for CanonicalIpPort {
+89    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+90        self.as_canonical_addr().fmt(f)
+91    }
+92}
+93
+94impl From<SocketAddr> for CanonicalIpPort {
+95    fn from(addr: SocketAddr) -> Self {
+96        Self {
+97            canonical_ip: addr.ip().to_canonical(),
+98            port: addr.port(),
+99        }
+100    }
+101}
+102
+103impl From<IpPort> for CanonicalIpPort {
+104    fn from((ip, port): IpPort) -> Self {
+105        Self {
+106            canonical_ip: ip.to_canonical(),
+107            port,
+108        }
+109    }
+110}
+111
+112/// Errors that the nat traversal state might encounter.
+113#[derive(Debug, thiserror::Error)]
+114pub enum Error {
+115    /// An endpoint (local or remote) tried to add too many addresses to their advertised set
+116    #[error("Tried to add too many addresses to their advertised set")]
+117    TooManyAddresses,
+118    /// The operation is not allowed for this endpoint's connection side
+119    #[error("Not allowed for this endpoint's connection side")]
+120    WrongConnectionSide,
+121    /// The extension was not negotiated
+122    #[error("n0's nat traversal was not negotiated")]
+123    ExtensionNotNegotiated,
+124    /// Not enough addresses to complete the operation
+125    #[error("Not enough addresses")]
+126    NotEnoughAddresses,
+127    /// Nat traversal attempt failed due to a multipath error
+128    #[error("Failed to establish paths {0}")]
+129    Multipath(super::PathError),
+130    /// Attempted to initiate NAT traversal on a closed, or closing connection.
+131    #[error("The connection is already closed")]
+132    Closed,
+133}
+134
+135/// Event emitted when the client receives ADD_ADDRESS or REMOVE_ADDRESS frames.
+136#[derive(Debug, Clone)]
+137pub enum Event {
+138    /// An ADD_ADDRESS frame was received.
+139    AddressAdded(SocketAddr),
+140    /// A REMOVE_ADDRESS frame was received.
+141    AddressRemoved(SocketAddr),
+142}
+143
+144/// State kept for n0's nat traversal
+145#[derive(Debug, Default)]
+146pub(crate) enum State {
+147    #[default]
+148    NotNegotiated,
+149    ClientSide(ClientState),
+150    ServerSide(ServerState),
+151}
+152
+153impl State {
+154    pub(crate) fn new(max_remote_addresses: u8, max_local_addresses: u8, side: Side) -> Self {
+155        match side {
+156            Side::Client => Self::ClientSide(ClientState::new(
+157                max_remote_addresses.into(),
+158                max_local_addresses.into(),
+159            )),
+160            Side::Server => Self::ServerSide(ServerState::new(
+161                max_remote_addresses.into(),
+162                max_local_addresses.into(),
+163            )),
+164        }
+165    }
+166
+167    pub(crate) fn is_negotiated(&self) -> bool {
+168        match self {
+169            Self::NotNegotiated => false,
+170            Self::ClientSide(_) | Self::ServerSide(_) => true,
+171        }
+172    }
+173
+174    pub(crate) fn client_side(&self) -> Result<&ClientState, Error> {
+175        match self {
+176            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+177            Self::ClientSide(client_side) => Ok(client_side),
+178            Self::ServerSide(_) => Err(Error::WrongConnectionSide),
+179        }
+180    }
+181
+182    pub(crate) fn client_side_mut(&mut self) -> Result<&mut ClientState, Error> {
+183        match self {
+184            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+185            Self::ClientSide(client_side) => Ok(client_side),
+186            Self::ServerSide(_) => Err(Error::WrongConnectionSide),
+187        }
+188    }
+189
+190    pub(crate) fn server_side_mut(&mut self) -> Result<&mut ServerState, Error> {
+191        match self {
+192            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+193            Self::ClientSide(_) => Err(Error::WrongConnectionSide),
+194            Self::ServerSide(server_side) => Ok(server_side),
+195        }
+196    }
+197
+198    /// Adds a local address to use for nat traversal.
+199    ///
+200    /// When this endpoint is the server within the connection, these addresses will be sent to the
+201    /// client in add address frames. For clients, these addresses will be sent in reach out frames
+202    /// when nat traversal attempts are initiated.
+203    ///
+204    /// If a frame should be sent, it is returned.
+205    pub(crate) fn add_local_address(
+206        &mut self,
+207        address: SocketAddr,
+208    ) -> Result<Option<AddAddress>, Error> {
+209        match self {
+210            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+211            Self::ClientSide(client_state) => {
+212                client_state.add_local_address(address)?;
+213                Ok(None)
+214            }
+215            Self::ServerSide(server_state) => server_state.add_local_address(address),
+216        }
+217    }
+218
+219    /// Removes a local address from the advertised set for nat traversal.
+220    ///
+221    /// When this endpoint is the server, removed addresses must be reported with remove address
+222    /// frames. Clients will simply stop reporting these addresses in reach out frames.
+223    ///
+224    /// If a frame should be sent, it is returned.
+225    pub(crate) fn remove_local_address(
+226        &mut self,
+227        address: SocketAddr,
+228    ) -> Result<Option<RemoveAddress>, Error> {
+229        let address = IpPort::from((address.ip(), address.port()));
+230        match self {
+231            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+232            Self::ClientSide(client_state) => {
+233                client_state.remove_local_address(&address);
+234                Ok(None)
+235            }
+236            Self::ServerSide(server_state) => Ok(server_state.remove_local_address(&address)),
+237        }
+238    }
+239
+240    pub(crate) fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, Error> {
+241        match self {
+242            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+243            Self::ClientSide(client_state) => Ok(client_state
+244                .local_addresses
+245                .iter()
+246                .map(CanonicalIpPort::as_canonical_addr)
+247                .collect()),
+248            Self::ServerSide(server_state) => Ok(server_state
+249                .local_addresses
+250                .keys()
+251                .map(CanonicalIpPort::as_canonical_addr)
+252                .collect()),
+253        }
+254    }
+255
+256    /// Returns the next ready probe's address.
+257    ///
+258    /// If this is actually sent you must call [`Self::mark_probe_sent`].
+259    pub(crate) fn next_probe_addr(&self) -> Option<IpPort> {
+260        match self {
+261            Self::NotNegotiated => None,
+262            Self::ClientSide(state) => state.next_probe_addr(),
+263            Self::ServerSide(state) => state.next_probe_addr(),
+264        }
+265    }
+266
+267    /// Marks a probe as sent to the address with the challenge.
+268    pub(crate) fn mark_probe_sent(&mut self, remote: IpPort, challenge: u64) {
+269        match self {
+270            Self::NotNegotiated => (),
+271            Self::ClientSide(state) => state.mark_probe_sent(remote, challenge),
+272            Self::ServerSide(state) => state.mark_probe_sent(remote, challenge),
+273        }
+274    }
+275
+276    /// Re-queues probes that have not yet succeeded or reached 0 remaining retries.
+277    ///
+278    /// After calling [`Self::retry_delay`] must be checked.
+279    pub(crate) fn queue_retries(&mut self, ipv6: bool) {
+280        match self {
+281            Self::NotNegotiated => (),
+282            Self::ClientSide(state) => state.queue_retries(ipv6),
+283            Self::ServerSide(state) => state.queue_retries(),
+284        };
+285    }
+286
+287    /// Marks a remote as successful if the response matches a sent probe.
+288    ///
+289    /// Returns true if it was a response to one of the NAT traversal probes and a path
+290    /// needs to be opened. Note that the NAT probes are not padded to 1200 bytes so only
+291    /// the address is validated, but not the entire path.
+292    pub(crate) fn handle_path_response(&mut self, src: FourTuple, challenge: u64) -> bool {
+293        match self {
+294            Self::NotNegotiated => false,
+295            Self::ClientSide(state) => state.handle_path_response(src, challenge),
+296            Self::ServerSide(state) => state.handle_path_response(src, challenge),
+297        }
+298    }
+299
+300    /// Returns the delay to arm the `NatTraversalProbeRetry` timer.
+301    ///
+302    /// `initial_rtt` must be [`TransportConfig::initial_rtt`] so retries are scaled to this
+303    /// value.
+304    ///
+305    /// [`TransportConfig::initial_rtt`]: crate::TransportConfig::initial_rtt
+306    pub(crate) fn retry_delay(&self, initial_rtt: Duration) -> Option<Duration> {
+307        match self {
+308            Self::NotNegotiated => return None,
+309            Self::ClientSide(state) => {
+310                if !state
+311                    .remote_addresses
+312                    .values()
+313                    .any(|(_, probes)| probes.remaining() > 0)
+314                {
+315                    return None;
+316                }
+317            }
+318            Self::ServerSide(state) => {
+319                if !state.remotes.values().any(|probes| probes.remaining() > 0) {
+320                    return None;
+321                }
+322            }
+323        }
+324
+325        let attempt = match self {
+326            Self::NotNegotiated => return None,
+327            Self::ClientSide(state) => state.attempt,
+328            Self::ServerSide(state) => state.attempt,
+329        };
+330
+331        // Retries follow at an exponential backoff, capped at max 2s interval. The base
+332        // delay is initial_rtt/10, which for the default value means 33.3ms. Just under
+333        // 10_000 km at the speed of light.
+334        const MAX_BACKOFF_EXPONENT: u8 = 8;
+335        const MAX_INTERVAL: Duration = Duration::from_secs(2);
+336        let base = initial_rtt / 10;
+337        let attempt = attempt.min(MAX_BACKOFF_EXPONENT) as u32;
+338        let interval = match attempt {
+339            0 => base * 2u32.pow(attempt),
+340            _ => base * 2u32.pow(attempt) - base * 2u32.pow(attempt - 1),
+341        };
+342        Some(interval.min(MAX_INTERVAL))
+343    }
+344}
+345
+346#[derive(Debug)]
+347pub(crate) struct ClientState {
+348    /// Max number of remote addresses we allow
+349    ///
+350    /// This is set by the local endpoint.
+351    max_remote_addresses: usize,
+352    /// Max number of local addresses allowed
+353    ///
+354    /// This is set by the remote endpoint.
+355    max_local_addresses: usize,
+356    /// Candidate addresses the remote endpoint advertises.
+357    ///
+358    /// These are addresses on which the server is potentially reachable, to use for NAT
+359    /// traversal attempts.
+360    ///
+361    /// They are indexed by their ADD_ADDRESS sequence id and stored in **canonical
+362    /// form**. Not in the socket-native form as usual. This because we need to store them
+363    /// so we have the correct sequence IDs.
+364    remote_addresses: FxHashMap<VarInt, (CanonicalIpPort, ProbeState)>,
+365    /// Candidate addresses for the local endpoint.
+366    ///
+367    /// These are addresses on which we are potentially reachable, to use for NAT traversal
+368    /// attempts.
+369    ///
+370    /// They are stored in **canonical form**, not in socket-native form as usual. We may
+371    /// nave a reflexive address that is IPv6 even if our local socket can only handle IPv4.
+372    local_addresses: FxHashSet<CanonicalIpPort>,
+373    /// Current nat traversal round.
+374    round: VarInt,
+375    /// The probing attempt in the round.
+376    ///
+377    /// Probes are sent to all remotes at the same time in a round, at intervals from
+378    /// [`State::retry_delay`]. This is the number of times probes have been sent.
+379    attempt: u8,
+380    /// The data of PATH_CHALLENGE frames sent in probes.
+381    ///
+382    /// These are cleared when a new round starts, so any late-arriving PATH_RESPONSEs will
+383    /// have no effect.
+384    ///
+385    /// They are stored in the usual socket-native form.
+386    sent_challenges: FxHashMap<u64, IpPort>,
+387    /// Queued probes to be sent in the next [`poll_transmit`] call.
+388    ///
+389    /// [`poll_transmit`]: crate::connection::Connection::poll_transmit
+390    ///
+391    /// They are stored in the usual socket-native form. Probes to address families not
+392    /// addressable by the family are never inserted.
+393    pending_probes: FxHashSet<IpPort>,
+394    /// Network paths that were successfully probed but not yet opened.
+395    ///
+396    /// When we do not have enough CIDs or free path IDs we might not have been able to open
+397    /// a new path. This allows us to try re-open the path when we get new CIDs or a new
+398    /// MAX_PATH_ID.
+399    // TODO(flub): perhaps there should be a time-limit on these?
+400    paths_to_be_opened: Vec<FourTuple>,
+401}
+402
+403impl ClientState {
+404    fn new(max_remote_addresses: usize, max_local_addresses: usize) -> Self {
+405        Self {
+406            max_remote_addresses,
+407            max_local_addresses,
+408            remote_addresses: Default::default(),
+409            local_addresses: Default::default(),
+410            round: Default::default(),
+411            attempt: 0,
+412            sent_challenges: Default::default(),
+413            pending_probes: Default::default(),
+414            paths_to_be_opened: Default::default(),
+415        }
+416    }
+417
+418    fn add_local_address(&mut self, address: SocketAddr) -> Result<(), Error> {
+419        let address = CanonicalIpPort::from(address);
+420        if self.local_addresses.len() < self.max_local_addresses {
+421            self.local_addresses.insert(address);
+422            Ok(())
+423        } else if self.local_addresses.contains(&address) {
+424            // at capacity, but the address is known, no issues here
+425            Ok(())
+426        } else {
+427            // at capacity and the address is new
+428            Err(Error::TooManyAddresses)
+429        }
+430    }
+431
+432    fn remove_local_address(&mut self, address: &IpPort) {
+433        let address = CanonicalIpPort::from(*address);
+434        self.local_addresses.remove(&address);
+435    }
+436
+437    /// Initiates a new nat traversal round.
+438    ///
+439    /// A nat traversal round involves advertising the client's local addresses in
+440    /// `REACH_OUT` frames, and initiating probing of the known remote addresses. When a new
+441    /// round is initiated, the previous one is cancelled.
+442    ///
+443    /// `ipv6` indicates if the connection runs on a socket that supports IPv6. If so, then
+444    /// all addresses returned [`PendingReachOutFrames`] will be IPv6 addresses (and
+445    /// IPv4-mapped IPv6 addresses if necessary). Otherwise they're all IPv4 addresses.  See
+446    /// also [`map_to_local_socket_family`].
+447    ///
+448    /// # Returns
+449    ///
+450    /// The REACH_OUT frames that need to be sent to the peer and the probed addresses. The
+451    /// probed addresses are only informational, the pending probes are stored in
+452    /// [`Self::pending_probes`].
+453    ///
+454    /// If the probed addresses are non-empty the `NatTraversalProbeRetry` timer needs to be
+455    /// set.
+456    pub(crate) fn initiate_nat_traversal_round(
+457        &mut self,
+458        ipv6: bool,
+459    ) -> Result<(PendingReachOutFrames, Vec<SocketAddr>), Error> {
+460        if self.local_addresses.is_empty() {
+461            return Err(Error::NotEnoughAddresses);
+462        }
+463
+464        self.round = self.round.saturating_add(1u8);
+465        self.attempt = 0;
+466        self.sent_challenges.clear();
+467        self.pending_probes.clear();
+468
+469        // Enqueue the NAT probes to known remote addresses.
+470        self.remote_addresses
+471            .values_mut()
+472            .for_each(|(ip_port, state)| {
+473                if let Some(ip_port) = ip_port.as_local_socket_family(ipv6) {
+474                    self.pending_probes.insert(ip_port);
+475                    *state = ProbeState::Active(MAX_NAT_PROBE_ATTEMPTS - 1);
+476                } else {
+477                    trace!(%ip_port, "not using IPv6 NAT candidate for IPv4 socket");
+478                    *state = ProbeState::Active(0);
+479                }
+480            });
+481        let probed_addrs: Vec<SocketAddr> = self
+482            .pending_probes
+483            .iter()
+484            .copied()
+485            .map(Into::into)
+486            .collect();
+487
+488        // Build the REACH_OUT frames.
+489        let reach_out_frames: PendingReachOutFrames = self
+490            .local_addresses
+491            .iter()
+492            .map(|ip_port| ReachOut {
+493                round: self.round,
+494                ip: ip_port.ip(),
+495                port: ip_port.port(),
+496            })
+497            .collect();
+498
+499        trace!(
+500            round = %self.round,
+501            reach_out = %reach_out_frames.len(),
+502            to_probe = %self.pending_probes.len(),
+503            "initiating NAT traversal round",
+504        );
+505        Ok((reach_out_frames, probed_addrs))
+506    }
+507
+508    /// Re-queues probes that have not yet succeeded or reached 0 remaining retries.
+509    ///
+510    /// Returns whether any probes are now queued to send. In this case the
+511    /// `NatTraversalProbeRetry` timer needs to be reset.
+512    ///
+513    /// `ipv6` as for [`Self::initiate_nat_traversal_round`].
+514    pub(crate) fn queue_retries(&mut self, ipv6: bool) {
+515        self.attempt += 1;
+516        self.remote_addresses
+517            .values_mut()
+518            .for_each(|(ip_port, state)| match state {
+519                ProbeState::Active(remaining) if *remaining > 0 => {
+520                    *remaining -= 1;
+521                    if let Some(ip_port) = ip_port.as_local_socket_family(ipv6) {
+522                        self.pending_probes.insert(ip_port);
+523                    } else {
+524                        trace!(%ip_port, "skipping IPv6 NAT candidate for IPv4 socket");
+525                        *remaining = 0;
+526                    }
+527                }
+528                ProbeState::Active(_) | ProbeState::Succeeded => {}
+529            });
+530    }
+531
+532    /// Returns the next ready probe's address.
+533    ///
+534    /// If this is actually sent you must call [`Self::mark_probe_sent`].
+535    fn next_probe_addr(&self) -> Option<IpPort> {
+536        self.pending_probes.iter().next().copied()
+537    }
+538
+539    /// Marks a probe as sent to the address with the challenge.
+540    fn mark_probe_sent(&mut self, remote: IpPort, challenge: u64) {
+541        self.pending_probes.remove(&remote);
+542        self.sent_challenges.insert(challenge, remote);
+543    }
+544
+545    /// Adds an address to the remote set.
+546    ///
+547    /// On success returns the address if it was new to the set. It will error when the set
+548    /// has no capacity for the address.
+549    ///
+550    /// If this is called while a round is in progress this will effectively add the address
+551    /// to the current round. There is no guarantee however that the current round is still
+552    /// in progress however, if the last [`Self::queue_retries`] call returned `false` the
+553    /// round has stopped.
+554    // TODO(flub): probably should add an event to signal that the round is finished, so
+555    //    that the application knows to start a new round.
+556    pub(crate) fn add_remote_address(
+557        &mut self,
+558        add_addr: AddAddress,
+559    ) -> Result<Option<SocketAddr>, Error> {
+560        let AddAddress { seq_no, ip, port } = add_addr;
+561        let address = CanonicalIpPort::from((ip, port));
+562        let allow_new = self.remote_addresses.len() < self.max_remote_addresses;
+563        match self.remote_addresses.entry(seq_no) {
+564            Entry::Occupied(mut occupied_entry) => {
+565                let is_update = occupied_entry.get().0 != address;
+566                if is_update {
+567                    occupied_entry.insert((address, ProbeState::Active(MAX_NAT_PROBE_ATTEMPTS)));
+568                }
+569                // The value might be different. This should not happen, but we assume that the new
+570                // address is more recent than the previous, and thus worth updating
+571                Ok(is_update.then_some(address.as_canonical_addr()))
+572            }
+573            Entry::Vacant(vacant_entry) if allow_new => {
+574                vacant_entry.insert((address, ProbeState::Active(MAX_NAT_PROBE_ATTEMPTS)));
+575                Ok(Some(address.as_canonical_addr()))
+576            }
+577            _ => Err(Error::TooManyAddresses),
+578        }
+579    }
+580
+581    /// Removes an address from the remote set.
+582    ///
+583    /// Returns whether the address was present.
+584    pub(crate) fn remove_remote_address(
+585        &mut self,
+586        remove_addr: RemoveAddress,
+587    ) -> Option<SocketAddr> {
+588        self.remote_addresses
+589            .remove(&remove_addr.seq_no)
+590            .map(|(address, _)| address.as_canonical_addr())
+591    }
+592
+593    /// Checks that a received remote address is valid.
+594    ///
+595    /// An address is valid as long as it does not change the value of a known address id.
+596    pub(crate) fn check_remote_address(&self, add_addr: &AddAddress) -> bool {
+597        match self.remote_addresses.get(&add_addr.seq_no) {
+598            None => true,
+599            Some((existing, _)) => *existing == CanonicalIpPort::from(add_addr.ip_port()),
+600        }
+601    }
+602
+603    pub(crate) fn get_remote_nat_traversal_addresses(&self) -> Vec<SocketAddr> {
+604        self.remote_addresses
+605            .values()
+606            .map(|(address, _)| (*address).as_canonical_addr())
+607            .collect()
+608    }
+609
+610    /// Marks a remote as successful if the response matches a sent probe.
+611    ///
+612    /// Returns `true` if it was a response to one of the NAT traversal probes. In that case
+613    /// [`Self::pop_pending_path_open`] should be called to open the next path.
+614    fn handle_path_response(&mut self, network_path: FourTuple, challenge: u64) -> bool {
+615        if let Entry::Occupied(entry) = self.sent_challenges.entry(challenge) {
+616            let remote = (network_path.remote().ip(), network_path.remote().port());
+617            if *entry.get() == remote {
+618                entry.remove();
+619                trace!(
+620                    ?network_path,
+621                    challenge = %display(format_args!("0x{challenge:x}")),
+622                    "Received valid NAT traversal probe response",
+623                );
+624                self.paths_to_be_opened.push(network_path);
+625
+626                // TODO: linear search is sad.
+627                let remote = CanonicalIpPort::from(remote);
+628                if let Some(seq) = self
+629                    .remote_addresses
+630                    .iter()
+631                    .filter_map(
+632                        |(seq, (addr, _))| {
+633                            if *addr == remote { Some(*seq) } else { None }
+634                        },
+635                    )
+636                    .next()
+637                {
+638                    // Stop probing this remote address.
+639                    self.remote_addresses
+640                        .insert(seq, (remote, ProbeState::Succeeded));
+641                } else {
+642                    // Nothing to stop probing, the remote was only challenged because a
+643                    // PATH_RESPONSE was being sent to it. These are not retried locally
+644                    // since the peer is responsible for retrying the challenges until it
+645                    // receives a response, at which time the local challenge is delivered.
+646                    trace!("probe opened un-advertised address, peer likely behind DEDN");
+647                }
+648                return true;
+649            } else {
+650                debug!(
+651                    ?network_path.remote,
+652                    expected_remote = ?entry.get(),
+653                    challenge = %display(format_args!("0x{challenge:x}")),
+654                    "PATH_RESPONSE matched a NAT traversal probe but mismatching addr",
+655                )
+656            }
+657        }
+658        false
+659    }
+660
+661    /// Returns a path that was NAT traversed and needs to be opened.
+662    pub(crate) fn pop_pending_path_open(&mut self) -> Option<FourTuple> {
+663        self.paths_to_be_opened.pop()
+664    }
+665
+666    /// Put back a path that needs to be opened, e.g. for a temporary failure.
+667    pub(crate) fn push_pending_path_open(&mut self, network_path: FourTuple) {
+668        self.paths_to_be_opened.push(network_path)
+669    }
+670}
+671
+672/// State of an off-path NAT traversal probe to a remote address.
+673#[derive(Debug)]
+674enum ProbeState {
+675    /// The remote still needs to be probed in this round.
+676    ///
+677    /// The remaining number of retries are stored in the `u8`.
+678    Active(u8),
+679    /// We received a probe response for this remote.
+680    Succeeded,
+681}
+682
+683impl ProbeState {
+684    /// Returns the remaining number of probes to try for this remote.
+685    fn remaining(&self) -> u8 {
+686        match self {
+687            Self::Active(remaining) => *remaining,
+688            Self::Succeeded => 0,
+689        }
+690    }
+691}
+692
+693#[derive(Debug)]
+694pub(crate) struct ServerState {
+695    /// Max number of remote addresses we allow.
+696    ///
+697    /// This is set by the local endpoint.
+698    max_remote_addresses: usize,
+699    /// Max number of local addresses allowed.
+700    ///
+701    /// This is set by the remote endpoint.
+702    max_local_addresses: usize,
+703    /// Candidate addresses the server reports as potentially reachable, to use for nat
+704    /// traversal attempts.
+705    ///
+706    /// They are stored in **canonical form**, not in socket-native form as usual. We may
+707    /// nave a reflexive address that is IPv6 even if our local socket can only handle IPv4.
+708    local_addresses: FxHashMap<CanonicalIpPort, VarInt>,
+709    /// The next id to use for local addresses sent to the client.
+710    next_local_addr_id: VarInt,
+711    /// Current nat traversal round
+712    ///
+713    /// Servers keep track of the client's most recent round and cancel probing related to previous
+714    /// rounds.
+715    round: VarInt,
+716    /// The probing attempt in the round.
+717    ///
+718    /// Probes are sent to all remotes at the same time in a round, at intervals from
+719    /// [`State::retry_delay`]. This is the number of times probes have been sent.
+720    attempt: u8,
+721    /// The remote addresses participating in this round.
+722    ///
+723    /// The set is cleared when a new round starts.
+724    ///
+725    /// These are stored in the usual local-socket native form.
+726    remotes: FxHashMap<IpPort, ProbeState>,
+727    /// The data of PATH_CHALLENGE frames sent in probes.
+728    ///
+729    /// These are cleared when a new round starts, so any late-arriving PATH_RESPONSEs will
+730    /// have no effect.
+731    sent_challenges: FxHashMap<u64, IpPort>,
+732    /// Queued probes to be sent in the next [`poll_transmit`] call.
+733    ///
+734    /// At the beginning of a round this is populated from REACH_OUT frames and at every
+735    /// retry this is populated from [`Self::remotes`].
+736    ///
+737    /// [`poll_transmit`]: crate::connection::Connection::poll_transmit
+738    pending_probes: FxHashSet<IpPort>,
+739}
+740
+741impl ServerState {
+742    fn new(max_remote_addresses: usize, max_local_addresses: usize) -> Self {
+743        Self {
+744            max_remote_addresses,
+745            max_local_addresses,
+746            local_addresses: Default::default(),
+747            next_local_addr_id: Default::default(),
+748            round: Default::default(),
+749            attempt: 0,
+750            remotes: Default::default(),
+751            sent_challenges: Default::default(),
+752            pending_probes: Default::default(),
+753        }
+754    }
+755
+756    fn add_local_address(&mut self, address: SocketAddr) -> Result<Option<AddAddress>, Error> {
+757        let address = CanonicalIpPort::from(address);
+758        let allow_new = self.local_addresses.len() < self.max_local_addresses;
+759        match self.local_addresses.entry(address) {
+760            Entry::Occupied(_) => Ok(None),
+761            Entry::Vacant(vacant_entry) if allow_new => {
+762                let id = self.next_local_addr_id;
+763                self.next_local_addr_id = self.next_local_addr_id.saturating_add(1u8);
+764                vacant_entry.insert(id);
+765                Ok(Some(AddAddress::new((address.ip(), address.port()), id)))
+766            }
+767            _ => Err(Error::TooManyAddresses),
+768        }
+769    }
+770
+771    fn remove_local_address(&mut self, address: &IpPort) -> Option<RemoveAddress> {
+772        let address = CanonicalIpPort::from(*address);
+773        self.local_addresses
+774            .remove(&address)
+775            .map(RemoveAddress::new)
+776    }
+777
+778    /// Returns the current NAT traversal round number.
+779    pub(crate) fn current_round(&self) -> VarInt {
+780        self.round
+781    }
+782
+783    /// Handles a received REACH_OUT frame.
+784    ///
+785    /// This might ignore the reach out frame if it belongs to an older round or if the
+786    /// frame contains an IPv6 address while the local socket is IPv4-only.
+787    ///
+788    /// If a new round was started, the `NatTraversalProbeRetry` timer needs to be reset.
+789    pub(crate) fn handle_reach_out(
+790        &mut self,
+791        reach_out: ReachOut,
+792        ipv6: bool,
+793    ) -> Result<(), Error> {
+794        let ReachOut { round, ip, port } = reach_out;
+795
+796        if round < self.round {
+797            trace!(current_round=%self.round, "ignoring REACH_OUT for previous round");
+798            return Ok(());
+799        }
+800        let Some(ip) = map_to_local_socket_family(ip, ipv6) else {
+801            trace!("Ignoring IPv6 REACH_OUT frame due to not supporting IPv6 locally");
+802            return Ok(());
+803        };
+804
+805        if round > self.round {
+806            self.round = round;
+807            self.attempt = 0;
+808            self.remotes.clear();
+809            self.sent_challenges.clear();
+810            self.pending_probes.clear();
+811        } else if self.remotes.contains_key(&(ip, port)) {
+812            // Retransmitted frame.
+813            return Ok(());
+814        } else if self.remotes.len() >= self.max_remote_addresses {
+815            return Err(Error::TooManyAddresses);
+816        }
+817        self.remotes
+818            .entry((ip, port))
+819            .or_insert(ProbeState::Active(MAX_NAT_PROBE_ATTEMPTS - 1));
+820        self.pending_probes.insert((ip, port));
+821        Ok(())
+822    }
+823
+824    /// Re-queues probes that have not yet succeeded or reached [`MAX_NAT_PROBE_ATTEMPTS`].
+825    ///
+826    /// Returns whether any probes are now queued to send. In this case the
+827    /// `NatTraversalProbeRetry` timer needs to be reset.
+828    pub(crate) fn queue_retries(&mut self) {
+829        self.attempt += 1;
+830        self.remotes
+831            .iter_mut()
+832            .for_each(|(remote, state)| match state {
+833                ProbeState::Active(remaining) if *remaining > 0 => {
+834                    *remaining -= 1;
+835                    self.pending_probes.insert(*remote);
+836                }
+837                ProbeState::Active(_) | ProbeState::Succeeded => (),
+838            });
+839    }
+840
+841    /// Returns the next ready probe's address.
+842    ///
+843    /// If this is actually sent you must call [`Self::mark_probe_sent`].
+844    fn next_probe_addr(&self) -> Option<IpPort> {
+845        self.pending_probes.iter().next().cloned()
+846    }
+847
+848    /// Marks a probe as sent to the address with the challenge.
+849    fn mark_probe_sent(&mut self, remote: IpPort, challenge: u64) {
+850        self.pending_probes.remove(&remote);
+851        self.sent_challenges.insert(challenge, remote);
+852    }
+853
+854    /// Marks a remote as successful if the response matches a sent probe.
+855    ///
+856    /// Returns `true` if it was a response to one of the NAT traversal probes.
+857    fn handle_path_response(&mut self, src: FourTuple, challenge: u64) -> bool {
+858        if let Entry::Occupied(entry) = self.sent_challenges.entry(challenge) {
+859            let remote = (src.remote().ip(), src.remote().port());
+860            if *entry.get() == remote {
+861                entry.remove();
+862                self.remotes.insert(remote, ProbeState::Succeeded);
+863                return true;
+864            } else {
+865                debug!(
+866                    ?challenge,
+867                    ?src.remote,
+868                    "PATH_RESPONSE matched a NAT traversal probe but mismatching addr",
+869                )
+870            }
+871        }
+872        false
+873    }
+874}
+875
+876/// Returns the given address as canonicalized IP address.
+877///
+878/// This checks that the address family is supported by our local socket.
+879/// If it is supported, then the address is mapped to the respective IP address.
+880/// If the given address is an IPv6 address, but our local socket doesn't support
+881/// IPv6, then this returns `None`.
+882pub(crate) fn map_to_local_socket_family(address: IpAddr, ipv6: bool) -> Option<IpAddr> {
+883    let ip = match address {
+884        IpAddr::V4(addr) if ipv6 => IpAddr::V6(addr.to_ipv6_mapped()),
+885        IpAddr::V4(_) => address,
+886        IpAddr::V6(_) if ipv6 => address,
+887        IpAddr::V6(addr) => IpAddr::V4(addr.to_ipv4_mapped()?),
+888    };
+889    Some(ip)
+890}
+891
+892#[cfg(test)]
+893mod tests {
+894    use testresult::TestResult;
+895
+896    use super::*;
+897
+898    #[test]
+899    fn test_basic_server_state() {
+900        let mut state = ServerState::new(2, 2);
+901
+902        state
+903            .handle_reach_out(
+904                ReachOut {
+905                    round: 1u32.into(),
+906                    ip: std::net::Ipv4Addr::LOCALHOST.into(),
+907                    port: 1,
+908                },
+909                true,
+910            )
+911            .unwrap();
+912
+913        state
+914            .handle_reach_out(
+915                ReachOut {
+916                    round: 1u32.into(),
+917                    ip: "1.1.1.1".parse().unwrap(), //std::net::Ipv4Addr::LOCALHOST.into(),
+918                    port: 2,
+919                },
+920                true,
+921            )
+922            .unwrap();
+923
+924        dbg!(&state);
+925        assert_eq!(state.pending_probes.len(), 2);
+926
+927        // Helper: send next ready probe
+928        let mut challenge = 0;
+929        let mut send_probe = |state: &mut ServerState| {
+930            let remote = state.next_probe_addr().unwrap();
+931            challenge += 1;
+932            state.mark_probe_sent(remote, challenge);
+933        };
+934
+935        send_probe(&mut state);
+936        send_probe(&mut state);
+937
+938        // After sending both probes, no ready probes remain but they're still tracked.
+939        assert!(state.next_probe_addr().is_none());
+940
+941        // After queuing retries, probes become available again
+942        state.queue_retries();
+943        send_probe(&mut state);
+944        send_probe(&mut state);
+945
+946        // After 2 attempts each, retries still available (max is 10)
+947        state.queue_retries();
+948        send_probe(&mut state);
+949        send_probe(&mut state);
+950
+951        // Exhaust remaining attempts
+952        for _ in 3..MAX_NAT_PROBE_ATTEMPTS {
+953            state.queue_retries();
+954            send_probe(&mut state);
+955            send_probe(&mut state);
+956        }
+957
+958        // After max attempts, probes are removed
+959        state.queue_retries();
+960        assert!(state.next_probe_addr().is_none());
+961    }
+962
+963    #[test]
+964    fn test_map_to_local_socket() {
+965        assert_eq!(
+966            map_to_local_socket_family("1.1.1.1".parse().unwrap(), false),
+967            Some("1.1.1.1".parse().unwrap())
+968        );
+969        assert_eq!(
+970            map_to_local_socket_family("1.1.1.1".parse().unwrap(), true),
+971            Some("::ffff:1.1.1.1".parse().unwrap())
+972        );
+973        assert_eq!(
+974            map_to_local_socket_family("::1".parse().unwrap(), true),
+975            Some("::1".parse().unwrap())
+976        );
+977        assert_eq!(
+978            map_to_local_socket_family("::1".parse().unwrap(), false),
+979            None
+980        );
+981        assert_eq!(
+982            map_to_local_socket_family("::ffff:1.1.1.1".parse().unwrap(), false),
+983            Some("1.1.1.1".parse().unwrap())
+984        )
+985    }
+986
+987    #[test]
+988    fn test_retry_delay_server_ipv6() -> TestResult {
+989        let initial_rtt = Duration::from_millis(333);
+990        let ipv6 = true;
+991        let remote = SocketAddr::from(("::2".parse::<IpAddr>()?, 2));
+992        let remote_ipp = (remote.ip(), remote.port());
+993
+994        let mut nat = State::new(8, 8, Side::Server);
+995
+996        nat.server_side_mut()?.handle_reach_out(
+997            ReachOut {
+998                round: 1u8.into(),
+999                ip: remote.ip(),
+1000                port: remote.port(),
+1001            },
+1002            ipv6,
+1003        )?;
+1004
+1005        let challenges = [1u64, 2, 3, 4, 5, 6, 7];
+1006        let delays = [
+1007            33_300u64, 66_600, 133_200, 266_400, 532_800, 1_065_600, 2_000_000,
+1008        ];
+1009        for (challenge, delay) in challenges.into_iter().zip(delays) {
+1010            nat.queue_retries(ipv6);
+1011            assert_eq!(nat.next_probe_addr(), Some(remote_ipp));
+1012            nat.mark_probe_sent(remote_ipp, challenge);
+1013            assert_eq!(
+1014                nat.retry_delay(initial_rtt),
+1015                Some(Duration::from_micros(delay)),
+1016                "challenge: {challenge}"
+1017            );
+1018        }
+1019
+1020        assert!(nat.handle_path_response(
+1021            FourTuple {
+1022                remote,
+1023                local_ip: Some("::3".parse::<IpAddr>()?),
+1024            },
+1025            challenges[6]
+1026        ));
+1027        assert_eq!(nat.retry_delay(initial_rtt), None);
+1028
+1029        Ok(())
+1030    }
+1031
+1032    #[test]
+1033    fn test_retry_delay_client_ipv6() -> TestResult {
+1034        let initial_rtt = Duration::from_millis(333);
+1035        let ipv6 = true;
+1036        let remote = SocketAddr::from(("::2".parse::<IpAddr>()?, 2));
+1037        let remote_ipp = (remote.ip(), remote.port());
+1038        let local_addr = SocketAddr::from(("::3".parse::<IpAddr>()?, 3));
+1039
+1040        let mut nat = State::new(8, 8, Side::Client);
+1041        nat.add_local_address(local_addr)?;
+1042        nat.client_side_mut()?.add_remote_address(AddAddress {
+1043            seq_no: 1u8.into(),
+1044            ip: remote.ip(),
+1045            port: remote.port(),
+1046        })?;
+1047        nat.client_side_mut()?.initiate_nat_traversal_round(ipv6)?;
+1048
+1049        let challenges = [1u64, 2, 3, 4, 5, 6, 7];
+1050        let delays = [
+1051            33_300u64, 66_600, 133_200, 266_400, 532_800, 1_065_600, 2_000_000,
+1052        ];
+1053        for (challenge, delay) in challenges.into_iter().zip(delays) {
+1054            nat.queue_retries(ipv6);
+1055            assert_eq!(nat.next_probe_addr(), Some(remote_ipp));
+1056            nat.mark_probe_sent(remote_ipp, challenge);
+1057            assert_eq!(
+1058                nat.retry_delay(initial_rtt),
+1059                Some(Duration::from_micros(delay)),
+1060                "challenge: {challenge}"
+1061            );
+1062        }
+1063
+1064        assert!(nat.handle_path_response(
+1065            FourTuple {
+1066                remote,
+1067                local_ip: Some(local_addr.ip()),
+1068            },
+1069            challenges[6]
+1070        ));
+1071        assert_eq!(nat.retry_delay(initial_rtt), None);
+1072
+1073        Ok(())
+1074    }
+1075}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/packet.rs.html b/pr/672/docs/src/noq_proto/packet.rs.html new file mode 100644 index 000000000..541cd3277 --- /dev/null +++ b/pr/672/docs/src/noq_proto/packet.rs.html @@ -0,0 +1,1095 @@ +packet.rs - source

noq_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    connection::{EncryptionLevel, SpaceKind},
+10    crypto,
+11};
+12
+13/// Decodes a QUIC packet's invariant header
+14///
+15/// Due to packet number encryption, it is impossible to fully decode a header
+16/// (which includes a variable-length packet number) without crypto context.
+17/// The crypto context (represented by the `Crypto` type in noq) is usually
+18/// part of the `Connection`, or can be derived from the destination CID for
+19/// Initial packets.
+20///
+21/// To cope with this, we decode the invariant header (which should be stable
+22/// across QUIC versions), which gives us the destination CID and allows us
+23/// to inspect the version and packet type (which depends on the version).
+24/// This information allows us to fully decode and decrypt the packet.
+25#[cfg_attr(test, derive(Clone))]
+26#[derive(Debug)]
+27pub struct PartialDecode {
+28    plain_header: ProtectedHeader,
+29    buf: io::Cursor<BytesMut>,
+30}
+31
+32#[allow(clippy::len_without_is_empty)]
+33impl PartialDecode {
+34    /// Begin decoding a QUIC packet from `bytes`, returning any trailing data not part of that packet
+35    pub fn new(
+36        bytes: BytesMut,
+37        cid_parser: &(impl ConnectionIdParser + ?Sized),
+38        supported_versions: &[u32],
+39        grease_quic_bit: bool,
+40    ) -> Result<(Self, Option<BytesMut>), PacketDecodeError> {
+41        let mut buf = io::Cursor::new(bytes);
+42        let plain_header =
+43            ProtectedHeader::decode(&mut buf, cid_parser, supported_versions, grease_quic_bit)?;
+44        let dgram_len = buf.get_ref().len();
+45        let packet_len = plain_header
+46            .payload_len()
+47            .map(|len| (buf.position() + len) as usize)
+48            .unwrap_or(dgram_len);
+49        match dgram_len.cmp(&packet_len) {
+50            Ordering::Equal => Ok((Self { plain_header, buf }, None)),
+51            Ordering::Less => Err(PacketDecodeError::InvalidHeader(
+52                "packet too short to contain payload length",
+53            )),
+54            Ordering::Greater => {
+55                let rest = Some(buf.get_mut().split_off(packet_len));
+56                Ok((Self { plain_header, buf }, rest))
+57            }
+58        }
+59    }
+60
+61    /// The underlying partially-decoded packet data
+62    pub(crate) fn data(&self) -> &[u8] {
+63        self.buf.get_ref()
+64    }
+65
+66    pub(crate) fn initial_header(&self) -> Option<&ProtectedInitialHeader> {
+67        self.plain_header.as_initial()
+68    }
+69
+70    pub(crate) fn has_long_header(&self) -> bool {
+71        !matches!(self.plain_header, ProtectedHeader::Short { .. })
+72    }
+73
+74    pub(crate) fn is_initial(&self) -> bool {
+75        self.encryption_level() == Some(EncryptionLevel::Initial)
+76    }
+77
+78    pub(crate) fn encryption_level(&self) -> Option<EncryptionLevel> {
+79        use ProtectedHeader::*;
+80        match self.plain_header {
+81            Initial { .. } => Some(EncryptionLevel::Initial),
+82            Long { ty, .. } => Some(match ty {
+83                LongType::Handshake => EncryptionLevel::Handshake,
+84                LongType::ZeroRtt => EncryptionLevel::ZeroRtt,
+85            }),
+86            Short { .. } => Some(EncryptionLevel::OneRtt),
+87            _ => None,
+88        }
+89    }
+90
+91    pub(crate) fn is_0rtt(&self) -> bool {
+92        match self.plain_header {
+93            ProtectedHeader::Long { ty, .. } => ty == LongType::ZeroRtt,
+94            _ => false,
+95        }
+96    }
+97
+98    /// The destination connection ID of the packet
+99    pub fn dst_cid(&self) -> ConnectionId {
+100        self.plain_header.dst_cid()
+101    }
+102
+103    /// Length of QUIC packet being decoded
+104    #[allow(unreachable_pub)] // fuzzing only
+105    pub fn len(&self) -> usize {
+106        self.buf.get_ref().len()
+107    }
+108
+109    pub(crate) fn finish(
+110        self,
+111        header_crypto: Option<&dyn crypto::HeaderKey>,
+112    ) -> Result<Packet, PacketDecodeError> {
+113        use ProtectedHeader::*;
+114        let Self {
+115            plain_header,
+116            mut buf,
+117        } = self;
+118
+119        if let Initial(ProtectedInitialHeader {
+120            dst_cid,
+121            src_cid,
+122            token_pos,
+123            version,
+124            ..
+125        }) = plain_header
+126        {
+127            let number = Self::decrypt_header(&mut buf, header_crypto.unwrap())?;
+128            let header_len = buf.position() as usize;
+129            let mut bytes = buf.into_inner();
+130
+131            let header_data = bytes.split_to(header_len).freeze();
+132            let token = header_data.slice(token_pos.start..token_pos.end);
+133            return Ok(Packet {
+134                header: Header::Initial(InitialHeader {
+135                    dst_cid,
+136                    src_cid,
+137                    token,
+138                    number,
+139                    version,
+140                }),
+141                header_data,
+142                payload: bytes,
+143            });
+144        }
+145
+146        let header = match plain_header {
+147            Long {
+148                ty,
+149                dst_cid,
+150                src_cid,
+151                version,
+152                ..
+153            } => Header::Long {
+154                ty,
+155                dst_cid,
+156                src_cid,
+157                number: Self::decrypt_header(&mut buf, header_crypto.unwrap())?,
+158                version,
+159            },
+160            Retry {
+161                dst_cid,
+162                src_cid,
+163                version,
+164            } => Header::Retry {
+165                dst_cid,
+166                src_cid,
+167                version,
+168            },
+169            Short { spin, dst_cid, .. } => {
+170                let number = Self::decrypt_header(&mut buf, header_crypto.unwrap())?;
+171                let key_phase = buf.get_ref()[0] & KEY_PHASE_BIT != 0;
+172                Header::Short {
+173                    spin,
+174                    key_phase,
+175                    dst_cid,
+176                    number,
+177                }
+178            }
+179            VersionNegotiate {
+180                random,
+181                dst_cid,
+182                src_cid,
+183            } => Header::VersionNegotiate {
+184                random,
+185                dst_cid,
+186                src_cid,
+187            },
+188            Initial { .. } => unreachable!(),
+189        };
+190
+191        let header_len = buf.position() as usize;
+192        let mut bytes = buf.into_inner();
+193        Ok(Packet {
+194            header,
+195            header_data: bytes.split_to(header_len).freeze(),
+196            payload: bytes,
+197        })
+198    }
+199
+200    fn decrypt_header(
+201        buf: &mut io::Cursor<BytesMut>,
+202        header_crypto: &dyn crypto::HeaderKey,
+203    ) -> Result<PacketNumber, PacketDecodeError> {
+204        let packet_length = buf.get_ref().len();
+205        let pn_offset = buf.position() as usize;
+206        if packet_length < pn_offset + 4 + header_crypto.sample_size() {
+207            return Err(PacketDecodeError::InvalidHeader(
+208                "packet too short to extract header protection sample",
+209            ));
+210        }
+211
+212        header_crypto.decrypt(pn_offset, buf.get_mut());
+213
+214        let len = PacketNumber::decode_len(buf.get_ref()[0]);
+215        PacketNumber::decode(len, buf)
+216    }
+217}
+218
+219/// A buffer that can tell how much has been written to it already
+220///
+221/// This is commonly used for when a buffer is passed and the user may not write past a
+222/// given size. It allows the user of such a buffer to know the current cursor position in
+223/// the buffer. The maximum write size is usually passed in the same unit as
+224/// [`BufLen::len`]: bytes since the buffer start.
+225pub(crate) trait BufLen {
+226    /// Returns the number of bytes written into the buffer so far
+227    fn len(&self) -> usize;
+228}
+229
+230impl BufLen for Vec<u8> {
+231    fn len(&self) -> usize {
+232        self.len()
+233    }
+234}
+235
+236/// A received packet with header protection removed.
+237// TODO(flub): Would be grand to make this typesafe by adding a generic that indicates
+238//    whether the payload is encrypted or decrypted.
+239pub(crate) struct Packet {
+240    /// The decoded header.
+241    pub(crate) header: Header,
+242    /// The encoded header data, with header protection removed (i.e. decrypted).
+243    pub(crate) header_data: Bytes,
+244    /// Packet payload, still encrypted when created, later decrypted in-place.
+245    pub(crate) payload: BytesMut,
+246}
+247
+248impl Packet {
+249    pub(crate) fn reserved_bits_valid(&self) -> bool {
+250        let mask = match self.header {
+251            Header::Short { .. } => SHORT_RESERVED_BITS,
+252            _ => LONG_RESERVED_BITS,
+253        };
+254        self.header_data[0] & mask == 0
+255    }
+256}
+257
+258pub(crate) struct InitialPacket {
+259    pub(crate) header: InitialHeader,
+260    pub(crate) header_data: Bytes,
+261    pub(crate) payload: BytesMut,
+262}
+263
+264impl From<InitialPacket> for Packet {
+265    fn from(x: InitialPacket) -> Self {
+266        Self {
+267            header: Header::Initial(x.header),
+268            header_data: x.header_data,
+269            payload: x.payload,
+270        }
+271    }
+272}
+273
+274#[cfg_attr(test, derive(Clone))]
+275#[derive(Debug)]
+276pub(crate) enum Header {
+277    Initial(InitialHeader),
+278    Long {
+279        ty: LongType,
+280        dst_cid: ConnectionId,
+281        src_cid: ConnectionId,
+282        number: PacketNumber,
+283        version: u32,
+284    },
+285    Retry {
+286        dst_cid: ConnectionId,
+287        src_cid: ConnectionId,
+288        version: u32,
+289    },
+290    Short {
+291        spin: bool,
+292        key_phase: bool,
+293        dst_cid: ConnectionId,
+294        number: PacketNumber,
+295    },
+296    VersionNegotiate {
+297        random: u8,
+298        src_cid: ConnectionId,
+299        dst_cid: ConnectionId,
+300    },
+301}
+302
+303impl Header {
+304    pub(crate) fn encode(&self, w: &mut (impl BufMut + BufLen)) -> PartialEncode {
+305        use Header::*;
+306        let start = w.len();
+307        match *self {
+308            Initial(InitialHeader {
+309                ref dst_cid,
+310                ref src_cid,
+311                ref token,
+312                number,
+313                version,
+314            }) => {
+315                w.write(u8::from(LongHeaderType::Initial) | number.tag());
+316                w.write(version);
+317                dst_cid.encode_long(w);
+318                src_cid.encode_long(w);
+319                w.write_var(token.len() as u64);
+320                w.put_slice(token);
+321                w.write::<u16>(0); // Placeholder for payload length; see `set_payload_length`
+322                number.encode(w);
+323                PartialEncode {
+324                    start,
+325                    header_len: w.len() - start,
+326                    pn: Some((number.len(), true)),
+327                }
+328            }
+329            Long {
+330                ty,
+331                ref dst_cid,
+332                ref src_cid,
+333                number,
+334                version,
+335            } => {
+336                w.write(u8::from(LongHeaderType::Standard(ty)) | number.tag());
+337                w.write(version);
+338                dst_cid.encode_long(w);
+339                src_cid.encode_long(w);
+340                w.write::<u16>(0); // Placeholder for payload length; see `set_payload_length`
+341                number.encode(w);
+342                PartialEncode {
+343                    start,
+344                    header_len: w.len() - start,
+345                    pn: Some((number.len(), true)),
+346                }
+347            }
+348            Retry {
+349                ref dst_cid,
+350                ref src_cid,
+351                version,
+352            } => {
+353                w.write(u8::from(LongHeaderType::Retry));
+354                w.write(version);
+355                dst_cid.encode_long(w);
+356                src_cid.encode_long(w);
+357                PartialEncode {
+358                    start,
+359                    header_len: w.len() - start,
+360                    pn: None,
+361                }
+362            }
+363            Short {
+364                spin,
+365                key_phase,
+366                ref dst_cid,
+367                number,
+368            } => {
+369                w.write(
+370                    FIXED_BIT
+371                        | if key_phase { KEY_PHASE_BIT } else { 0 }
+372                        | if spin { SPIN_BIT } else { 0 }
+373                        | number.tag(),
+374                );
+375                w.put_slice(dst_cid);
+376                number.encode(w);
+377                PartialEncode {
+378                    start,
+379                    header_len: w.len() - start,
+380                    pn: Some((number.len(), false)),
+381                }
+382            }
+383            VersionNegotiate {
+384                ref random,
+385                ref dst_cid,
+386                ref src_cid,
+387            } => {
+388                w.write(0x80u8 | random);
+389                w.write::<u32>(0);
+390                dst_cid.encode_long(w);
+391                src_cid.encode_long(w);
+392                PartialEncode {
+393                    start,
+394                    header_len: w.len() - start,
+395                    pn: None,
+396                }
+397            }
+398        }
+399    }
+400
+401    /// Whether the packet is encrypted on the wire
+402    pub(crate) fn is_protected(&self) -> bool {
+403        !matches!(*self, Self::Retry { .. } | Self::VersionNegotiate { .. })
+404    }
+405
+406    pub(crate) fn number(&self) -> Option<PacketNumber> {
+407        use Header::*;
+408        Some(match *self {
+409            Initial(InitialHeader { number, .. }) => number,
+410            Long { number, .. } => number,
+411            Short { number, .. } => number,
+412            _ => {
+413                return None;
+414            }
+415        })
+416    }
+417
+418    pub(crate) fn space(&self) -> SpaceKind {
+419        use Header::*;
+420        match *self {
+421            Short { .. } => SpaceKind::Data,
+422            Long {
+423                ty: LongType::ZeroRtt,
+424                ..
+425            } => SpaceKind::Data,
+426            Long {
+427                ty: LongType::Handshake,
+428                ..
+429            } => SpaceKind::Handshake,
+430            _ => SpaceKind::Initial,
+431        }
+432    }
+433
+434    pub(crate) fn key_phase(&self) -> bool {
+435        match *self {
+436            Self::Short { key_phase, .. } => key_phase,
+437            _ => false,
+438        }
+439    }
+440
+441    pub(crate) fn is_short(&self) -> bool {
+442        matches!(*self, Self::Short { .. })
+443    }
+444
+445    pub(crate) fn is_1rtt(&self) -> bool {
+446        self.is_short()
+447    }
+448
+449    pub(crate) fn is_0rtt(&self) -> bool {
+450        matches!(
+451            *self,
+452            Self::Long {
+453                ty: LongType::ZeroRtt,
+454                ..
+455            }
+456        )
+457    }
+458
+459    pub(crate) fn dst_cid(&self) -> ConnectionId {
+460        use Header::*;
+461        match *self {
+462            Initial(InitialHeader { dst_cid, .. }) => dst_cid,
+463            Long { dst_cid, .. } => dst_cid,
+464            Retry { dst_cid, .. } => dst_cid,
+465            Short { dst_cid, .. } => dst_cid,
+466            VersionNegotiate { dst_cid, .. } => dst_cid,
+467        }
+468    }
+469
+470    /// Is this packet allowed to be coalesced with others?
+471    ///
+472    /// Ref <https://www.rfc-editor.org/rfc/rfc9000.html#name-coalescing-packets>
+473    pub(crate) fn can_coalesce(&self) -> bool {
+474        use Header::*;
+475        match *self {
+476            Initial(_) => true,
+477            Long { .. } => true,
+478            Retry { .. } => false,
+479            Short { .. } => false,
+480            VersionNegotiate { .. } => false,
+481        }
+482    }
+483
+484    /// Whether the payload of this packet contains QUIC frames
+485    pub(crate) fn has_frames(&self) -> bool {
+486        use Header::*;
+487        match *self {
+488            Initial(_) => true,
+489            Long { .. } => true,
+490            Retry { .. } => false,
+491            Short { .. } => true,
+492            VersionNegotiate { .. } => false,
+493        }
+494    }
+495
+496    #[cfg(feature = "qlog")]
+497    pub(crate) fn src_cid(&self) -> Option<ConnectionId> {
+498        match self {
+499            Self::Initial(initial_header) => Some(initial_header.src_cid),
+500            Self::Long { src_cid, .. } => Some(*src_cid),
+501            Self::Retry { src_cid, .. } => Some(*src_cid),
+502            Self::Short { .. } => None,
+503            Self::VersionNegotiate { src_cid, .. } => Some(*src_cid),
+504        }
+505    }
+506}
+507
+508pub(crate) struct PartialEncode {
+509    pub(crate) start: usize,
+510    pub(crate) header_len: usize,
+511    // Packet number length, payload length needed
+512    pn: Option<(usize, bool)>,
+513}
+514
+515impl PartialEncode {
+516    pub(crate) fn finish(
+517        self,
+518        buf: &mut [u8],
+519        header_crypto: &dyn crypto::HeaderKey,
+520        crypto: Option<(u64, PathId, &dyn crypto::PacketKey)>,
+521    ) {
+522        let Self { header_len, pn, .. } = self;
+523        let Some((pn_len, write_len)) = pn else {
+524            return;
+525        };
+526
+527        let pn_pos = header_len - pn_len;
+528        if write_len {
+529            let len = buf.len() - header_len + pn_len;
+530            assert!(len < 2usize.pow(14)); // Fits in reserved space
+531            let mut slice = &mut buf[pn_pos - 2..pn_pos];
+532            slice.put_u16(len as u16 | (0b01 << 14));
+533        }
+534
+535        if let Some((packet_number, path_id, crypto)) = crypto {
+536            crypto.encrypt(path_id, packet_number, buf, header_len);
+537        }
+538
+539        debug_assert!(
+540            pn_pos + 4 + header_crypto.sample_size() <= buf.len(),
+541            "packet must be padded to at least {} bytes for header protection sampling",
+542            pn_pos + 4 + header_crypto.sample_size()
+543        );
+544        header_crypto.encrypt(pn_pos, buf);
+545    }
+546
+547    /// Creates a [`PartialEncode`] that has not encoded a header into the buffer.
+548    ///
+549    /// This is used exclusively for testing as such a type is otherwise invalid.
+550    #[cfg(test)]
+551    pub(crate) fn no_header() -> Self {
+552        Self {
+553            start: 0,
+554            header_len: 0,
+555            pn: None,
+556        }
+557    }
+558}
+559
+560/// Plain packet header
+561#[derive(Clone, Debug)]
+562pub enum ProtectedHeader {
+563    /// An Initial packet header
+564    Initial(ProtectedInitialHeader),
+565    /// A Long packet header, as used during the handshake
+566    Long {
+567        /// Type of the Long header packet
+568        ty: LongType,
+569        /// Destination Connection ID
+570        dst_cid: ConnectionId,
+571        /// Source Connection ID
+572        src_cid: ConnectionId,
+573        /// Length of the packet payload
+574        len: u64,
+575        /// QUIC version
+576        version: u32,
+577    },
+578    /// A Retry packet header
+579    Retry {
+580        /// Destination Connection ID
+581        dst_cid: ConnectionId,
+582        /// Source Connection ID
+583        src_cid: ConnectionId,
+584        /// QUIC version
+585        version: u32,
+586    },
+587    /// A short packet header, as used during the data phase
+588    Short {
+589        /// Spin bit
+590        spin: bool,
+591        /// Destination Connection ID
+592        dst_cid: ConnectionId,
+593    },
+594    /// A Version Negotiation packet header
+595    VersionNegotiate {
+596        /// Random value
+597        random: u8,
+598        /// Destination Connection ID
+599        dst_cid: ConnectionId,
+600        /// Source Connection ID
+601        src_cid: ConnectionId,
+602    },
+603}
+604
+605impl ProtectedHeader {
+606    fn as_initial(&self) -> Option<&ProtectedInitialHeader> {
+607        match self {
+608            Self::Initial(x) => Some(x),
+609            _ => None,
+610        }
+611    }
+612
+613    /// The destination Connection ID of the packet
+614    pub fn dst_cid(&self) -> ConnectionId {
+615        use ProtectedHeader::*;
+616        match self {
+617            Initial(header) => header.dst_cid,
+618            &Long { dst_cid, .. } => dst_cid,
+619            &Retry { dst_cid, .. } => dst_cid,
+620            &Short { dst_cid, .. } => dst_cid,
+621            &VersionNegotiate { dst_cid, .. } => dst_cid,
+622        }
+623    }
+624
+625    fn payload_len(&self) -> Option<u64> {
+626        use ProtectedHeader::*;
+627        match self {
+628            Initial(ProtectedInitialHeader { len, .. }) | Long { len, .. } => Some(*len),
+629            _ => None,
+630        }
+631    }
+632
+633    /// Decode a plain header from given buffer, with given [`ConnectionIdParser`].
+634    pub fn decode(
+635        buf: &mut io::Cursor<BytesMut>,
+636        cid_parser: &(impl ConnectionIdParser + ?Sized),
+637        supported_versions: &[u32],
+638        grease_quic_bit: bool,
+639    ) -> Result<Self, PacketDecodeError> {
+640        let first = buf.get::<u8>()?;
+641        if !grease_quic_bit && first & FIXED_BIT == 0 {
+642            return Err(PacketDecodeError::InvalidHeader("fixed bit unset"));
+643        }
+644        if first & LONG_HEADER_FORM == 0 {
+645            let spin = first & SPIN_BIT != 0;
+646
+647            Ok(Self::Short {
+648                spin,
+649                dst_cid: cid_parser.parse(buf)?,
+650            })
+651        } else {
+652            let version = buf.get::<u32>()?;
+653
+654            let dst_cid = ConnectionId::decode_long(buf)
+655                .ok_or(PacketDecodeError::InvalidHeader("malformed cid"))?;
+656            let src_cid = ConnectionId::decode_long(buf)
+657                .ok_or(PacketDecodeError::InvalidHeader("malformed cid"))?;
+658
+659            // TODO: Support long CIDs for compatibility with future QUIC versions
+660            if version == 0 {
+661                let random = first & !LONG_HEADER_FORM;
+662                return Ok(Self::VersionNegotiate {
+663                    random,
+664                    dst_cid,
+665                    src_cid,
+666                });
+667            }
+668
+669            if !supported_versions.contains(&version) {
+670                return Err(PacketDecodeError::UnsupportedVersion {
+671                    src_cid,
+672                    dst_cid,
+673                    version,
+674                });
+675            }
+676
+677            match LongHeaderType::from_byte(first)? {
+678                LongHeaderType::Initial => {
+679                    let token_len = buf.get_var()? as usize;
+680                    let token_start = buf.position() as usize;
+681                    if token_len > buf.remaining() {
+682                        return Err(PacketDecodeError::InvalidHeader("token out of bounds"));
+683                    }
+684                    buf.advance(token_len);
+685
+686                    let len = buf.get_var()?;
+687                    Ok(Self::Initial(ProtectedInitialHeader {
+688                        dst_cid,
+689                        src_cid,
+690                        token_pos: token_start..token_start + token_len,
+691                        len,
+692                        version,
+693                    }))
+694                }
+695                LongHeaderType::Retry => Ok(Self::Retry {
+696                    dst_cid,
+697                    src_cid,
+698                    version,
+699                }),
+700                LongHeaderType::Standard(ty) => Ok(Self::Long {
+701                    ty,
+702                    dst_cid,
+703                    src_cid,
+704                    len: buf.get_var()?,
+705                    version,
+706                }),
+707            }
+708        }
+709    }
+710}
+711
+712/// Header of an Initial packet, before decryption
+713#[derive(Clone, Debug)]
+714pub struct ProtectedInitialHeader {
+715    /// Destination Connection ID
+716    pub dst_cid: ConnectionId,
+717    /// Source Connection ID
+718    pub src_cid: ConnectionId,
+719    /// The position of a token in the packet buffer
+720    pub token_pos: Range<usize>,
+721    /// Length of the packet payload
+722    pub len: u64,
+723    /// QUIC version
+724    pub version: u32,
+725}
+726
+727#[derive(Clone, Debug)]
+728pub(crate) struct InitialHeader {
+729    pub(crate) dst_cid: ConnectionId,
+730    pub(crate) src_cid: ConnectionId,
+731    pub(crate) token: Bytes,
+732    pub(crate) number: PacketNumber,
+733    pub(crate) version: u32,
+734}
+735
+736// An encoded packet number
+737#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+738pub(crate) enum PacketNumber {
+739    U8(u8),
+740    U16(u16),
+741    U24(u32),
+742    U32(u32),
+743}
+744
+745impl PacketNumber {
+746    pub(crate) fn new(n: u64, largest_acked: u64) -> Self {
+747        let range = (n - largest_acked) * 2;
+748        if range < 1 << 8 {
+749            Self::U8(n as u8)
+750        } else if range < 1 << 16 {
+751            Self::U16(n as u16)
+752        } else if range < 1 << 24 {
+753            Self::U24(n as u32)
+754        } else if range < 1 << 32 {
+755            Self::U32(n as u32)
+756        } else {
+757            panic!("packet number too large to encode")
+758        }
+759    }
+760
+761    pub(crate) fn len(self) -> usize {
+762        use PacketNumber::*;
+763        match self {
+764            U8(_) => 1,
+765            U16(_) => 2,
+766            U24(_) => 3,
+767            U32(_) => 4,
+768        }
+769    }
+770
+771    pub(crate) fn encode<W: BufMut>(self, w: &mut W) {
+772        use PacketNumber::*;
+773        match self {
+774            U8(x) => w.write(x),
+775            U16(x) => w.write(x),
+776            U24(x) => w.put_uint(u64::from(x), 3),
+777            U32(x) => w.write(x),
+778        }
+779    }
+780
+781    pub(crate) fn decode<R: Buf>(len: usize, r: &mut R) -> Result<Self, PacketDecodeError> {
+782        use PacketNumber::*;
+783        let pn = match len {
+784            1 => U8(r.get()?),
+785            2 => U16(r.get()?),
+786            3 => U24(r.get_uint(3) as u32),
+787            4 => U32(r.get()?),
+788            _ => unreachable!(),
+789        };
+790        Ok(pn)
+791    }
+792
+793    pub(crate) fn decode_len(tag: u8) -> usize {
+794        1 + (tag & 0x03) as usize
+795    }
+796
+797    fn tag(self) -> u8 {
+798        use PacketNumber::*;
+799        match self {
+800            U8(_) => 0b00,
+801            U16(_) => 0b01,
+802            U24(_) => 0b10,
+803            U32(_) => 0b11,
+804        }
+805    }
+806
+807    pub(crate) fn expand(self, expected: u64) -> u64 {
+808        // From Appendix A
+809        use PacketNumber::*;
+810        let truncated = match self {
+811            U8(x) => u64::from(x),
+812            U16(x) => u64::from(x),
+813            U24(x) => u64::from(x),
+814            U32(x) => u64::from(x),
+815        };
+816        let nbits = self.len() * 8;
+817        let win = 1 << nbits;
+818        let hwin = win / 2;
+819        let mask = win - 1;
+820        // The incoming packet number should be greater than expected - hwin and less than or equal
+821        // to expected + hwin
+822        //
+823        // This means we can't just strip the trailing bits from expected and add the truncated
+824        // because that might yield a value outside the window.
+825        //
+826        // The following code calculates a candidate value and makes sure it's within the packet
+827        // number window.
+828        let candidate = (expected & !mask) | truncated;
+829        if expected.checked_sub(hwin).is_some_and(|x| candidate <= x) {
+830            candidate + win
+831        } else if candidate > expected + hwin && candidate > win {
+832            candidate - win
+833        } else {
+834            candidate
+835        }
+836    }
+837}
+838
+839/// A [`ConnectionIdParser`] implementation that assumes the connection ID is of fixed length
+840pub struct FixedLengthConnectionIdParser {
+841    expected_len: usize,
+842}
+843
+844impl FixedLengthConnectionIdParser {
+845    /// Create a new instance of `FixedLengthConnectionIdParser`
+846    pub fn new(expected_len: usize) -> Self {
+847        Self { expected_len }
+848    }
+849}
+850
+851impl ConnectionIdParser for FixedLengthConnectionIdParser {
+852    fn parse(&self, buffer: &mut dyn Buf) -> Result<ConnectionId, PacketDecodeError> {
+853        (buffer.remaining() >= self.expected_len)
+854            .then(|| ConnectionId::from_buf(buffer, self.expected_len))
+855            .ok_or(PacketDecodeError::InvalidHeader("packet too small"))
+856    }
+857}
+858
+859/// Parse connection id in short header packet
+860pub trait ConnectionIdParser {
+861    /// Parse a connection id from given buffer
+862    fn parse(&self, buf: &mut dyn Buf) -> Result<ConnectionId, PacketDecodeError>;
+863}
+864
+865/// Long packet type including non-uniform cases
+866#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+867pub(crate) enum LongHeaderType {
+868    Initial,
+869    Retry,
+870    Standard(LongType),
+871}
+872
+873impl LongHeaderType {
+874    fn from_byte(b: u8) -> Result<Self, PacketDecodeError> {
+875        use {LongHeaderType::*, LongType::*};
+876        debug_assert!(b & LONG_HEADER_FORM != 0, "not a long packet");
+877        Ok(match (b & 0x30) >> 4 {
+878            0x0 => Initial,
+879            0x1 => Standard(ZeroRtt),
+880            0x2 => Standard(Handshake),
+881            0x3 => Retry,
+882            _ => unreachable!(),
+883        })
+884    }
+885}
+886
+887impl From<LongHeaderType> for u8 {
+888    fn from(ty: LongHeaderType) -> Self {
+889        use {LongHeaderType::*, LongType::*};
+890        match ty {
+891            Initial => LONG_HEADER_FORM | FIXED_BIT,
+892            Standard(ZeroRtt) => LONG_HEADER_FORM | FIXED_BIT | (0x1 << 4),
+893            Standard(Handshake) => LONG_HEADER_FORM | FIXED_BIT | (0x2 << 4),
+894            Retry => LONG_HEADER_FORM | FIXED_BIT | (0x3 << 4),
+895        }
+896    }
+897}
+898
+899/// Long packet types with uniform header structure
+900#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+901pub enum LongType {
+902    /// Handshake packet
+903    Handshake,
+904    /// 0-RTT packet
+905    ZeroRtt,
+906}
+907
+908/// Packet decode error
+909#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+910pub enum PacketDecodeError {
+911    /// Packet uses a QUIC version that is not supported
+912    #[error("unsupported version {version:x}")]
+913    UnsupportedVersion {
+914        /// Source Connection ID
+915        src_cid: ConnectionId,
+916        /// Destination Connection ID
+917        dst_cid: ConnectionId,
+918        /// The version that was unsupported
+919        version: u32,
+920    },
+921    /// The packet header is invalid
+922    #[error("invalid header: {0}")]
+923    InvalidHeader(&'static str),
+924}
+925
+926impl From<coding::UnexpectedEnd> for PacketDecodeError {
+927    fn from(_: coding::UnexpectedEnd) -> Self {
+928        Self::InvalidHeader("unexpected end of packet")
+929    }
+930}
+931
+932pub(crate) const LONG_HEADER_FORM: u8 = 0x80;
+933pub(crate) const FIXED_BIT: u8 = 0x40;
+934pub(crate) const SPIN_BIT: u8 = 0x20;
+935const SHORT_RESERVED_BITS: u8 = 0x18;
+936const LONG_RESERVED_BITS: u8 = 0x0c;
+937const KEY_PHASE_BIT: u8 = 0x04;
+938
+939/// Packet number space identifiers
+940#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+941pub(crate) enum SpaceId {
+942    /// Unprotected packets, used to bootstrap the handshake
+943    Initial = 0,
+944    Handshake = 1,
+945    /// Application data space, used for 0-RTT and post-handshake/1-RTT packets
+946    Data = 2,
+947}
+948
+949impl SpaceId {
+950    pub(crate) fn iter() -> impl Iterator<Item = Self> {
+951        [Self::Initial, Self::Handshake, Self::Data].iter().cloned()
+952    }
+953
+954    /// Returns the next higher packet space.
+955    ///
+956    /// Returns `None` if at  [`SpaceId::Data`].
+957    pub(crate) fn next(&self) -> Option<Self> {
+958        match self {
+959            Self::Initial => Some(Self::Handshake),
+960            Self::Handshake => Some(Self::Data),
+961            Self::Data => None,
+962        }
+963    }
+964
+965    /// Returns the encryption level for this packet space.
+966    pub(crate) fn encryption_level(self) -> EncryptionLevel {
+967        match self {
+968            Self::Initial => EncryptionLevel::Initial,
+969            Self::Handshake => EncryptionLevel::Handshake,
+970            Self::Data => EncryptionLevel::OneRtt,
+971        }
+972    }
+973
+974    /// Returns the [`SpaceKind`] for this packet space.
+975    pub(crate) fn kind(self) -> SpaceKind {
+976        match self {
+977            Self::Initial => SpaceKind::Initial,
+978            Self::Handshake => SpaceKind::Handshake,
+979            Self::Data => SpaceKind::Data,
+980        }
+981    }
+982}
+983
+984#[cfg(test)]
+985mod tests {
+986    use super::*;
+987    use hex_literal::hex;
+988    use std::io;
+989
+990    fn check_pn(typed: PacketNumber, encoded: &[u8]) {
+991        let mut buf = Vec::new();
+992        typed.encode(&mut buf);
+993        assert_eq!(&buf[..], encoded);
+994        let decoded = PacketNumber::decode(typed.len(), &mut io::Cursor::new(&buf)).unwrap();
+995        assert_eq!(typed, decoded);
+996    }
+997
+998    #[test]
+999    fn roundtrip_packet_numbers() {
+1000        check_pn(PacketNumber::U8(0x7f), &hex!("7f"));
+1001        check_pn(PacketNumber::U16(0x80), &hex!("0080"));
+1002        check_pn(PacketNumber::U16(0x3fff), &hex!("3fff"));
+1003        check_pn(PacketNumber::U32(0x0000_4000), &hex!("0000 4000"));
+1004        check_pn(PacketNumber::U32(0xffff_ffff), &hex!("ffff ffff"));
+1005    }
+1006
+1007    #[test]
+1008    fn pn_encode() {
+1009        check_pn(PacketNumber::new(0x10, 0), &hex!("10"));
+1010        check_pn(PacketNumber::new(0x100, 0), &hex!("0100"));
+1011        check_pn(PacketNumber::new(0x10000, 0), &hex!("010000"));
+1012    }
+1013
+1014    #[test]
+1015    fn pn_expand_roundtrip() {
+1016        for expected in 0..1024 {
+1017            for actual in expected..1024 {
+1018                assert_eq!(actual, PacketNumber::new(actual, expected).expand(expected));
+1019            }
+1020        }
+1021    }
+1022
+1023    #[cfg(all(feature = "rustls", any(feature = "aws-lc-rs", feature = "ring")))]
+1024    #[test]
+1025    fn header_encoding() {
+1026        use crate::Side;
+1027        use crate::crypto::rustls::{configured_provider, initial_keys, initial_suite_from_provider};
+1028        use rustls::quic::Version;
+1029
+1030        let dcid = ConnectionId::new(&hex!("06b858ec6f80452b"));
+1031        let provider = configured_provider();
+1032
+1033        let suite = initial_suite_from_provider(&provider).unwrap();
+1034        let client = initial_keys(Version::V1, dcid, Side::Client, &suite);
+1035        let mut buf = Vec::new();
+1036        let header = Header::Initial(InitialHeader {
+1037            number: PacketNumber::U8(0),
+1038            src_cid: ConnectionId::new(&[]),
+1039            dst_cid: dcid,
+1040            token: Bytes::new(),
+1041            version: crate::DEFAULT_SUPPORTED_VERSIONS[0],
+1042        });
+1043        let encode = header.encode(&mut buf);
+1044        let header_len = buf.len();
+1045        buf.resize(header_len + 16 + client.packet.local.tag_len(), 0);
+1046        encode.finish(
+1047            &mut buf,
+1048            &*client.header.local,
+1049            Some((0, PathId::ZERO, &*client.packet.local)),
+1050        );
+1051
+1052        for byte in &buf {
+1053            print!("{byte:02x}");
+1054        }
+1055        println!();
+1056        assert_eq!(
+1057            buf[..],
+1058            hex!(
+1059                "c8000000010806b858ec6f80452b00004021be
+1060                 3ef50807b84191a196f760a6dad1e9d1c430c48952cba0148250c21c0a6a70e1"
+1061            )[..]
+1062        );
+1063
+1064        let server = initial_keys(Version::V1, dcid, Side::Server, &suite);
+1065        let supported_versions = crate::DEFAULT_SUPPORTED_VERSIONS.to_vec();
+1066        let decode = PartialDecode::new(
+1067            buf.as_slice().into(),
+1068            &FixedLengthConnectionIdParser::new(0),
+1069            &supported_versions,
+1070            false,
+1071        )
+1072        .unwrap()
+1073        .0;
+1074        let mut packet = decode.finish(Some(&*server.header.remote)).unwrap();
+1075        assert_eq!(
+1076            packet.header_data[..],
+1077            hex!("c0000000010806b858ec6f80452b0000402100")[..]
+1078        );
+1079        server
+1080            .packet
+1081            .remote
+1082            .decrypt(PathId::ZERO, 0, &packet.header_data, &mut packet.payload)
+1083            .unwrap();
+1084        assert_eq!(packet.payload[..], [0; 16]);
+1085        match packet.header {
+1086            Header::Initial(InitialHeader {
+1087                number: PacketNumber::U8(0),
+1088                ..
+1089            }) => {}
+1090            _ => {
+1091                panic!("unexpected header {:?}", packet.header);
+1092            }
+1093        }
+1094    }
+1095}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/range_set/array_range_set.rs.html b/pr/672/docs/src/noq_proto/range_set/array_range_set.rs.html new file mode 100644 index 000000000..afb3736b8 --- /dev/null +++ b/pr/672/docs/src/noq_proto/range_set/array_range_set.rs.html @@ -0,0 +1,253 @@ +array_range_set.rs - source

noq_proto/range_set/
array_range_set.rs

1use std::fmt::{self, Write};
+2use std::ops::Range;
+3
+4use tinyvec::TinyVec;
+5
+6/// A set of u64 values optimized for long runs and random insert/delete/contains
+7///
+8/// `ArrayRangeSet` uses an array representation, where each array entry represents
+9/// a range.
+10///
+11/// The array-based RangeSet provides 2 benefits:
+12/// - There exists an inline representation, which avoids the need of heap
+13///   allocating ACK ranges for SentFrames for small ranges.
+14/// - Iterating over ranges should usually be faster since there is only
+15///   a single cache-friendly contiguous range.
+16///
+17/// `ArrayRangeSet` is especially useful for tracking ACK ranges where the amount
+18/// of ranges is usually very low (since ACK numbers are in consecutive fashion
+19/// unless reordering or packet loss occur).
+20#[derive(Default, PartialEq, Eq)]
+21pub(crate) struct ArrayRangeSet(TinyVec<[Range<u64>; ARRAY_RANGE_SET_INLINE_CAPACITY]>);
+22
+23impl fmt::Debug for ArrayRangeSet {
+24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+25        f.write_char('[')?;
+26        let mut first = true;
+27        for range in self.0.iter() {
+28            if !first {
+29                f.write_char(',')?;
+30            }
+31            write!(f, "{range:?}")?;
+32            first = false;
+33        }
+34        f.write_char(']')?;
+35        Ok(())
+36    }
+37}
+38
+39/// The capacity of elements directly stored in [`ArrayRangeSet`]
+40///
+41/// An inline capacity of 2 is chosen to keep `SentFrame` below 128 bytes.
+42const ARRAY_RANGE_SET_INLINE_CAPACITY: usize = 2;
+43
+44impl Clone for ArrayRangeSet {
+45    fn clone(&self) -> Self {
+46        // tinyvec keeps the heap representation after clones.
+47        // We rather prefer the inline representation for clones if possible,
+48        // since clones (e.g. for storage in `SentFrames`) are rarely mutated
+49        if self.0.is_inline() || self.0.len() > ARRAY_RANGE_SET_INLINE_CAPACITY {
+50            return Self(self.0.clone());
+51        }
+52
+53        let mut vec = TinyVec::new();
+54        vec.extend_from_slice(self.0.as_slice());
+55        Self(vec)
+56    }
+57}
+58
+59impl ArrayRangeSet {
+60    pub(crate) fn new() -> Self {
+61        Default::default()
+62    }
+63
+64    pub(crate) fn iter(&self) -> impl DoubleEndedIterator<Item = Range<u64>> + '_ {
+65        self.0.iter().cloned()
+66    }
+67
+68    pub(crate) fn elts(&self) -> impl Iterator<Item = u64> + '_ {
+69        self.iter().flatten()
+70    }
+71
+72    pub(crate) fn len(&self) -> usize {
+73        self.0.len()
+74    }
+75
+76    #[cfg(test)]
+77    pub(crate) fn contains(&self, x: u64) -> bool {
+78        for range in self.0.iter() {
+79            if range.start > x {
+80                // We only get here if there was no prior range that contained x
+81                return false;
+82            } else if range.contains(&x) {
+83                return true;
+84            }
+85        }
+86        false
+87    }
+88
+89    pub(crate) fn iter_range(&self, range: Range<u64>) -> impl Iterator<Item = Range<u64>> + '_ {
+90        self.iter().filter_map(move |r| {
+91            if r.end > range.start && r.start < range.end {
+92                Some(r.start.max(range.start)..r.end.min(range.end))
+93            } else {
+94                None
+95            }
+96        })
+97    }
+98
+99    pub(crate) fn insert_one(&mut self, x: u64) -> bool {
+100        self.insert(x..x + 1)
+101    }
+102
+103    pub(crate) fn insert(&mut self, x: Range<u64>) -> bool {
+104        let mut result = false;
+105
+106        if x.is_empty() {
+107            // Don't try to deal with ranges where x.end <= x.start
+108            return false;
+109        }
+110
+111        let mut idx = 0;
+112        while idx != self.0.len() {
+113            let range = &mut self.0[idx];
+114
+115            if range.start > x.end {
+116                // The range is fully before this range and therefore not extensible.
+117                // Add a new range to the left
+118                self.0.insert(idx, x);
+119                return true;
+120            } else if range.start > x.start {
+121                // The new range starts before this range but overlaps.
+122                // Extend the current range to the left
+123                // Note that we don't have to merge a potential left range, since
+124                // this case would have been captured by merging the right range
+125                // in the previous loop iteration
+126                result = true;
+127                range.start = x.start;
+128            }
+129
+130            // At this point we have handled all parts of the new range which
+131            // are in front of the current range. Now we handle everything from
+132            // the start of the current range
+133
+134            if x.end <= range.end {
+135                // Fully contained
+136                return result;
+137            } else if x.start <= range.end {
+138                // Extend the current range to the end of the new range.
+139                // Since it's not contained it must be bigger
+140                range.end = x.end;
+141
+142                // Merge all follow-up ranges which overlap
+143                while idx != self.0.len() - 1 {
+144                    let curr = self.0[idx].clone();
+145                    let next = self.0[idx + 1].clone();
+146                    if curr.end >= next.start {
+147                        self.0[idx].end = next.end.max(curr.end);
+148                        self.0.remove(idx + 1);
+149                    } else {
+150                        break;
+151                    }
+152                }
+153
+154                return true;
+155            }
+156
+157            idx += 1;
+158        }
+159
+160        // Insert a range at the end
+161        self.0.push(x);
+162        true
+163    }
+164
+165    pub(crate) fn remove(&mut self, x: Range<u64>) -> bool {
+166        let mut result = false;
+167
+168        if x.is_empty() {
+169            // Don't try to deal with ranges where x.end <= x.start
+170            return false;
+171        }
+172
+173        let mut idx = 0;
+174        while idx != self.0.len() && x.start != x.end {
+175            let range = self.0[idx].clone();
+176
+177            if x.end <= range.start {
+178                // The range is fully before this range
+179                return result;
+180            } else if x.start >= range.end {
+181                // The range is fully after this range
+182                idx += 1;
+183                continue;
+184            }
+185
+186            // The range overlaps with this range
+187            result = true;
+188
+189            let left = range.start..x.start;
+190            let right = x.end..range.end;
+191            if left.is_empty() && right.is_empty() {
+192                self.0.remove(idx);
+193            } else if left.is_empty() {
+194                self.0[idx] = right;
+195                idx += 1;
+196            } else if right.is_empty() {
+197                self.0[idx] = left;
+198                idx += 1;
+199            } else {
+200                self.0[idx] = right;
+201                self.0.insert(idx, left);
+202                idx += 2;
+203            }
+204        }
+205
+206        result
+207    }
+208
+209    pub(crate) fn is_empty(&self) -> bool {
+210        self.0.is_empty()
+211    }
+212
+213    pub(crate) fn pop_min(&mut self) -> Option<Range<u64>> {
+214        if !self.0.is_empty() {
+215            Some(self.0.remove(0))
+216        } else {
+217            None
+218        }
+219    }
+220
+221    pub(crate) fn min(&self) -> Option<u64> {
+222        self.iter().next().map(|x| x.start)
+223    }
+224
+225    pub(crate) fn max(&self) -> Option<u64> {
+226        self.iter().next_back().map(|x| x.end - 1)
+227    }
+228}
+229
+230#[cfg(test)]
+231impl proptest::arbitrary::Arbitrary for ArrayRangeSet {
+232    type Parameters = ();
+233    type Strategy = proptest::strategy::BoxedStrategy<Self>;
+234
+235    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
+236        use proptest::prelude::*;
+237        // Generate 1-8 ranges. Each range is defined by a gap from the previous and a size.
+238        // We use small values to keep encoding reasonable.
+239        prop::collection::vec((1u64..100, 1u64..50), 1..8)
+240            .prop_map(|gaps_and_sizes| {
+241                let mut ranges = Self::new();
+242                let mut pos = 0u64;
+243                for (gap, size) in gaps_and_sizes {
+244                    let start = pos + gap;
+245                    let end = start + size;
+246                    ranges.insert(start..end);
+247                    pos = end;
+248                }
+249                ranges
+250            })
+251            .boxed()
+252    }
+253}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/range_set/mod.rs.html b/pr/672/docs/src/noq_proto/range_set/mod.rs.html new file mode 100644 index 000000000..d9544ce15 --- /dev/null +++ b/pr/672/docs/src/noq_proto/range_set/mod.rs.html @@ -0,0 +1,5 @@ +mod.rs - source

noq_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/672/docs/src/noq_proto/shared.rs.html b/pr/672/docs/src/noq_proto/shared.rs.html new file mode 100644 index 000000000..a1d42f5c4 --- /dev/null +++ b/pr/672/docs/src/noq_proto/shared.rs.html @@ -0,0 +1,215 @@ +shared.rs - source

noq_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    /// Whether this is the event is the event indicating the start of the draining period.
+53    pub fn is_draining(&self) -> bool {
+54        self.0 == EndpointEventInner::Draining
+55    }
+56}
+57
+58#[derive(Clone, Debug, Eq, PartialEq)]
+59pub(crate) enum EndpointEventInner {
+60    /// The connection started draining
+61    Draining,
+62    /// The connection has been drained
+63    Drained,
+64    /// The connection has a new active reset token
+65    ///
+66    /// Whenever the connection switches to a new remote CID issued by the peer, it also
+67    /// switches the matching reset token that can be used to abort this connection. This
+68    /// event provides a new reset token for the active remote CID.
+69    ResetToken(PathId, SocketAddr, ResetToken),
+70    /// Retire the remotely issued reset token for a path, without replacing it with a new one
+71    ///
+72    /// This is like `ResetToken` above, but without replacing the `ResetToken` with a new
+73    /// one. See `ConnectionIndex::connection_reset_tokens`.
+74    RetireResetToken(PathId),
+75    /// The connection needs connection identifiers.
+76    ///
+77    /// The fields are:
+78    /// - The path ID for which the identifiers are needed.
+79    /// - The time when the identifiers were needed, not used to generate the CIDs but sent
+80    ///   back via the [`ConnectionEventInner::NewIdentifiers`] so the connection can track
+81    ///   the lifetime of when it needs to be rotated.  should be rotated.
+82    /// - The number of CIDs needed.
+83    NeedIdentifiers(PathId, Instant, u64),
+84    /// Retire a locally issued CID
+85    ///
+86    /// Stop routing connection ID for this sequence number to the connection
+87    /// When `bool == true`, a new connection ID will be issued to peer
+88    RetireConnectionId(Instant, PathId, u64, bool),
+89}
+90
+91/// Protocol-level identifier for a connection.
+92///
+93/// Mainly useful for identifying this connection's packets on the wire with tools like Wireshark.
+94#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
+95#[cfg_attr(test, derive(test_strategy::Arbitrary))]
+96pub struct ConnectionId {
+97    /// length of CID
+98    #[cfg_attr(test, strategy(1u8..=MAX_CID_SIZE as u8))]
+99    len: u8,
+100    /// CID in byte array
+101    bytes: [u8; MAX_CID_SIZE],
+102}
+103
+104impl ConnectionId {
+105    /// Construct cid from byte array
+106    pub fn new(bytes: &[u8]) -> Self {
+107        debug_assert!(bytes.len() <= MAX_CID_SIZE);
+108        let mut res = Self {
+109            len: bytes.len() as u8,
+110            bytes: [0; MAX_CID_SIZE],
+111        };
+112        res.bytes[..bytes.len()].copy_from_slice(bytes);
+113        res
+114    }
+115
+116    /// Constructs cid by reading `len` bytes from a `Buf`
+117    ///
+118    /// Callers need to assure that `buf.remaining() >= len`
+119    pub fn from_buf(buf: &mut (impl Buf + ?Sized), len: usize) -> Self {
+120        debug_assert!(len <= MAX_CID_SIZE);
+121        let mut res = Self {
+122            len: len as u8,
+123            bytes: [0; MAX_CID_SIZE],
+124        };
+125        buf.copy_to_slice(&mut res[..len]);
+126        res
+127    }
+128
+129    pub(crate) fn len(&self) -> usize {
+130        self.len as usize
+131    }
+132
+133    /// Decode from long header format
+134    pub(crate) fn decode_long(buf: &mut impl Buf) -> Option<Self> {
+135        let len = buf.get::<u8>().ok()? as usize;
+136        match len > MAX_CID_SIZE || buf.remaining() < len {
+137            false => Some(Self::from_buf(buf, len)),
+138            true => None,
+139        }
+140    }
+141
+142    /// Encode in long header format
+143    pub(crate) fn encode_long(&self, buf: &mut impl BufMut) {
+144        buf.put_u8(self.len() as u8);
+145        buf.put_slice(self);
+146    }
+147}
+148
+149impl ::std::ops::Deref for ConnectionId {
+150    type Target = [u8];
+151    fn deref(&self) -> &[u8] {
+152        &self.bytes[0..self.len as usize]
+153    }
+154}
+155
+156impl ::std::ops::DerefMut for ConnectionId {
+157    fn deref_mut(&mut self) -> &mut [u8] {
+158        &mut self.bytes[0..self.len as usize]
+159    }
+160}
+161
+162impl fmt::Debug for ConnectionId {
+163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+164        self.bytes[0..self.len as usize].fmt(f)
+165    }
+166}
+167
+168impl fmt::Display for ConnectionId {
+169    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+170        for byte in self.iter() {
+171            write!(f, "{byte:02x}")?;
+172        }
+173        Ok(())
+174    }
+175}
+176
+177/// Explicit congestion notification codepoint
+178#[repr(u8)]
+179#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+180pub enum EcnCodepoint {
+181    /// The ECT(0) codepoint, indicating that an endpoint is ECN-capable
+182    Ect0 = 0b10,
+183    /// The ECT(1) codepoint, indicating that an endpoint is ECN-capable
+184    Ect1 = 0b01,
+185    /// The CE codepoint, signalling that congestion was experienced
+186    Ce = 0b11,
+187}
+188
+189impl EcnCodepoint {
+190    /// Create new object from the given bits
+191    pub fn from_bits(x: u8) -> Option<Self> {
+192        use EcnCodepoint::*;
+193        Some(match x & 0b11 {
+194            0b10 => Ect0,
+195            0b01 => Ect1,
+196            0b11 => Ce,
+197            _ => {
+198                return None;
+199            }
+200        })
+201    }
+202
+203    /// Returns whether the codepoint is a CE, signalling that congestion was experienced
+204    pub fn is_ce(self) -> bool {
+205        matches!(self, Self::Ce)
+206    }
+207}
+208
+209#[derive(Debug, Copy, Clone)]
+210pub(crate) struct IssuedCid {
+211    pub(crate) path_id: PathId,
+212    pub(crate) sequence: u64,
+213    pub(crate) id: ConnectionId,
+214    pub(crate) reset_token: ResetToken,
+215}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/token.rs.html b/pr/672/docs/src/noq_proto/token.rs.html new file mode 100644 index 000000000..f11afef54 --- /dev/null +++ b/pr/672/docs/src/noq_proto/token.rs.html @@ -0,0 +1,492 @@ +token.rs - source

noq_proto/
token.rs

1use std::{
+2    fmt,
+3    net::{IpAddr, SocketAddr},
+4};
+5
+6use bytes::{Buf, BufMut, Bytes};
+7use rand::{CryptoRng, RngExt};
+8
+9use crate::{
+10    Duration, RESET_TOKEN_SIZE, ServerConfig, SystemTime, UNIX_EPOCH,
+11    coding::{BufExt, BufMutExt},
+12    crypto::{HandshakeTokenKey, HmacKey},
+13    packet::InitialHeader,
+14    shared::ConnectionId,
+15};
+16
+17/// Responsible for limiting clients' ability to reuse validation tokens
+18///
+19/// [_RFC 9000 § 8.1.4:_](https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1.4)
+20///
+21/// > Attackers could replay tokens to use servers as amplifiers in DDoS attacks. To protect
+22/// > against such attacks, servers MUST ensure that replay of tokens is prevented or limited.
+23/// > Servers SHOULD ensure that tokens sent in Retry packets are only accepted for a short time,
+24/// > as they are returned immediately by clients. Tokens that are provided in NEW_TOKEN frames
+25/// > (Section 19.7) need to be valid for longer but SHOULD NOT be accepted multiple times.
+26/// > Servers are encouraged to allow tokens to be used only once, if possible; tokens MAY include
+27/// > additional information about clients to further narrow applicability or reuse.
+28///
+29/// `TokenLog` pertains only to tokens provided in NEW_TOKEN frames.
+30pub trait TokenLog: Send + Sync {
+31    /// Record that the token was used and, ideally, return a token reuse error if the token may
+32    /// have been already used previously
+33    ///
+34    /// False negatives and false positives are both permissible. Called when a client uses an
+35    /// address validation token.
+36    ///
+37    /// Parameters:
+38    /// - `nonce`: A server-generated random unique value for the token.
+39    /// - `issued`: The time the server issued the token.
+40    /// - `lifetime`: The expiration time of address validation tokens sent via NEW_TOKEN frames,
+41    ///   as configured by [`ServerValidationTokenConfig::lifetime`][1].
+42    ///
+43    /// [1]: crate::ValidationTokenConfig::lifetime
+44    ///
+45    /// ## Security & Performance
+46    ///
+47    /// To the extent that it is possible to repeatedly trigger false negatives (returning `Ok` for
+48    /// a token which has been reused), an attacker could use the server to perform [amplification
+49    /// attacks][2]. The QUIC specification requires that this be limited, if not prevented fully.
+50    ///
+51    /// A false positive (returning `Err` for a token which has never been used) is not a security
+52    /// vulnerability; it is permissible for a `TokenLog` to always return `Err`. A false positive
+53    /// causes the token to be ignored, which may cause the transmission of some 0.5-RTT data to be
+54    /// delayed until the handshake completes, if a sufficient amount of 0.5-RTT data it sent.
+55    ///
+56    /// [2]: https://en.wikipedia.org/wiki/Denial-of-service_attack#Amplification
+57    fn check_and_insert(
+58        &self,
+59        nonce: u128,
+60        issued: SystemTime,
+61        lifetime: Duration,
+62    ) -> Result<(), TokenReuseError>;
+63}
+64
+65/// Error for when a validation token may have been reused
+66pub struct TokenReuseError;
+67
+68/// Null implementation of [`TokenLog`], which never accepts tokens
+69pub struct NoneTokenLog;
+70
+71impl TokenLog for NoneTokenLog {
+72    fn check_and_insert(&self, _: u128, _: SystemTime, _: Duration) -> Result<(), TokenReuseError> {
+73        Err(TokenReuseError)
+74    }
+75}
+76
+77/// Responsible for storing validation tokens received from servers and retrieving them for use in
+78/// subsequent connections
+79pub trait TokenStore: Send + Sync {
+80    /// Potentially store a token for later one-time use
+81    ///
+82    /// Called when a NEW_TOKEN frame is received from the server.
+83    fn insert(&self, server_name: &str, token: Bytes);
+84
+85    /// Try to find and take a token that was stored with the given server name
+86    ///
+87    /// The same token must never be returned from `take` twice, as doing so can be used to
+88    /// de-anonymize a client's traffic.
+89    ///
+90    /// Called when trying to connect to a server. It is always ok for this to return `None`.
+91    fn take(&self, server_name: &str) -> Option<Bytes>;
+92}
+93
+94/// Null implementation of [`TokenStore`], which does not store any tokens
+95pub struct NoneTokenStore;
+96
+97impl TokenStore for NoneTokenStore {
+98    fn insert(&self, _: &str, _: Bytes) {}
+99    fn take(&self, _: &str) -> Option<Bytes> {
+100        None
+101    }
+102}
+103
+104/// State in an `Incoming` determined by a token or lack thereof
+105#[derive(Debug)]
+106pub(crate) struct IncomingToken {
+107    pub(crate) retry_src_cid: Option<ConnectionId>,
+108    pub(crate) orig_dst_cid: ConnectionId,
+109    pub(crate) validated: bool,
+110}
+111
+112impl IncomingToken {
+113    /// Construct for an `Incoming` given the first packet header, or error if the connection
+114    /// cannot be established
+115    pub(crate) fn from_header(
+116        header: &InitialHeader,
+117        server_config: &ServerConfig,
+118        remote_address: SocketAddr,
+119    ) -> Result<Self, InvalidRetryTokenError> {
+120        let unvalidated = Self {
+121            retry_src_cid: None,
+122            orig_dst_cid: header.dst_cid,
+123            validated: false,
+124        };
+125
+126        // Decode token or short-circuit
+127        if header.token.is_empty() {
+128            return Ok(unvalidated);
+129        }
+130
+131        // In cases where a token cannot be decrypted/decoded, we must allow for the possibility
+132        // that this is caused not by client malfeasance, but by the token having been generated by
+133        // an incompatible endpoint, e.g. a different version or a neighbor behind the same load
+134        // balancer. In such cases we proceed as if there was no token.
+135        //
+136        // [_RFC 9000 § 8.1.3:_](https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1.3-10)
+137        //
+138        // > If the token is invalid, then the server SHOULD proceed as if the client did not have
+139        // > a validated address, including potentially sending a Retry packet.
+140        let Some(retry) = Token::decode(&*server_config.token_key, &header.token) else {
+141            return Ok(unvalidated);
+142        };
+143
+144        // Validate token, then convert into Self
+145        match retry.payload {
+146            TokenPayload::Retry {
+147                address,
+148                orig_dst_cid,
+149                issued,
+150            } => {
+151                if address != remote_address {
+152                    return Err(InvalidRetryTokenError);
+153                }
+154                if issued + server_config.retry_token_lifetime < server_config.time_source.now() {
+155                    return Err(InvalidRetryTokenError);
+156                }
+157
+158                Ok(Self {
+159                    retry_src_cid: Some(header.dst_cid),
+160                    orig_dst_cid,
+161                    validated: true,
+162                })
+163            }
+164            TokenPayload::Validation { ip, issued } => {
+165                if ip != remote_address.ip() {
+166                    return Ok(unvalidated);
+167                }
+168                if issued + server_config.validation_token.lifetime
+169                    < server_config.time_source.now()
+170                {
+171                    return Ok(unvalidated);
+172                }
+173                if server_config
+174                    .validation_token
+175                    .log
+176                    .check_and_insert(retry.nonce, issued, server_config.validation_token.lifetime)
+177                    .is_err()
+178                {
+179                    return Ok(unvalidated);
+180                }
+181
+182                Ok(Self {
+183                    retry_src_cid: None,
+184                    orig_dst_cid: header.dst_cid,
+185                    validated: true,
+186                })
+187            }
+188        }
+189    }
+190}
+191
+192/// Error for a token being unambiguously from a Retry packet, and not valid
+193///
+194/// The connection cannot be established.
+195pub(crate) struct InvalidRetryTokenError;
+196
+197/// Retry or validation token
+198pub(crate) struct Token {
+199    /// Content that is encrypted from the client
+200    pub(crate) payload: TokenPayload,
+201    /// Randomly generated value, which must be unique, and is visible to the client
+202    nonce: u128,
+203}
+204
+205impl Token {
+206    /// Construct with newly sampled randomness
+207    pub(crate) fn new(payload: TokenPayload, rng: &mut impl CryptoRng) -> Self {
+208        Self {
+209            nonce: rng.random(),
+210            payload,
+211        }
+212    }
+213
+214    /// Encode and encrypt
+215    pub(crate) fn encode(&self, key: &dyn HandshakeTokenKey) -> Vec<u8> {
+216        let mut buf = Vec::new();
+217
+218        // Encode payload
+219        match self.payload {
+220            TokenPayload::Retry {
+221                address,
+222                orig_dst_cid,
+223                issued,
+224            } => {
+225                buf.put_u8(TokenType::Retry as u8);
+226                encode_addr(&mut buf, address);
+227                orig_dst_cid.encode_long(&mut buf);
+228                encode_unix_secs(&mut buf, issued);
+229            }
+230            TokenPayload::Validation { ip, issued } => {
+231                buf.put_u8(TokenType::Validation as u8);
+232                encode_ip(&mut buf, ip);
+233                encode_unix_secs(&mut buf, issued);
+234            }
+235        }
+236
+237        // Encrypt
+238        key.seal(self.nonce, &mut buf).unwrap();
+239        buf.extend(&self.nonce.to_le_bytes());
+240
+241        buf
+242    }
+243
+244    /// Decode and decrypt
+245    fn decode(key: &dyn HandshakeTokenKey, raw_token_bytes: &[u8]) -> Option<Self> {
+246        // Decrypt
+247
+248        let (sealed_token, nonce_bytes) = raw_token_bytes.split_last_chunk()?;
+249
+250        let nonce = u128::from_le_bytes(*nonce_bytes);
+251
+252        let mut sealed_token = sealed_token.to_vec();
+253        let mut data = key.open(nonce, &mut sealed_token).ok()?;
+254
+255        // Decode payload
+256        let payload = match TokenType::from_byte((&mut data).get::<u8>().ok()?)? {
+257            TokenType::Retry => TokenPayload::Retry {
+258                address: decode_addr(&mut data)?,
+259                orig_dst_cid: ConnectionId::decode_long(&mut data)?,
+260                issued: decode_unix_secs(&mut data)?,
+261            },
+262            TokenType::Validation => TokenPayload::Validation {
+263                ip: decode_ip(&mut data)?,
+264                issued: decode_unix_secs(&mut data)?,
+265            },
+266        };
+267
+268        if !data.is_empty() {
+269            // Consider extra bytes a decoding error (it may be from an incompatible endpoint)
+270            return None;
+271        }
+272
+273        Some(Self { nonce, payload })
+274    }
+275}
+276
+277/// Content of a [`Token`] that is encrypted from the client
+278pub(crate) enum TokenPayload {
+279    /// Token originating from a Retry packet
+280    Retry {
+281        /// The client's address
+282        address: SocketAddr,
+283        /// The destination connection ID set in the very first packet from the client
+284        orig_dst_cid: ConnectionId,
+285        /// The time at which this token was issued
+286        issued: SystemTime,
+287    },
+288    /// Token originating from a NEW_TOKEN frame
+289    Validation {
+290        /// The client's IP address (its port is likely to change between sessions)
+291        ip: IpAddr,
+292        /// The time at which this token was issued
+293        issued: SystemTime,
+294    },
+295}
+296
+297/// Variant tag for a [`TokenPayload`]
+298#[derive(Copy, Clone)]
+299#[repr(u8)]
+300enum TokenType {
+301    Retry = 0,
+302    Validation = 1,
+303}
+304
+305impl TokenType {
+306    fn from_byte(n: u8) -> Option<Self> {
+307        use TokenType::*;
+308        [Retry, Validation].into_iter().find(|ty| *ty as u8 == n)
+309    }
+310}
+311
+312fn encode_addr(buf: &mut Vec<u8>, address: SocketAddr) {
+313    encode_ip(buf, address.ip());
+314    buf.put_u16(address.port());
+315}
+316
+317fn decode_addr<B: Buf>(buf: &mut B) -> Option<SocketAddr> {
+318    let ip = decode_ip(buf)?;
+319    let port = buf.get().ok()?;
+320    Some(SocketAddr::new(ip, port))
+321}
+322
+323fn encode_ip(buf: &mut Vec<u8>, ip: IpAddr) {
+324    match ip {
+325        IpAddr::V4(x) => {
+326            buf.put_u8(0);
+327            buf.put_slice(&x.octets());
+328        }
+329        IpAddr::V6(x) => {
+330            buf.put_u8(1);
+331            buf.put_slice(&x.octets());
+332        }
+333    }
+334}
+335
+336fn decode_ip<B: Buf>(buf: &mut B) -> Option<IpAddr> {
+337    match buf.get::<u8>().ok()? {
+338        0 => buf.get().ok().map(IpAddr::V4),
+339        1 => buf.get().ok().map(IpAddr::V6),
+340        _ => None,
+341    }
+342}
+343
+344fn encode_unix_secs(buf: &mut Vec<u8>, time: SystemTime) {
+345    buf.write::<u64>(
+346        time.duration_since(UNIX_EPOCH)
+347            .unwrap_or_default()
+348            .as_secs(),
+349    );
+350}
+351
+352fn decode_unix_secs<B: Buf>(buf: &mut B) -> Option<SystemTime> {
+353    Some(UNIX_EPOCH + Duration::from_secs(buf.get::<u64>().ok()?))
+354}
+355
+356/// Stateless reset token
+357///
+358/// Used for an endpoint to securely communicate that it has lost state for a connection.
+359#[allow(clippy::derived_hash_with_manual_eq)] // Custom PartialEq impl matches derived semantics
+360#[derive(Debug, Copy, Clone, Hash)]
+361pub(crate) struct ResetToken([u8; RESET_TOKEN_SIZE]);
+362
+363impl ResetToken {
+364    pub(crate) fn new(key: &dyn HmacKey, id: ConnectionId) -> Self {
+365        let mut signature = vec![0; key.signature_len()];
+366        key.sign(&id, &mut signature);
+367        // TODO: Server ID??
+368        let mut result = [0; RESET_TOKEN_SIZE];
+369        result.copy_from_slice(&signature[..RESET_TOKEN_SIZE]);
+370        result.into()
+371    }
+372}
+373
+374impl PartialEq for ResetToken {
+375    fn eq(&self, other: &Self) -> bool {
+376        crate::constant_time::eq(&self.0, &other.0)
+377    }
+378}
+379
+380impl Eq for ResetToken {}
+381
+382impl From<[u8; RESET_TOKEN_SIZE]> for ResetToken {
+383    fn from(x: [u8; RESET_TOKEN_SIZE]) -> Self {
+384        Self(x)
+385    }
+386}
+387
+388impl std::ops::Deref for ResetToken {
+389    type Target = [u8];
+390    fn deref(&self) -> &[u8] {
+391        &self.0
+392    }
+393}
+394
+395impl fmt::Display for ResetToken {
+396    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+397        for byte in self.iter() {
+398            write!(f, "{byte:02x}")?;
+399        }
+400        Ok(())
+401    }
+402}
+403
+404#[cfg(all(test, any(feature = "aws-lc-rs", feature = "ring")))]
+405mod test {
+406    use rand::Rng;
+407
+408    use crate::crypto::ring_like::RetryTokenKey;
+409
+410    use super::*;
+411
+412    fn token_round_trip(payload: TokenPayload) -> TokenPayload {
+413        let rng = &mut rand::rng();
+414        let token = Token::new(payload, rng);
+415        let master_key = RetryTokenKey::new(rng);
+416        let encoded = token.encode(&master_key);
+417        let decoded = Token::decode(&master_key, &encoded).expect("token didn't decrypt / decode");
+418        assert_eq!(token.nonce, decoded.nonce);
+419        decoded.payload
+420    }
+421
+422    #[test]
+423    fn retry_token_sanity() {
+424        use crate::MAX_CID_SIZE;
+425        use crate::cid_generator::{ConnectionIdGenerator, RandomConnectionIdGenerator};
+426        use crate::{Duration, UNIX_EPOCH};
+427
+428        use std::net::Ipv6Addr;
+429
+430        let address_1 = SocketAddr::new(Ipv6Addr::LOCALHOST.into(), 4433);
+431        let orig_dst_cid_1 = RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid();
+432        let issued_1 = UNIX_EPOCH + Duration::from_secs(42); // Fractional seconds would be lost
+433        let payload_1 = TokenPayload::Retry {
+434            address: address_1,
+435            orig_dst_cid: orig_dst_cid_1,
+436            issued: issued_1,
+437        };
+438        let TokenPayload::Retry {
+439            address: address_2,
+440            orig_dst_cid: orig_dst_cid_2,
+441            issued: issued_2,
+442        } = token_round_trip(payload_1)
+443        else {
+444            panic!("token decoded as wrong variant");
+445        };
+446
+447        assert_eq!(address_1, address_2);
+448        assert_eq!(orig_dst_cid_1, orig_dst_cid_2);
+449        assert_eq!(issued_1, issued_2);
+450    }
+451
+452    #[test]
+453    fn validation_token_sanity() {
+454        use crate::{Duration, UNIX_EPOCH};
+455
+456        use std::net::Ipv6Addr;
+457
+458        let ip_1 = Ipv6Addr::LOCALHOST.into();
+459        let issued_1 = UNIX_EPOCH + Duration::from_secs(42); // Fractional seconds would be lost
+460
+461        let payload_1 = TokenPayload::Validation {
+462            ip: ip_1,
+463            issued: issued_1,
+464        };
+465        let TokenPayload::Validation {
+466            ip: ip_2,
+467            issued: issued_2,
+468        } = token_round_trip(payload_1)
+469        else {
+470            panic!("token decoded as wrong variant");
+471        };
+472
+473        assert_eq!(ip_1, ip_2);
+474        assert_eq!(issued_1, issued_2);
+475    }
+476
+477    #[test]
+478    fn invalid_token_returns_err() {
+479        use super::*;
+480
+481        let master_key = RetryTokenKey::new(&mut rand::rng());
+482
+483        let mut invalid_token = Vec::new();
+484
+485        let mut random_data = [0; 32];
+486        rand::rng().fill_bytes(&mut random_data);
+487        invalid_token.put_slice(&random_data);
+488
+489        // Assert: garbage sealed data returns err
+490        assert!(Token::decode(&master_key, &invalid_token).is_none());
+491    }
+492}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/token_memory_cache.rs.html b/pr/672/docs/src/noq_proto/token_memory_cache.rs.html new file mode 100644 index 000000000..4252015ee --- /dev/null +++ b/pr/672/docs/src/noq_proto/token_memory_cache.rs.html @@ -0,0 +1,247 @@ +token_memory_cache.rs - source

noq_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/672/docs/src/noq_proto/transport_error.rs.html b/pr/672/docs/src/noq_proto/transport_error.rs.html new file mode 100644 index 000000000..56c854397 --- /dev/null +++ b/pr/672/docs/src/noq_proto/transport_error.rs.html @@ -0,0 +1,166 @@ +transport_error.rs - source

noq_proto/
transport_error.rs

1use std::{fmt, sync::Arc};
+2
+3use bytes::{Buf, BufMut};
+4
+5use crate::{
+6    VarInt,
+7    coding::{self, BufExt, BufMutExt, Decodable, Encodable},
+8    frame::MaybeFrame,
+9};
+10
+11/// Transport-level errors occur when a peer violates the protocol specification
+12///
+13/// # Note
+14///
+15/// The `PartialEq` implementation for this type performs comparison on the `code` field only
+16#[derive(Debug, Clone)]
+17#[non_exhaustive]
+18pub struct Error {
+19    /// Type of error
+20    pub code: Code,
+21    /// Frame type that triggered the error
+22    pub frame: MaybeFrame,
+23    /// Human-readable explanation of the reason
+24    pub reason: String,
+25    /// An underlying crypto (e.g. TLS) layer error
+26    pub crypto: Option<Arc<dyn std::error::Error + Send + Sync>>,
+27}
+28
+29impl Error {
+30    /// Construct an error with a code and a reason
+31    pub fn new(code: Code, reason: String) -> Self {
+32        Self {
+33            code,
+34            frame: MaybeFrame::None,
+35            reason,
+36            crypto: None,
+37        }
+38    }
+39}
+40
+41impl PartialEq for Error {
+42    fn eq(&self, other: &Self) -> bool {
+43        self.code == other.code
+44    }
+45}
+46
+47impl Eq for Error {}
+48
+49impl fmt::Display for Error {
+50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+51        self.code.fmt(f)?;
+52        if self.frame != MaybeFrame::None {
+53            write!(f, " in {}", self.frame)?;
+54        }
+55        if !self.reason.is_empty() {
+56            write!(f, ": {}", self.reason)?;
+57        }
+58        Ok(())
+59    }
+60}
+61
+62impl std::error::Error for Error {}
+63
+64/// Transport-level error code
+65#[derive(Copy, Clone, Eq, PartialEq)]
+66#[cfg_attr(test, derive(test_strategy::Arbitrary))]
+67pub struct Code(#[cfg_attr(test, strategy(crate::varint::varint_u64()))] u64);
+68
+69impl Code {
+70    /// Create QUIC error code from TLS alert code
+71    pub fn crypto(code: u8) -> Self {
+72        Self(0x100 | u64::from(code))
+73    }
+74}
+75
+76impl Decodable for Code {
+77    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+78        Ok(Self(buf.get_var()?))
+79    }
+80}
+81
+82impl Encodable for Code {
+83    fn encode<B: BufMut>(&self, buf: &mut B) {
+84        buf.write_var(self.0)
+85    }
+86}
+87
+88impl From<Code> for u64 {
+89    fn from(x: Code) -> Self {
+90        x.0
+91    }
+92}
+93
+94impl From<VarInt> for Code {
+95    fn from(value: VarInt) -> Self {
+96        Self(value.0)
+97    }
+98}
+99
+100impl From<Code> for VarInt {
+101    fn from(value: Code) -> Self {
+102        Self(value.0)
+103    }
+104}
+105
+106macro_rules! errors {
+107    {$($name:ident($val:expr) $desc:expr;)*} => {
+108        #[allow(non_snake_case, unused)]
+109        impl Error {
+110            $(
+111            pub(crate) fn $name<T>(reason: T) -> Self where T: Into<String> {
+112                Self::new(Code::$name, reason.into())
+113            }
+114            )*
+115        }
+116
+117        impl Code {
+118            $(#[doc = $desc] pub const $name: Self = Code($val);)*
+119        }
+120
+121        impl fmt::Debug for Code {
+122            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+123                match self.0 {
+124                    $($val => f.write_str(stringify!($name)),)*
+125                    x if (0x100..0x200).contains(&x) => write!(f, "Code::crypto({:02x})", self.0 as u8),
+126                    _ => write!(f, "Code({:x})", self.0),
+127                }
+128            }
+129        }
+130
+131        impl fmt::Display for Code {
+132            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+133                match self.0 {
+134                    $($val => f.write_str($desc),)*
+135                    // We're trying to be abstract over the crypto protocol, so human-readable descriptions here is tricky.
+136                    _ if self.0 >= 0x100 && self.0 < 0x200 => write!(f, "the cryptographic handshake failed: error {}", self.0 & 0xFF),
+137                    _ => f.write_str("unknown error"),
+138                }
+139            }
+140        }
+141    }
+142}
+143
+144errors! {
+145    NO_ERROR(0x0) "the connection is being closed abruptly in the absence of any error";
+146    INTERNAL_ERROR(0x1) "the endpoint encountered an internal error and cannot continue with the connection";
+147    CONNECTION_REFUSED(0x2) "the server refused to accept a new connection";
+148    FLOW_CONTROL_ERROR(0x3) "received more data than permitted in advertised data limits";
+149    STREAM_LIMIT_ERROR(0x4) "received a frame for a stream identifier that exceeded advertised the stream limit for the corresponding stream type";
+150    STREAM_STATE_ERROR(0x5) "received a frame for a stream that was not in a state that permitted that frame";
+151    FINAL_SIZE_ERROR(0x6) "received a STREAM frame or a RESET_STREAM frame containing a different final size to the one already established";
+152    FRAME_ENCODING_ERROR(0x7) "received a frame that was badly formatted";
+153    TRANSPORT_PARAMETER_ERROR(0x8) "received transport parameters that were badly formatted, included an invalid value, was absent even though it is mandatory, was present though it is forbidden, or is otherwise in error";
+154    CONNECTION_ID_LIMIT_ERROR(0x9) "the number of connection IDs provided by the peer exceeds the advertised active_connection_id_limit";
+155    PROTOCOL_VIOLATION(0xA) "detected an error with protocol compliance that was not covered by more specific error codes";
+156    INVALID_TOKEN(0xB) "received an invalid Retry Token in a client Initial";
+157    APPLICATION_ERROR(0xC) "the application or application protocol caused the connection to be closed during the handshake";
+158    CRYPTO_BUFFER_EXCEEDED(0xD) "received more data in CRYPTO frames than can be buffered";
+159    KEY_UPDATE_ERROR(0xE) "key update error";
+160    AEAD_LIMIT_REACHED(0xF) "the endpoint has reached the confidentiality or integrity limit for the AEAD algorithm";
+161    NO_VIABLE_PATH(0x10) "no viable network path exists";
+162    APPLICATION_ABANDON_PATH(0x3e) "Path abandoned at the application's request";
+163    PATH_RESOURCE_LIMIT_REACHED(0x3e75) "Path abandoned due to resource limitations in the transport";
+164    PATH_UNSTABLE_OR_POOR(0x3e76) "Path abandoned due to unstable interfaces";
+165    NO_CID_AVAILABLE_FOR_PATH(0x3e77) "Path abandoned due to no available connection IDs for the path";
+166}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/transport_parameters.rs.html b/pr/672/docs/src/noq_proto/transport_parameters.rs.html new file mode 100644 index 000000000..c2a6ff437 --- /dev/null +++ b/pr/672/docs/src/noq_proto/transport_parameters.rs.html @@ -0,0 +1,980 @@ +transport_parameters.rs - source

noq_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, RngExt, seq::SliceRandom as _};
+17use thiserror::Error;
+18
+19use crate::{
+20    LOCAL_CID_COUNT, MAX_CID_SIZE, MAX_STREAM_COUNT, RESET_TOKEN_SIZE, ResetToken, Side,
+21    TIMER_GRANULARITY, TransportError, VarInt, address_discovery,
+22    cid_generator::ConnectionIdGenerator,
+23    cid_queue::CidQueue,
+24    coding::{BufExt, BufMutExt, UnexpectedEnd},
+25    config::{EndpointConfig, ServerConfig, TransportConfig},
+26    connection::PathId,
+27    shared::ConnectionId,
+28};
+29
+30// Apply a given macro to a list of all the transport parameters having integer types, along with
+31// their codes and default values. Using this helps us avoid error-prone duplication of the
+32// contained information across decoding, encoding, and the `Default` impl. Whenever we want to do
+33// something with transport parameters, we'll handle the bulk of cases by writing a macro that
+34// takes a list of arguments in this form, then passing it to this macro.
+35macro_rules! apply_params {
+36    ($macro:ident) => {
+37        $macro! {
+38            // #[doc] name (id) = default,
+39            /// Milliseconds, disabled if zero
+40            max_idle_timeout(MaxIdleTimeout) = 0,
+41            /// Limits the size of UDP payloads that the endpoint is willing to receive
+42            max_udp_payload_size(MaxUdpPayloadSize) = 65527,
+43
+44            /// Initial value for the maximum amount of data that can be sent on the connection
+45            initial_max_data(InitialMaxData) = 0,
+46            /// Initial flow control limit for locally-initiated bidirectional streams
+47            initial_max_stream_data_bidi_local(InitialMaxStreamDataBidiLocal) = 0,
+48            /// Initial flow control limit for peer-initiated bidirectional streams
+49            initial_max_stream_data_bidi_remote(InitialMaxStreamDataBidiRemote) = 0,
+50            /// Initial flow control limit for unidirectional streams
+51            initial_max_stream_data_uni(InitialMaxStreamDataUni) = 0,
+52
+53            /// Initial maximum number of bidirectional streams the peer may initiate
+54            initial_max_streams_bidi(InitialMaxStreamsBidi) = 0,
+55            /// Initial maximum number of unidirectional streams the peer may initiate
+56            initial_max_streams_uni(InitialMaxStreamsUni) = 0,
+57
+58            /// Exponent used to decode the ACK Delay field in the ACK frame
+59            ack_delay_exponent(AckDelayExponent) = 3,
+60            /// Maximum amount of time in milliseconds by which the endpoint will delay sending
+61            /// acknowledgments
+62            max_ack_delay(MaxAckDelay) = 25,
+63            /// Maximum number of connection IDs from the peer that an endpoint is willing to store
+64            active_connection_id_limit(ActiveConnectionIdLimit) = 2,
+65        }
+66    };
+67}
+68
+69macro_rules! make_struct {
+70    {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
+71        /// Transport parameters used to negotiate connection-level preferences between peers
+72        #[derive(Debug, Copy, Clone, Eq, PartialEq)]
+73        pub struct TransportParameters {
+74            $($(#[$doc])* pub(crate) $name : VarInt,)*
+75
+76            /// Does the endpoint support active connection migration
+77            pub(crate) disable_active_migration: bool,
+78            /// Maximum size for datagram frames
+79            pub(crate) max_datagram_frame_size: Option<VarInt>,
+80            /// The value that the endpoint included in the Source Connection ID field of the first
+81            /// Initial packet it sends for the connection
+82            pub(crate) initial_src_cid: Option<ConnectionId>,
+83            /// The endpoint is willing to receive QUIC packets containing any value for the fixed
+84            /// bit
+85            pub(crate) grease_quic_bit: bool,
+86
+87            /// Minimum amount of time in microseconds by which the endpoint is able to delay
+88            /// sending acknowledgments
+89            ///
+90            /// If a value is provided, it implies that the endpoint supports QUIC Acknowledgement
+91            /// Frequency
+92            pub(crate) min_ack_delay: Option<VarInt>,
+93
+94            // Server-only
+95            /// The value of the Destination Connection ID field from the first Initial packet sent
+96            /// by the client
+97            pub(crate) original_dst_cid: Option<ConnectionId>,
+98            /// The value that the server included in the Source Connection ID field of a Retry
+99            /// packet
+100            pub(crate) retry_src_cid: Option<ConnectionId>,
+101            /// Token used by the client to verify a stateless reset from the server
+102            pub(crate) stateless_reset_token: Option<ResetToken>,
+103            /// The server's preferred address for communication after handshake completion
+104            pub(crate) preferred_address: Option<PreferredAddress>,
+105
+106            /// The randomly generated reserved transport parameter to sustain future extensibility
+107            /// of transport parameter extensions.
+108            /// When present, it is included during serialization but ignored during deserialization.
+109            pub(crate) grease_transport_parameter: Option<ReservedTransportParameter>,
+110
+111            /// Defines the order in which transport parameters are serialized.
+112            ///
+113            /// This field is initialized only for outgoing `TransportParameters` instances and
+114            /// is set to `None` for `TransportParameters` received from a peer.
+115            pub(crate) write_order: Option<[u8; TransportParameterId::SUPPORTED.len()]>,
+116
+117            /// The role of this peer in address discovery, if any.
+118            pub(crate) address_discovery_role: address_discovery::Role,
+119
+120            /// Multipath extension
+121            pub(crate) initial_max_path_id: Option<PathId>,
+122
+123            /// Nat traversal draft
+124            pub max_remote_nat_traversal_addresses: Option<NonZeroU8>,
+125        }
+126
+127        // We deliberately don't implement the `Default` trait, since that would be public, and
+128        // downstream crates should never construct `TransportParameters` except by decoding those
+129        // supplied by a peer.
+130        impl TransportParameters {
+131            /// Standard defaults, used if the peer does not supply a given parameter.
+132            pub(crate) fn default() -> Self {
+133                Self {
+134                    $($name: VarInt::from_u32($default),)*
+135
+136                    disable_active_migration: false,
+137                    max_datagram_frame_size: None,
+138                    initial_src_cid: None,
+139                    grease_quic_bit: false,
+140                    min_ack_delay: None,
+141
+142                    original_dst_cid: None,
+143                    retry_src_cid: None,
+144                    stateless_reset_token: None,
+145                    preferred_address: None,
+146                    grease_transport_parameter: None,
+147                    write_order: None,
+148                    address_discovery_role: address_discovery::Role::Disabled,
+149                    initial_max_path_id: None,
+150                    max_remote_nat_traversal_addresses: None,
+151                }
+152            }
+153        }
+154    }
+155}
+156
+157apply_params!(make_struct);
+158
+159impl TransportParameters {
+160    pub(crate) fn new(
+161        config: &TransportConfig,
+162        endpoint_config: &EndpointConfig,
+163        cid_gen: &dyn ConnectionIdGenerator,
+164        initial_src_cid: ConnectionId,
+165        server_config: Option<&ServerConfig>,
+166        rng: &mut impl Rng,
+167    ) -> Self {
+168        Self {
+169            initial_src_cid: Some(initial_src_cid),
+170            initial_max_streams_bidi: config.max_concurrent_bidi_streams,
+171            initial_max_streams_uni: config.max_concurrent_uni_streams,
+172            initial_max_data: config.receive_window,
+173            initial_max_stream_data_bidi_local: config.stream_receive_window,
+174            initial_max_stream_data_bidi_remote: config.stream_receive_window,
+175            initial_max_stream_data_uni: config.stream_receive_window,
+176            max_udp_payload_size: endpoint_config.max_udp_payload_size,
+177            max_idle_timeout: config.max_idle_timeout.unwrap_or(VarInt(0)),
+178            disable_active_migration: server_config.is_some_and(|c| !c.migration),
+179            active_connection_id_limit: if cid_gen.cid_len() == 0 {
+180                2 // i.e. default, i.e. unsent
+181            } else {
+182                CidQueue::LEN as u32
+183            }
+184            .into(),
+185            max_datagram_frame_size: config
+186                .datagram_receive_buffer_size
+187                .map(|x| (x.min(u16::MAX.into()) as u16).into()),
+188            grease_quic_bit: endpoint_config.grease_quic_bit,
+189            min_ack_delay: Some(
+190                VarInt::from_u64(u64::try_from(TIMER_GRANULARITY.as_micros()).unwrap()).unwrap(),
+191            ),
+192            grease_transport_parameter: Some(ReservedTransportParameter::random(rng)),
+193            write_order: Some({
+194                let mut order = std::array::from_fn(|i| i as u8);
+195                order.shuffle(rng);
+196                order
+197            }),
+198            address_discovery_role: config.address_discovery_role,
+199            initial_max_path_id: config.get_initial_max_path_id(),
+200            max_remote_nat_traversal_addresses: config.max_remote_nat_traversal_addresses,
+201            ..Self::default()
+202        }
+203    }
+204
+205    /// Check that these parameters are legal when resuming from
+206    /// certain cached parameters
+207    pub(crate) fn validate_resumption_from(&self, cached: &Self) -> Result<(), TransportError> {
+208        if cached.active_connection_id_limit > self.active_connection_id_limit
+209            || cached.initial_max_data > self.initial_max_data
+210            || cached.initial_max_stream_data_bidi_local > self.initial_max_stream_data_bidi_local
+211            || cached.initial_max_stream_data_bidi_remote > self.initial_max_stream_data_bidi_remote
+212            || cached.initial_max_stream_data_uni > self.initial_max_stream_data_uni
+213            || cached.initial_max_streams_bidi > self.initial_max_streams_bidi
+214            || cached.initial_max_streams_uni > self.initial_max_streams_uni
+215            || cached.max_datagram_frame_size > self.max_datagram_frame_size
+216            || cached.grease_quic_bit && !self.grease_quic_bit
+217            || cached.address_discovery_role != self.address_discovery_role
+218            || cached.max_remote_nat_traversal_addresses != self.max_remote_nat_traversal_addresses
+219        {
+220            return Err(TransportError::PROTOCOL_VIOLATION(
+221                "0-RTT accepted with incompatible transport parameters",
+222            ));
+223        }
+224        Ok(())
+225    }
+226
+227    /// Maximum number of CIDs to issue to this peer
+228    ///
+229    /// Consider both a) the active_connection_id_limit from the other end; and
+230    /// b) LOCAL_CID_COUNT used locally
+231    pub(crate) fn issue_cids_limit(&self) -> u64 {
+232        self.active_connection_id_limit.0.min(LOCAL_CID_COUNT)
+233    }
+234}
+235
+236/// A server's preferred address
+237///
+238/// This is communicated as a transport parameter during TLS session establishment.
+239#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+240pub(crate) struct PreferredAddress {
+241    pub(crate) address_v4: Option<SocketAddrV4>,
+242    pub(crate) address_v6: Option<SocketAddrV6>,
+243    pub(crate) connection_id: ConnectionId,
+244    pub(crate) stateless_reset_token: ResetToken,
+245}
+246
+247impl PreferredAddress {
+248    fn wire_size(&self) -> u16 {
+249        4 + 2 + 16 + 2 + 1 + self.connection_id.len() as u16 + 16
+250    }
+251
+252    fn write<W: BufMut>(&self, w: &mut W) {
+253        w.write(self.address_v4.map_or(Ipv4Addr::UNSPECIFIED, |x| *x.ip()));
+254        w.write::<u16>(self.address_v4.map_or(0, |x| x.port()));
+255        w.write(self.address_v6.map_or(Ipv6Addr::UNSPECIFIED, |x| *x.ip()));
+256        w.write::<u16>(self.address_v6.map_or(0, |x| x.port()));
+257        w.write::<u8>(self.connection_id.len() as u8);
+258        w.put_slice(&self.connection_id);
+259        w.put_slice(&self.stateless_reset_token);
+260    }
+261
+262    fn read<R: Buf>(r: &mut R) -> Result<Self, Error> {
+263        let ip_v4 = r.get::<Ipv4Addr>()?;
+264        let port_v4 = r.get::<u16>()?;
+265        let ip_v6 = r.get::<Ipv6Addr>()?;
+266        let port_v6 = r.get::<u16>()?;
+267        let cid_len = r.get::<u8>()?;
+268        if r.remaining() < cid_len as usize || cid_len > MAX_CID_SIZE as u8 {
+269            return Err(Error::Malformed);
+270        }
+271        let mut stage = [0; MAX_CID_SIZE];
+272        r.copy_to_slice(&mut stage[0..cid_len as usize]);
+273        let cid = ConnectionId::new(&stage[0..cid_len as usize]);
+274        if r.remaining() < 16 {
+275            return Err(Error::Malformed);
+276        }
+277        let mut token = [0; RESET_TOKEN_SIZE];
+278        r.copy_to_slice(&mut token);
+279        let address_v4 = if ip_v4.is_unspecified() && port_v4 == 0 {
+280            None
+281        } else {
+282            Some(SocketAddrV4::new(ip_v4, port_v4))
+283        };
+284        let address_v6 = if ip_v6.is_unspecified() && port_v6 == 0 {
+285            None
+286        } else {
+287            Some(SocketAddrV6::new(ip_v6, port_v6, 0, 0))
+288        };
+289        if address_v4.is_none() && address_v6.is_none() {
+290            return Err(Error::IllegalValue);
+291        }
+292        Ok(Self {
+293            address_v4,
+294            address_v6,
+295            connection_id: cid,
+296            stateless_reset_token: token.into(),
+297        })
+298    }
+299}
+300
+301/// Errors encountered while decoding `TransportParameters`
+302#[derive(Debug, Copy, Clone, Eq, PartialEq, Error)]
+303pub enum Error {
+304    /// Parameters that are semantically invalid
+305    #[error("parameter had illegal value")]
+306    IllegalValue,
+307    /// Catch-all error for problems while decoding transport parameters
+308    #[error("parameters were malformed")]
+309    Malformed,
+310}
+311
+312impl From<Error> for TransportError {
+313    fn from(e: Error) -> Self {
+314        match e {
+315            Error::IllegalValue => Self::TRANSPORT_PARAMETER_ERROR("illegal value"),
+316            Error::Malformed => Self::TRANSPORT_PARAMETER_ERROR("malformed"),
+317        }
+318    }
+319}
+320
+321impl From<UnexpectedEnd> for Error {
+322    fn from(_: UnexpectedEnd) -> Self {
+323        Self::Malformed
+324    }
+325}
+326
+327impl TransportParameters {
+328    /// Encode `TransportParameters` into buffer
+329    pub fn write<W: BufMut>(&self, w: &mut W) {
+330        let ids = match &self.write_order {
+331            Some(order) => order,
+332            None => &std::array::from_fn(|i| i as u8),
+333        };
+334
+335        for idx in ids {
+336            let id = TransportParameterId::SUPPORTED[*idx as usize];
+337            match id {
+338                TransportParameterId::ReservedTransportParameter => {
+339                    if let Some(param) = self.grease_transport_parameter {
+340                        param.write(w);
+341                    }
+342                }
+343                TransportParameterId::StatelessResetToken => {
+344                    if let Some(ref x) = self.stateless_reset_token {
+345                        w.write_var(id as u64);
+346                        w.write_var(16);
+347                        w.put_slice(x);
+348                    }
+349                }
+350                TransportParameterId::DisableActiveMigration => {
+351                    if self.disable_active_migration {
+352                        w.write_var(id as u64);
+353                        w.write_var(0);
+354                    }
+355                }
+356                TransportParameterId::MaxDatagramFrameSize => {
+357                    if let Some(x) = self.max_datagram_frame_size {
+358                        w.write_var(id as u64);
+359                        w.write_var(x.size() as u64);
+360                        w.write(x);
+361                    }
+362                }
+363                TransportParameterId::PreferredAddress => {
+364                    if let Some(ref x) = self.preferred_address {
+365                        w.write_var(id as u64);
+366                        w.write_var(x.wire_size() as u64);
+367                        x.write(w);
+368                    }
+369                }
+370                TransportParameterId::OriginalDestinationConnectionId => {
+371                    if let Some(ref cid) = self.original_dst_cid {
+372                        w.write_var(id as u64);
+373                        w.write_var(cid.len() as u64);
+374                        w.put_slice(cid);
+375                    }
+376                }
+377                TransportParameterId::InitialSourceConnectionId => {
+378                    if let Some(ref cid) = self.initial_src_cid {
+379                        w.write_var(id as u64);
+380                        w.write_var(cid.len() as u64);
+381                        w.put_slice(cid);
+382                    }
+383                }
+384                TransportParameterId::RetrySourceConnectionId => {
+385                    if let Some(ref cid) = self.retry_src_cid {
+386                        w.write_var(id as u64);
+387                        w.write_var(cid.len() as u64);
+388                        w.put_slice(cid);
+389                    }
+390                }
+391                TransportParameterId::GreaseQuicBit => {
+392                    if self.grease_quic_bit {
+393                        w.write_var(id as u64);
+394                        w.write_var(0);
+395                    }
+396                }
+397                TransportParameterId::MinAckDelayDraft07 => {
+398                    if let Some(x) = self.min_ack_delay {
+399                        w.write_var(id as u64);
+400                        w.write_var(x.size() as u64);
+401                        w.write(x);
+402                    }
+403                }
+404                TransportParameterId::ObservedAddr => {
+405                    if let Some(varint_role) = self.address_discovery_role.as_transport_parameter()
+406                    {
+407                        w.write_var(id as u64);
+408                        w.write_var(varint_role.size() as u64);
+409                        w.write(varint_role);
+410                    }
+411                }
+412                TransportParameterId::InitialMaxPathId => {
+413                    if let Some(val) = self.initial_max_path_id {
+414                        w.write_var(id as u64);
+415                        w.write_var(val.size() as u64);
+416                        w.write(val);
+417                    }
+418                }
+419                TransportParameterId::N0NatTraversal => {
+420                    if let Some(val) = self.max_remote_nat_traversal_addresses {
+421                        w.write_var(id as u64);
+422                        w.write(VarInt(1));
+423                        w.write(val.get());
+424                    }
+425                }
+426                id => {
+427                    macro_rules! write_params {
+428                        {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
+429                            match id {
+430                                $(TransportParameterId::$id => {
+431                                    if self.$name.0 != $default {
+432                                        w.write_var(id as u64);
+433                                        w.write(VarInt::try_from(self.$name.size()).unwrap());
+434                                        w.write(self.$name);
+435                                    }
+436                                })*,
+437                                _ => {
+438                                    unimplemented!("Missing implementation of write for transport parameter with code {id:?}");
+439                                }
+440                            }
+441                        }
+442                    }
+443                    apply_params!(write_params);
+444                }
+445            }
+446        }
+447    }
+448
+449    /// Decode `TransportParameters` from buffer
+450    pub fn read<R: Buf>(side: Side, r: &mut R) -> Result<Self, Error> {
+451        // Initialize to protocol-specified defaults
+452        let mut params = Self::default();
+453
+454        // State to check for duplicate transport parameters.
+455        macro_rules! param_state {
+456            {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {{
+457                struct ParamState {
+458                    $($name: bool,)*
+459                }
+460
+461                ParamState {
+462                    $($name: false,)*
+463                }
+464            }}
+465        }
+466        let mut got = apply_params!(param_state);
+467
+468        while r.has_remaining() {
+469            let id = r.get_var()?;
+470            let len = r.get_var()?;
+471            if (r.remaining() as u64) < len {
+472                return Err(Error::Malformed);
+473            }
+474            let len = len as usize;
+475            let Ok(id) = TransportParameterId::try_from(id) else {
+476                // unknown transport parameters are ignored
+477                r.advance(len);
+478                continue;
+479            };
+480
+481            match id {
+482                TransportParameterId::OriginalDestinationConnectionId => {
+483                    decode_cid(len, &mut params.original_dst_cid, r)?
+484                }
+485                TransportParameterId::StatelessResetToken => {
+486                    if len != 16 || params.stateless_reset_token.is_some() {
+487                        return Err(Error::Malformed);
+488                    }
+489                    let mut tok = [0; RESET_TOKEN_SIZE];
+490                    r.copy_to_slice(&mut tok);
+491                    params.stateless_reset_token = Some(tok.into());
+492                }
+493                TransportParameterId::DisableActiveMigration => {
+494                    if len != 0 || params.disable_active_migration {
+495                        return Err(Error::Malformed);
+496                    }
+497                    params.disable_active_migration = true;
+498                }
+499                TransportParameterId::PreferredAddress => {
+500                    if params.preferred_address.is_some() {
+501                        return Err(Error::Malformed);
+502                    }
+503                    params.preferred_address = Some(PreferredAddress::read(&mut r.take(len))?);
+504                }
+505                TransportParameterId::InitialSourceConnectionId => {
+506                    decode_cid(len, &mut params.initial_src_cid, r)?
+507                }
+508                TransportParameterId::RetrySourceConnectionId => {
+509                    decode_cid(len, &mut params.retry_src_cid, r)?
+510                }
+511                TransportParameterId::MaxDatagramFrameSize => {
+512                    if len > 8 || params.max_datagram_frame_size.is_some() {
+513                        return Err(Error::Malformed);
+514                    }
+515                    params.max_datagram_frame_size = Some(r.get()?);
+516                }
+517                TransportParameterId::GreaseQuicBit => match len {
+518                    0 => params.grease_quic_bit = true,
+519                    _ => return Err(Error::Malformed),
+520                },
+521                TransportParameterId::MinAckDelayDraft07 => params.min_ack_delay = Some(r.get()?),
+522                TransportParameterId::ObservedAddr => {
+523                    if !params.address_discovery_role.is_disabled() {
+524                        // duplicate parameter
+525                        return Err(Error::Malformed);
+526                    }
+527                    let value: VarInt = r.get()?;
+528                    if len != value.size() {
+529                        return Err(Error::Malformed);
+530                    }
+531                    params.address_discovery_role = value.try_into()?;
+532                    tracing::debug!(
+533                        role = ?params.address_discovery_role,
+534                        "address discovery enabled for peer"
+535                    );
+536                }
+537                TransportParameterId::InitialMaxPathId => {
+538                    if params.initial_max_path_id.is_some() {
+539                        return Err(Error::Malformed);
+540                    }
+541
+542                    let value: PathId = r.get()?;
+543                    if len != value.size() {
+544                        return Err(Error::Malformed);
+545                    }
+546
+547                    params.initial_max_path_id = Some(value);
+548                }
+549                TransportParameterId::N0NatTraversal => {
+550                    if params.max_remote_nat_traversal_addresses.is_some() {
+551                        return Err(Error::Malformed);
+552                    }
+553                    if len != 1 {
+554                        return Err(Error::Malformed);
+555                    }
+556
+557                    let value: u8 = r.get()?;
+558                    let value = NonZeroU8::new(value).ok_or(Error::IllegalValue)?;
+559
+560                    params.max_remote_nat_traversal_addresses = Some(value);
+561                }
+562                _ => {
+563                    macro_rules! parse {
+564                        {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
+565                            match id {
+566                                $(TransportParameterId::$id => {
+567                                    let value = r.get::<VarInt>()?;
+568                                    if len != value.size() || got.$name { return Err(Error::Malformed); }
+569                                    params.$name = value.into();
+570                                    got.$name = true;
+571                                })*
+572                                _ => r.advance(len),
+573                            }
+574                        }
+575                    }
+576                    apply_params!(parse);
+577                }
+578            }
+579        }
+580
+581        // Semantic validation
+582
+583        // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.26.1
+584        if params.ack_delay_exponent.0 > 20
+585            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.28.1
+586            || params.max_ack_delay.0 >= 1 << 14
+587            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-6.2.1
+588            || params.active_connection_id_limit.0 < 2
+589            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.10.1
+590            || params.max_udp_payload_size.0 < 1200
+591            // https://www.rfc-editor.org/rfc/rfc9000.html#section-4.6-2
+592            || params.initial_max_streams_bidi.0 > MAX_STREAM_COUNT
+593            || params.initial_max_streams_uni.0 > MAX_STREAM_COUNT
+594            // https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-08.html#section-3-4
+595            || params.min_ack_delay.is_some_and(|min_ack_delay| {
+596                // min_ack_delay uses microseconds, whereas max_ack_delay uses milliseconds
+597                min_ack_delay.0 > params.max_ack_delay.0 * 1_000
+598            })
+599            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-8
+600            || (side.is_server()
+601                && (params.original_dst_cid.is_some()
+602                    || params.preferred_address.is_some()
+603                    || params.retry_src_cid.is_some()
+604                    || params.stateless_reset_token.is_some()))
+605            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.38.1
+606            || params
+607                .preferred_address.is_some_and(|x| x.connection_id.is_empty())
+608        {
+609            return Err(Error::IllegalValue);
+610        }
+611
+612        Ok(params)
+613    }
+614}
+615
+616/// A reserved transport parameter.
+617///
+618/// It has an identifier of the form 31 * N + 27 for the integer value of N.
+619/// Such identifiers are reserved to exercise the requirement that unknown transport parameters be ignored.
+620/// The reserved transport parameter has no semantics and can carry arbitrary values.
+621/// It may be included in transport parameters sent to the peer, and should be ignored when received.
+622///
+623/// See spec: <https://www.rfc-editor.org/rfc/rfc9000.html#section-18.1>
+624#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+625pub(crate) struct ReservedTransportParameter {
+626    /// The reserved identifier of the transport parameter
+627    id: VarInt,
+628
+629    /// Buffer to store the parameter payload
+630    payload: [u8; Self::MAX_PAYLOAD_LEN],
+631
+632    /// The number of bytes to include in the wire format from the `payload` buffer
+633    payload_len: usize,
+634}
+635
+636impl ReservedTransportParameter {
+637    /// Generates a transport parameter with a random payload and a reserved ID.
+638    ///
+639    /// The implementation is inspired by quic-go and quiche:
+640    /// 1. <https://github.com/quic-go/quic-go/blob/3e0a67b2476e1819752f04d75968de042b197b56/internal/wire/transport_parameters.go#L338-L344>
+641    /// 2. <https://github.com/google/quiche/blob/cb1090b20c40e2f0815107857324e99acf6ec567/quiche/quic/core/crypto/transport_parameters.cc#L843-L860>
+642    fn random(rng: &mut impl Rng) -> Self {
+643        let id = Self::generate_reserved_id(rng);
+644
+645        let payload_len = rng.random_range(0..Self::MAX_PAYLOAD_LEN);
+646
+647        let payload = {
+648            let mut slice = [0u8; Self::MAX_PAYLOAD_LEN];
+649            rng.fill_bytes(&mut slice[..payload_len]);
+650            slice
+651        };
+652
+653        Self {
+654            id,
+655            payload,
+656            payload_len,
+657        }
+658    }
+659
+660    fn write(&self, w: &mut impl BufMut) {
+661        w.write_var(self.id.0);
+662        w.write_var(self.payload_len as u64);
+663        w.put_slice(&self.payload[..self.payload_len]);
+664    }
+665
+666    /// Generates a random reserved identifier of the form `31 * N + 27`, as required by RFC 9000.
+667    /// Reserved transport parameter identifiers are used to test compliance with the requirement
+668    /// that unknown transport parameters must be ignored by peers.
+669    /// See: <https://www.rfc-editor.org/rfc/rfc9000.html#section-18.1> and <https://www.rfc-editor.org/rfc/rfc9000.html#section-22.3>
+670    fn generate_reserved_id(rng: &mut impl Rng) -> VarInt {
+671        let id = {
+672            let rand = rng.random_range(0u64..(1 << 62) - 27);
+673            let n = rand / 31;
+674            31 * n + 27
+675        };
+676        debug_assert!(
+677            id % 31 == 27,
+678            "generated id does not have the form of 31 * N + 27"
+679        );
+680        VarInt::from_u64(id).expect(
+681            "generated id does fit into range of allowed transport parameter IDs: [0; 2^62)",
+682        )
+683    }
+684
+685    /// The maximum length of the payload to include as the parameter payload.
+686    /// This value is not a specification-imposed limit but is chosen to match
+687    /// the limit used by other implementations of QUIC, e.g., quic-go and quiche.
+688    const MAX_PAYLOAD_LEN: usize = 16;
+689}
+690
+691#[repr(u64)]
+692#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+693pub(crate) enum TransportParameterId {
+694    // https://www.rfc-editor.org/rfc/rfc9000.html#iana-tp-table
+695    OriginalDestinationConnectionId = 0x00,
+696    MaxIdleTimeout = 0x01,
+697    StatelessResetToken = 0x02,
+698    MaxUdpPayloadSize = 0x03,
+699    InitialMaxData = 0x04,
+700    InitialMaxStreamDataBidiLocal = 0x05,
+701    InitialMaxStreamDataBidiRemote = 0x06,
+702    InitialMaxStreamDataUni = 0x07,
+703    InitialMaxStreamsBidi = 0x08,
+704    InitialMaxStreamsUni = 0x09,
+705    AckDelayExponent = 0x0A,
+706    MaxAckDelay = 0x0B,
+707    DisableActiveMigration = 0x0C,
+708    PreferredAddress = 0x0D,
+709    ActiveConnectionIdLimit = 0x0E,
+710    InitialSourceConnectionId = 0x0F,
+711    RetrySourceConnectionId = 0x10,
+712
+713    // Smallest possible ID of reserved transport parameter https://datatracker.ietf.org/doc/html/rfc9000#section-22.3
+714    ReservedTransportParameter = 0x1B,
+715
+716    // https://www.rfc-editor.org/rfc/rfc9221.html#section-3
+717    MaxDatagramFrameSize = 0x20,
+718
+719    // https://www.rfc-editor.org/rfc/rfc9287.html#section-3
+720    GreaseQuicBit = 0x2AB2,
+721
+722    // https://datatracker.ietf.org/doc/html/draft-ietf-quic-ack-frequency#section-10.1
+723    MinAckDelayDraft07 = 0xFF04DE1B,
+724
+725    // <https://datatracker.ietf.org/doc/draft-seemann-quic-address-discovery/>
+726    ObservedAddr = 0x9f81a176,
+727
+728    // https://datatracker.ietf.org/doc/html/draft-ietf-quic-multipath
+729    InitialMaxPathId = 0x3e,
+730
+731    // inspired by https://www.ietf.org/archive/id/draft-seemann-quic-nat-traversal-02.html,
+732    // simplified to n0's own protocol.
+733    N0NatTraversal = 0x3d7f91120401,
+734}
+735
+736impl TransportParameterId {
+737    /// Array with all supported transport parameter IDs
+738    const SUPPORTED: [Self; 24] = [
+739        Self::MaxIdleTimeout,
+740        Self::MaxUdpPayloadSize,
+741        Self::InitialMaxData,
+742        Self::InitialMaxStreamDataBidiLocal,
+743        Self::InitialMaxStreamDataBidiRemote,
+744        Self::InitialMaxStreamDataUni,
+745        Self::InitialMaxStreamsBidi,
+746        Self::InitialMaxStreamsUni,
+747        Self::AckDelayExponent,
+748        Self::MaxAckDelay,
+749        Self::ActiveConnectionIdLimit,
+750        Self::ReservedTransportParameter,
+751        Self::StatelessResetToken,
+752        Self::DisableActiveMigration,
+753        Self::MaxDatagramFrameSize,
+754        Self::PreferredAddress,
+755        Self::OriginalDestinationConnectionId,
+756        Self::InitialSourceConnectionId,
+757        Self::RetrySourceConnectionId,
+758        Self::GreaseQuicBit,
+759        Self::MinAckDelayDraft07,
+760        Self::ObservedAddr,
+761        Self::InitialMaxPathId,
+762        Self::N0NatTraversal,
+763    ];
+764}
+765
+766impl std::cmp::PartialEq<u64> for TransportParameterId {
+767    fn eq(&self, other: &u64) -> bool {
+768        *other == (*self as u64)
+769    }
+770}
+771
+772impl TryFrom<u64> for TransportParameterId {
+773    type Error = ();
+774
+775    fn try_from(value: u64) -> Result<Self, Self::Error> {
+776        let param = match value {
+777            id if Self::MaxIdleTimeout == id => Self::MaxIdleTimeout,
+778            id if Self::MaxUdpPayloadSize == id => Self::MaxUdpPayloadSize,
+779            id if Self::InitialMaxData == id => Self::InitialMaxData,
+780            id if Self::InitialMaxStreamDataBidiLocal == id => Self::InitialMaxStreamDataBidiLocal,
+781            id if Self::InitialMaxStreamDataBidiRemote == id => {
+782                Self::InitialMaxStreamDataBidiRemote
+783            }
+784            id if Self::InitialMaxStreamDataUni == id => Self::InitialMaxStreamDataUni,
+785            id if Self::InitialMaxStreamsBidi == id => Self::InitialMaxStreamsBidi,
+786            id if Self::InitialMaxStreamsUni == id => Self::InitialMaxStreamsUni,
+787            id if Self::AckDelayExponent == id => Self::AckDelayExponent,
+788            id if Self::MaxAckDelay == id => Self::MaxAckDelay,
+789            id if Self::ActiveConnectionIdLimit == id => Self::ActiveConnectionIdLimit,
+790            id if Self::ReservedTransportParameter == id => Self::ReservedTransportParameter,
+791            id if Self::StatelessResetToken == id => Self::StatelessResetToken,
+792            id if Self::DisableActiveMigration == id => Self::DisableActiveMigration,
+793            id if Self::MaxDatagramFrameSize == id => Self::MaxDatagramFrameSize,
+794            id if Self::PreferredAddress == id => Self::PreferredAddress,
+795            id if Self::OriginalDestinationConnectionId == id => {
+796                Self::OriginalDestinationConnectionId
+797            }
+798            id if Self::InitialSourceConnectionId == id => Self::InitialSourceConnectionId,
+799            id if Self::RetrySourceConnectionId == id => Self::RetrySourceConnectionId,
+800            id if Self::GreaseQuicBit == id => Self::GreaseQuicBit,
+801            id if Self::MinAckDelayDraft07 == id => Self::MinAckDelayDraft07,
+802            id if Self::ObservedAddr == id => Self::ObservedAddr,
+803            id if Self::InitialMaxPathId == id => Self::InitialMaxPathId,
+804            id if Self::N0NatTraversal == id => Self::N0NatTraversal,
+805            _ => return Err(()),
+806        };
+807        Ok(param)
+808    }
+809}
+810
+811fn decode_cid(len: usize, value: &mut Option<ConnectionId>, r: &mut impl Buf) -> Result<(), Error> {
+812    if len > MAX_CID_SIZE || value.is_some() || r.remaining() < len {
+813        return Err(Error::Malformed);
+814    }
+815
+816    *value = Some(ConnectionId::from_buf(r, len));
+817    Ok(())
+818}
+819
+820#[cfg(test)]
+821mod test {
+822
+823    use super::*;
+824
+825    #[test]
+826    fn coding() {
+827        let mut buf = Vec::new();
+828        let params = TransportParameters {
+829            initial_src_cid: Some(ConnectionId::new(&[])),
+830            original_dst_cid: Some(ConnectionId::new(&[])),
+831            initial_max_streams_bidi: 16u32.into(),
+832            initial_max_streams_uni: 16u32.into(),
+833            ack_delay_exponent: 2u32.into(),
+834            max_udp_payload_size: 1200u32.into(),
+835            preferred_address: Some(PreferredAddress {
+836                address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
+837                address_v6: Some(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 24, 0, 0)),
+838                connection_id: ConnectionId::new(&[0x42]),
+839                stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
+840            }),
+841            grease_quic_bit: true,
+842            min_ack_delay: Some(2_000u32.into()),
+843            address_discovery_role: address_discovery::Role::SendOnly,
+844            initial_max_path_id: Some(PathId::MAX),
+845            max_remote_nat_traversal_addresses: Some(5u8.try_into().unwrap()),
+846            ..TransportParameters::default()
+847        };
+848        params.write(&mut buf);
+849        assert_eq!(
+850            TransportParameters::read(Side::Client, &mut buf.as_slice()).unwrap(),
+851            params
+852        );
+853    }
+854
+855    #[test]
+856    fn reserved_transport_parameter_generate_reserved_id() {
+857        let mut rngs = [
+858            StepRng(0),
+859            StepRng(1),
+860            StepRng(27),
+861            StepRng(31),
+862            StepRng(u32::MAX as u64),
+863            StepRng(u32::MAX as u64 - 1),
+864            StepRng(u32::MAX as u64 + 1),
+865            StepRng(u32::MAX as u64 - 27),
+866            StepRng(u32::MAX as u64 + 27),
+867            StepRng(u32::MAX as u64 - 31),
+868            StepRng(u32::MAX as u64 + 31),
+869            StepRng(u64::MAX),
+870            StepRng(u64::MAX - 1),
+871            StepRng(u64::MAX - 27),
+872            StepRng(u64::MAX - 31),
+873            StepRng(1 << 62),
+874            StepRng((1 << 62) - 1),
+875            StepRng((1 << 62) + 1),
+876            StepRng((1 << 62) - 27),
+877            StepRng((1 << 62) + 27),
+878            StepRng((1 << 62) - 31),
+879            StepRng((1 << 62) + 31),
+880        ];
+881        for rng in &mut rngs {
+882            let id = ReservedTransportParameter::generate_reserved_id(rng);
+883            assert!(id.0 % 31 == 27)
+884        }
+885    }
+886
+887    struct StepRng(u64);
+888
+889    impl rand::TryRng for StepRng {
+890        type Error = std::convert::Infallible;
+891
+892        #[inline]
+893        fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
+894            Ok(self.next_u64() as u32)
+895        }
+896
+897        #[inline]
+898        fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
+899            let res = self.0;
+900            self.0 = self.0.wrapping_add(1);
+901            Ok(res)
+902        }
+903
+904        #[inline]
+905        fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
+906            let mut left = dst;
+907            while left.len() >= 8 {
+908                let (l, r) = left.split_at_mut(8);
+909                left = r;
+910                l.copy_from_slice(&self.next_u64().to_le_bytes());
+911            }
+912            let n = left.len();
+913            if n > 0 {
+914                left.copy_from_slice(&self.next_u32().to_le_bytes()[..n]);
+915            }
+916            Ok(())
+917        }
+918    }
+919
+920    #[test]
+921    fn reserved_transport_parameter_ignored_when_read() {
+922        let mut buf = Vec::new();
+923        let reserved_parameter = ReservedTransportParameter::random(&mut rand::rng());
+924        assert!(reserved_parameter.payload_len < ReservedTransportParameter::MAX_PAYLOAD_LEN);
+925        assert!(reserved_parameter.id.0 % 31 == 27);
+926
+927        reserved_parameter.write(&mut buf);
+928        assert!(!buf.is_empty());
+929        let read_params = TransportParameters::read(Side::Server, &mut buf.as_slice()).unwrap();
+930        assert_eq!(read_params, TransportParameters::default());
+931    }
+932
+933    #[test]
+934    fn read_semantic_validation() {
+935        #[allow(clippy::type_complexity)]
+936        let illegal_params_builders: Vec<Box<dyn FnMut(&mut TransportParameters)>> = vec![
+937            Box::new(|t| {
+938                // This min_ack_delay is bigger than max_ack_delay!
+939                let min_ack_delay = t.max_ack_delay.0 * 1_000 + 1;
+940                t.min_ack_delay = Some(VarInt::from_u64(min_ack_delay).unwrap())
+941            }),
+942            Box::new(|t| {
+943                // Preferred address can only be sent by senders (and we are reading the transport
+944                // params as a client)
+945                t.preferred_address = Some(PreferredAddress {
+946                    address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
+947                    address_v6: None,
+948                    connection_id: ConnectionId::new(&[]),
+949                    stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
+950                })
+951            }),
+952        ];
+953
+954        for mut builder in illegal_params_builders {
+955            let mut buf = Vec::new();
+956            let mut params = TransportParameters::default();
+957            builder(&mut params);
+958            params.write(&mut buf);
+959
+960            assert_eq!(
+961                TransportParameters::read(Side::Server, &mut buf.as_slice()),
+962                Err(Error::IllegalValue)
+963            );
+964        }
+965    }
+966
+967    #[test]
+968    fn resumption_params_validation() {
+969        let high_limit = TransportParameters {
+970            initial_max_streams_uni: 32u32.into(),
+971            ..TransportParameters::default()
+972        };
+973        let low_limit = TransportParameters {
+974            initial_max_streams_uni: 16u32.into(),
+975            ..TransportParameters::default()
+976        };
+977        high_limit.validate_resumption_from(&low_limit).unwrap();
+978        low_limit.validate_resumption_from(&high_limit).unwrap_err();
+979    }
+980}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_proto/varint.rs.html b/pr/672/docs/src/noq_proto/varint.rs.html new file mode 100644 index 000000000..b53e4d0b6 --- /dev/null +++ b/pr/672/docs/src/noq_proto/varint.rs.html @@ -0,0 +1,233 @@ +varint.rs - source

noq_proto/
varint.rs

1use std::{convert::TryInto, fmt};
+2
+3use bytes::{Buf, BufMut};
+4use thiserror::Error;
+5
+6use crate::coding::{self, Decodable, Encodable, UnexpectedEnd};
+7
+8/// An integer less than 2^62
+9///
+10/// Values of this type are suitable for encoding as QUIC variable-length integer.
+11// It would be neat if we could express to Rust that the top two bits are available for use as enum
+12// discriminants
+13#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+14pub struct VarInt(pub(crate) u64);
+15
+16impl VarInt {
+17    /// The largest representable value
+18    pub const MAX: Self = Self((1 << 62) - 1);
+19    /// The largest encoded value length
+20    pub const MAX_SIZE: usize = 8;
+21
+22    /// Construct a `VarInt` infallibly
+23    pub const fn from_u32(x: u32) -> Self {
+24        Self(x as u64)
+25    }
+26
+27    /// Succeeds iff `x` < 2^62
+28    pub fn from_u64(x: u64) -> Result<Self, VarIntBoundsExceeded> {
+29        if x < 2u64.pow(62) {
+30            Ok(Self(x))
+31        } else {
+32            Err(VarIntBoundsExceeded)
+33        }
+34    }
+35
+36    /// Create a VarInt without ensuring it's in range
+37    ///
+38    /// # Safety
+39    ///
+40    /// `x` must be less than 2^62.
+41    pub const unsafe fn from_u64_unchecked(x: u64) -> Self {
+42        Self(x)
+43    }
+44
+45    /// Extract the integer value
+46    pub const fn into_inner(self) -> u64 {
+47        self.0
+48    }
+49
+50    /// Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead
+51    /// of overflowing.
+52    pub fn saturating_add(self, rhs: impl Into<Self>) -> Self {
+53        let rhs = rhs.into();
+54        let inner = self.0.saturating_add(rhs.0).min(Self::MAX.0);
+55        Self(inner)
+56    }
+57
+58    /// Compute the number of bytes needed to encode this value
+59    pub(crate) const fn size(self) -> usize {
+60        let x = self.0;
+61        if x < 2u64.pow(6) {
+62            1
+63        } else if x < 2u64.pow(14) {
+64            2
+65        } else if x < 2u64.pow(30) {
+66            4
+67        } else if x < 2u64.pow(62) {
+68            8
+69        } else {
+70            panic!("malformed VarInt");
+71        }
+72    }
+73}
+74
+75impl From<VarInt> for u64 {
+76    fn from(x: VarInt) -> Self {
+77        x.0
+78    }
+79}
+80
+81impl From<u8> for VarInt {
+82    fn from(x: u8) -> Self {
+83        Self(x.into())
+84    }
+85}
+86
+87impl From<u16> for VarInt {
+88    fn from(x: u16) -> Self {
+89        Self(x.into())
+90    }
+91}
+92
+93impl From<u32> for VarInt {
+94    fn from(x: u32) -> Self {
+95        Self(x.into())
+96    }
+97}
+98
+99impl std::convert::TryFrom<u64> for VarInt {
+100    type Error = VarIntBoundsExceeded;
+101    /// Succeeds iff `x` < 2^62
+102    fn try_from(x: u64) -> Result<Self, VarIntBoundsExceeded> {
+103        Self::from_u64(x)
+104    }
+105}
+106
+107impl std::convert::TryFrom<u128> for VarInt {
+108    type Error = VarIntBoundsExceeded;
+109    /// Succeeds iff `x` < 2^62
+110    fn try_from(x: u128) -> Result<Self, VarIntBoundsExceeded> {
+111        Self::from_u64(x.try_into().map_err(|_| VarIntBoundsExceeded)?)
+112    }
+113}
+114
+115impl std::convert::TryFrom<usize> for VarInt {
+116    type Error = VarIntBoundsExceeded;
+117    /// Succeeds iff `x` < 2^62
+118    fn try_from(x: usize) -> Result<Self, VarIntBoundsExceeded> {
+119        Self::try_from(x as u64)
+120    }
+121}
+122
+123impl fmt::Debug for VarInt {
+124    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+125        self.0.fmt(f)
+126    }
+127}
+128
+129impl fmt::Display for VarInt {
+130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+131        self.0.fmt(f)
+132    }
+133}
+134
+135#[cfg(feature = "arbitrary")]
+136impl<'arbitrary> arbitrary::Arbitrary<'arbitrary> for VarInt {
+137    fn arbitrary(u: &mut arbitrary::Unstructured<'arbitrary>) -> arbitrary::Result<Self> {
+138        Ok(Self(u.int_in_range(0..=Self::MAX.0)?))
+139    }
+140}
+141
+142#[cfg(test)]
+143impl proptest::arbitrary::Arbitrary for VarInt {
+144    type Parameters = ();
+145    type Strategy = proptest::strategy::BoxedStrategy<Self>;
+146
+147    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
+148        use proptest::strategy::Strategy;
+149        (0..=Self::MAX.0).prop_map(Self).boxed()
+150    }
+151}
+152
+153/// Strategy for generating a u64 in the valid VarInt range (0..2^62)
+154#[cfg(test)]
+155pub(crate) fn varint_u64() -> impl proptest::strategy::Strategy<Value = u64> {
+156    0..=VarInt::MAX.0
+157}
+158
+159/// Error returned when constructing a `VarInt` from a value >= 2^62
+160#[derive(Debug, Copy, Clone, Eq, PartialEq, Error)]
+161#[error("value too large for varint encoding")]
+162pub struct VarIntBoundsExceeded;
+163
+164impl Decodable for VarInt {
+165    fn decode<B: Buf>(r: &mut B) -> coding::Result<Self> {
+166        if !r.has_remaining() {
+167            return Err(UnexpectedEnd);
+168        }
+169        let mut buf = [0; 8];
+170        buf[0] = r.get_u8();
+171        let tag = buf[0] >> 6;
+172        buf[0] &= 0b0011_1111;
+173        let x = match tag {
+174            0b00 => u64::from(buf[0]),
+175            0b01 => {
+176                if r.remaining() < 1 {
+177                    return Err(UnexpectedEnd);
+178                }
+179                r.copy_to_slice(&mut buf[1..2]);
+180                u64::from(u16::from_be_bytes(buf[..2].try_into().unwrap()))
+181            }
+182            0b10 => {
+183                if r.remaining() < 3 {
+184                    return Err(UnexpectedEnd);
+185                }
+186                r.copy_to_slice(&mut buf[1..4]);
+187                u64::from(u32::from_be_bytes(buf[..4].try_into().unwrap()))
+188            }
+189            0b11 => {
+190                if r.remaining() < 7 {
+191                    return Err(UnexpectedEnd);
+192                }
+193                r.copy_to_slice(&mut buf[1..8]);
+194                u64::from_be_bytes(buf)
+195            }
+196            _ => unreachable!(),
+197        };
+198        Ok(Self(x))
+199    }
+200}
+201
+202impl Encodable for VarInt {
+203    fn encode<B: BufMut>(&self, w: &mut B) {
+204        let x = self.0;
+205        if x < 2u64.pow(6) {
+206            w.put_u8(x as u8);
+207        } else if x < 2u64.pow(14) {
+208            w.put_u16((0b01 << 14) | x as u16);
+209        } else if x < 2u64.pow(30) {
+210            w.put_u32((0b10 << 30) | x as u32);
+211        } else if x < 2u64.pow(62) {
+212            w.put_u64((0b11 << 62) | x);
+213        } else {
+214            unreachable!("malformed VarInt")
+215        }
+216    }
+217}
+218
+219#[cfg(test)]
+220mod tests {
+221    use super::*;
+222
+223    #[test]
+224    fn test_saturating_add() {
+225        // add within range behaves normally
+226        let large: VarInt = u32::MAX.into();
+227        let next = u64::from(u32::MAX) + 1;
+228        assert_eq!(large.saturating_add(1u8), VarInt::from_u64(next).unwrap());
+229
+230        // outside range saturates
+231        assert_eq!(VarInt::MAX.saturating_add(1u8), VarInt::MAX)
+232    }
+233}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_udp/cmsg/mod.rs.html b/pr/672/docs/src/noq_udp/cmsg/mod.rs.html new file mode 100644 index 000000000..937486b08 --- /dev/null +++ b/pr/672/docs/src/noq_udp/cmsg/mod.rs.html @@ -0,0 +1,157 @@ +mod.rs - source

noq_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/672/docs/src/noq_udp/cmsg/unix.rs.html b/pr/672/docs/src/noq_udp/cmsg/unix.rs.html new file mode 100644 index 000000000..6f4276d24 --- /dev/null +++ b/pr/672/docs/src/noq_udp/cmsg/unix.rs.html @@ -0,0 +1,81 @@ +unix.rs - source

noq_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/672/docs/src/noq_udp/lib.rs.html b/pr/672/docs/src/noq_udp/lib.rs.html new file mode 100644 index 000000000..0d8f30d7a --- /dev/null +++ b/pr/672/docs/src/noq_udp/lib.rs.html @@ -0,0 +1,302 @@ +lib.rs - source

noq_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(all(unix, not(posix_minimal)), windows))]
+42mod cmsg;
+43
+44#[cfg(all(unix, not(posix_minimal)))]
+45#[path = "unix.rs"]
+46mod imp;
+47
+48#[cfg(windows)]
+49#[path = "windows.rs"]
+50mod imp;
+51
+52// Minimal POSIX UDP for platforms without advanced socket APIs (cmsg, GSO, GRO)
+53#[cfg(posix_minimal)]
+54#[path = "posix_minimal.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 (except under Wine), Linux, Android
+117    /// (API level > 25), 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#[cfg(not(posix_minimal))]
+154impl Transmit<'_> {
+155    /// Computes the effective segment-size of the packet.
+156    ///
+157    /// Some (older) network drivers don't like being told to do GSO even if
+158    /// there is effectively only a single segment.
+159    /// (i.e. `segment_size == contents.len()`)
+160    /// Additionally, a `segment_size` that is greater than the content also
+161    /// means there is effectively only a single segment.
+162    /// This case is actually quite common when splitting up a prepared GSO batch
+163    /// again after GSO has been disabled because the last datagram in a GSO
+164    /// batch is allowed to be smaller than the segment size.
+165    #[cfg_attr(apple_fast, allow(dead_code))] // Used by prepare_msg, which is unused when apple_fast
+166    fn effective_segment_size(&self) -> Option<usize> {
+167        match self.segment_size? {
+168            size if size >= self.contents.len() => None,
+169            size => Some(size),
+170        }
+171    }
+172}
+173
+174/// Log at most 1 IO error per minute
+175#[cfg(not(wasm_browser))]
+176const IO_ERROR_LOG_INTERVAL: Duration = std::time::Duration::from_secs(60);
+177
+178/// Logs a warning message when sendmsg fails
+179///
+180/// Logging will only be performed if at least [`IO_ERROR_LOG_INTERVAL`]
+181/// has elapsed since the last error was logged.
+182#[cfg(all(not(wasm_browser), any(feature = "tracing-log", feature = "log")))]
+183fn log_sendmsg_error(
+184    last_send_error: &Mutex<Instant>,
+185    err: impl core::fmt::Debug,
+186    transmit: &Transmit<'_>,
+187) {
+188    let now = Instant::now();
+189    let last_send_error = &mut *last_send_error.lock().expect("poisend lock");
+190    if now.saturating_duration_since(*last_send_error) > IO_ERROR_LOG_INTERVAL {
+191        *last_send_error = now;
+192        log::warn!(
+193            "sendmsg error: {:?}, Transmit: {{ destination: {:?}, src_ip: {:?}, ecn: {:?}, len: {:?}, segment_size: {:?} }}",
+194            err,
+195            transmit.destination,
+196            transmit.src_ip,
+197            transmit.ecn,
+198            transmit.contents.len(),
+199            transmit.segment_size
+200        );
+201    }
+202}
+203
+204// No-op
+205#[cfg(not(any(wasm_browser, feature = "tracing-log", feature = "log")))]
+206fn log_sendmsg_error(_: &Mutex<Instant>, _: impl core::fmt::Debug, _: &Transmit<'_>) {}
+207
+208/// A borrowed UDP socket
+209///
+210/// On Unix, constructible via `From<T: AsFd>`. On Windows, constructible via `From<T:
+211/// AsSocket>`.
+212// Wrapper around socket2 to avoid making it a public dependency and incurring stability risk
+213#[cfg(not(wasm_browser))]
+214pub struct UdpSockRef<'a>(socket2::SockRef<'a>);
+215
+216#[cfg(unix)]
+217impl<'s, S> From<&'s S> for UdpSockRef<'s>
+218where
+219    S: AsFd,
+220{
+221    fn from(socket: &'s S) -> Self {
+222        Self(socket.into())
+223    }
+224}
+225
+226#[cfg(windows)]
+227impl<'s, S> From<&'s S> for UdpSockRef<'s>
+228where
+229    S: AsSocket,
+230{
+231    fn from(socket: &'s S) -> Self {
+232        Self(socket.into())
+233    }
+234}
+235
+236/// Explicit congestion notification codepoint
+237#[repr(u8)]
+238#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+239pub enum EcnCodepoint {
+240    /// The ECT(0) codepoint, indicating that an endpoint is ECN-capable
+241    Ect0 = 0b10,
+242    /// The ECT(1) codepoint, indicating that an endpoint is ECN-capable
+243    Ect1 = 0b01,
+244    /// The CE codepoint, signalling that congestion was experienced
+245    Ce = 0b11,
+246}
+247
+248impl EcnCodepoint {
+249    /// Create new object from the given bits
+250    pub fn from_bits(x: u8) -> Option<Self> {
+251        use EcnCodepoint::*;
+252        Some(match x & 0b11 {
+253            0b10 => Ect0,
+254            0b01 => Ect1,
+255            0b11 => Ce,
+256            _ => {
+257                return None;
+258            }
+259        })
+260    }
+261}
+262
+263#[cfg(all(test, not(posix_minimal)))]
+264mod tests {
+265    use std::net::Ipv4Addr;
+266
+267    use super::*;
+268
+269    #[test]
+270    fn effective_segment_size() {
+271        assert_eq!(
+272            make_transmit(&[0u8; 10], Some(15)).effective_segment_size(),
+273            None,
+274            "segment_size > content_len should yield no effective segment_size"
+275        );
+276        assert_eq!(
+277            make_transmit(&[0u8; 10], Some(10)).effective_segment_size(),
+278            None,
+279            "segment_size == content_len should yield no effective segment_size"
+280        );
+281        assert_eq!(
+282            make_transmit(&[0u8; 10], None).effective_segment_size(),
+283            None,
+284            "no segment_size should yield no effective segment_size"
+285        );
+286        assert_eq!(
+287            make_transmit(&[0u8; 10], Some(5)).effective_segment_size(),
+288            Some(5),
+289            "segment_size < content_len should yield effective segment_size"
+290        );
+291    }
+292
+293    fn make_transmit(contents: &[u8], segment_size: Option<usize>) -> Transmit<'_> {
+294        Transmit {
+295            destination: SocketAddr::from((Ipv4Addr::UNSPECIFIED, 1)),
+296            ecn: None,
+297            contents,
+298            segment_size,
+299            src_ip: None,
+300        }
+301    }
+302}
\ No newline at end of file diff --git a/pr/672/docs/src/noq_udp/unix.rs.html b/pr/672/docs/src/noq_udp/unix.rs.html new file mode 100644 index 000000000..3772fc41b --- /dev/null +++ b/pr/672/docs/src/noq_udp/unix.rs.html @@ -0,0 +1,1258 @@ +unix.rs - source

noq_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::fd::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(target_os = "freebsd")]
+38type IpTosTy = libc::c_uchar;
+39#[cfg(not(any(target_os = "freebsd", target_os = "netbsd")))]
+40type IpTosTy = libc::c_int;
+41
+42/// Tokio-compatible UDP socket with some useful specializations.
+43///
+44/// Unlike a standard tokio UDP socket, this allows ECN bits to be read and written on some
+45/// platforms.
+46#[derive(Debug)]
+47pub struct UdpSocketState {
+48    last_send_error: Mutex<Instant>,
+49    max_gso_segments: AtomicUsize,
+50    gro_segments: NonZeroUsize,
+51    may_fragment: bool,
+52
+53    /// True if we have received EINVAL error from `sendmsg` system call at least once.
+54    ///
+55    /// If enabled, we assume that old kernel is used and switch to fallback mode.
+56    /// In particular, we do not use IP_TOS cmsg_type in this case,
+57    /// which is not supported on Linux <3.13 and results in not sending the UDP packet at all.
+58    sendmsg_einval: AtomicBool,
+59
+60    /// Whether to use Apple's fast `sendmsg_x`/`recvmsg_x` APIs.
+61    ///
+62    /// These private APIs provide better performance but may not be available on all
+63    /// Apple OS versions. Callers must verify availability before enabling.
+64    #[cfg(apple_fast)]
+65    apple_fast_path: AtomicBool,
+66}
+67
+68impl UdpSocketState {
+69    pub fn new(sock: UdpSockRef<'_>) -> io::Result<Self> {
+70        let io = sock.0;
+71        let mut cmsg_platform_space = 0;
+72        if cfg!(target_os = "linux")
+73            || cfg!(bsd)
+74            || cfg!(apple)
+75            || cfg!(target_os = "android")
+76            || cfg!(solarish)
+77        {
+78            cmsg_platform_space +=
+79                unsafe { libc::CMSG_SPACE(mem::size_of::<libc::in6_pktinfo>() as _) as usize };
+80        }
+81
+82        assert!(
+83            CMSG_LEN
+84                >= unsafe { libc::CMSG_SPACE(mem::size_of::<libc::c_int>() as _) as usize }
+85                    + cmsg_platform_space
+86        );
+87        assert!(
+88            mem::align_of::<libc::cmsghdr>() <= mem::align_of::<cmsg::Aligned<[u8; 0]>>(),
+89            "control message buffers will be misaligned"
+90        );
+91
+92        io.set_nonblocking(true)?;
+93
+94        let addr = io.local_addr()?;
+95        let is_ipv4 = addr.family() == libc::AF_INET as libc::sa_family_t;
+96
+97        // mac and ios do not support IP_RECVTOS on dual-stack sockets :(
+98        // older macos versions also don't have the flag and will error out if we don't ignore it
+99        #[cfg(not(any(
+100            target_os = "openbsd",
+101            target_os = "netbsd",
+102            target_os = "dragonfly",
+103            solarish
+104        )))]
+105        if (is_ipv4 || !io.only_v6()?)
+106            && let Err(_err) =
+107                set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_RECVTOS, OPTION_ON)
+108        {
+109            crate::log::debug!("Ignoring error setting IP_RECVTOS on socket: {_err:?}");
+110        }
+111
+112        let mut may_fragment = false;
+113        #[cfg_attr(
+114            not(any(target_os = "linux", target_os = "android")),
+115            expect(unused_mut)
+116        )]
+117        let mut gro_segments = NonZeroUsize::MIN;
+118
+119        #[cfg(any(target_os = "linux", target_os = "android"))]
+120        {
+121            // Forbid IPv4 fragmentation. Set even for IPv6 to account for IPv6 mapped IPv4 addresses.
+122            // Set `may_fragment` to `true` if this option is not supported on the platform.
+123            may_fragment |= !set_socket_option_supported(
+124                &*io,
+125                libc::IPPROTO_IP,
+126                libc::IP_MTU_DISCOVER,
+127                libc::IP_PMTUDISC_PROBE,
+128            )?;
+129
+130            if is_ipv4 {
+131                set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_PKTINFO, OPTION_ON)?;
+132            } else {
+133                // Set `may_fragment` to `true` if this option is not supported on the platform.
+134                may_fragment |= !set_socket_option_supported(
+135                    &*io,
+136                    libc::IPPROTO_IPV6,
+137                    libc::IPV6_MTU_DISCOVER,
+138                    libc::IPV6_PMTUDISC_PROBE,
+139                )?;
+140            }
+141
+142            if set_socket_option(&*io, libc::SOL_UDP, libc::UDP_GRO, OPTION_ON).is_ok() {
+143                // As defined in net/ipv4/udp_offload.c
+144                // #define UDP_GRO_CNT_MAX 64
+145                //
+146                // NOTE: this MUST be set to UDP_GRO_CNT_MAX to ensure that the receive buffer size
+147                // (get_max_udp_payload_size() * gro_segments()) is large enough to hold the largest GRO
+148                // list the kernel might potentially produce. See
+149                // https://github.com/quinn-rs/quinn/pull/1354.
+150                gro_segments = NonZeroUsize::new(64).expect("known");
+151            }
+152        }
+153        #[cfg(any(target_os = "freebsd", apple))]
+154        {
+155            if is_ipv4 {
+156                // Set `may_fragment` to `true` if this option is not supported on the platform.
+157                may_fragment |= !set_socket_option_supported(
+158                    &*io,
+159                    libc::IPPROTO_IP,
+160                    libc::IP_DONTFRAG,
+161                    OPTION_ON,
+162                )?;
+163            }
+164        }
+165        #[cfg(any(bsd, apple, solarish))]
+166        // IP_RECVDSTADDR == IP_SENDSRCADDR on FreeBSD
+167        // macOS uses only IP_RECVDSTADDR, no IP_SENDSRCADDR on macOS (the same on Solaris)
+168        // macOS also supports IP_PKTINFO
+169        {
+170            if is_ipv4 {
+171                set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, OPTION_ON)?;
+172            }
+173        }
+174
+175        // Options standardized in RFC 3542
+176        if !is_ipv4 {
+177            set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, OPTION_ON)?;
+178            set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_RECVTCLASS, OPTION_ON)?;
+179            // Linux's IP_PMTUDISC_PROBE allows us to operate under interface MTU rather than the
+180            // kernel's path MTU guess, but actually disabling fragmentation requires this too. See
+181            // __ip6_append_data in ip6_output.c.
+182            // Set `may_fragment` to `true` if this option is not supported on the platform.
+183            may_fragment |= !set_socket_option_supported(
+184                &*io,
+185                libc::IPPROTO_IPV6,
+186                libc::IPV6_DONTFRAG,
+187                OPTION_ON,
+188            )?;
+189        }
+190
+191        let now = Instant::now();
+192        Ok(Self {
+193            last_send_error: Mutex::new(now.checked_sub(2 * IO_ERROR_LOG_INTERVAL).unwrap_or(now)),
+194            max_gso_segments: AtomicUsize::new(gso::max_gso_segments(&*io)),
+195            gro_segments,
+196            may_fragment,
+197            sendmsg_einval: AtomicBool::new(false),
+198            #[cfg(apple_fast)]
+199            apple_fast_path: AtomicBool::new(false),
+200        })
+201    }
+202
+203    /// Sends a [`Transmit`] on the given socket.
+204    ///
+205    /// This function will only ever return errors of kind [`io::ErrorKind::WouldBlock`].
+206    /// All other errors will be logged and converted to `Ok`.
+207    ///
+208    /// UDP transmission errors are considered non-fatal because higher-level protocols must
+209    /// employ retransmits and timeouts anyway in order to deal with UDP's unreliable nature.
+210    /// Thus, logging is most likely the only thing you can do with these errors.
+211    ///
+212    /// If you would like to handle these errors yourself, use [`UdpSocketState::try_send`]
+213    /// instead.
+214    pub fn send(&self, socket: UdpSockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+215        match send(self, socket.0, transmit) {
+216            Ok(()) => Ok(()),
+217            Err(e) if e.kind() == io::ErrorKind::WouldBlock => Err(e),
+218            // - EMSGSIZE is expected for MTU probes. Future work might be able to avoid
+219            //   these by automatically clamping the MTUD upper bound to the interface MTU.
+220            Err(e) if e.raw_os_error() == Some(libc::EMSGSIZE) => Ok(()),
+221            Err(e) => {
+222                log_sendmsg_error(&self.last_send_error, e, transmit);
+223
+224                Ok(())
+225            }
+226        }
+227    }
+228
+229    /// Sends a [`Transmit`] on the given socket without any additional error handling.
+230    pub fn try_send(&self, socket: UdpSockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+231        send(self, socket.0, transmit)
+232    }
+233
+234    #[cfg(not(any(
+235        apple,
+236        target_os = "openbsd",
+237        target_os = "netbsd",
+238        target_os = "dragonfly",
+239        solarish
+240    )))]
+241    pub fn recv(
+242        &self,
+243        socket: UdpSockRef<'_>,
+244        bufs: &mut [IoSliceMut<'_>],
+245        meta: &mut [RecvMeta],
+246    ) -> io::Result<usize> {
+247        recv_via_recvmmsg(socket.0, bufs, meta)
+248    }
+249
+250    #[cfg(apple_fast)]
+251    pub fn recv(
+252        &self,
+253        socket: UdpSockRef<'_>,
+254        bufs: &mut [IoSliceMut<'_>],
+255        meta: &mut [RecvMeta],
+256    ) -> io::Result<usize> {
+257        if self.is_apple_fast_path_enabled() {
+258            recv_via_recvmsg_x(self, socket.0, bufs, meta)
+259        } else {
+260            recv_single(socket.0, bufs, meta)
+261        }
+262    }
+263
+264    #[cfg(any(
+265        target_os = "openbsd",
+266        target_os = "netbsd",
+267        target_os = "dragonfly",
+268        solarish,
+269        apple_slow
+270    ))]
+271    pub fn recv(
+272        &self,
+273        socket: UdpSockRef<'_>,
+274        bufs: &mut [IoSliceMut<'_>],
+275        meta: &mut [RecvMeta],
+276    ) -> io::Result<usize> {
+277        recv_single(socket.0, bufs, meta)
+278    }
+279
+280    /// The maximum amount of segments which can be transmitted if a platform
+281    /// supports Generic Send Offload (GSO).
+282    ///
+283    /// This is 1 if the platform doesn't support GSO. Subject to change if errors are detected
+284    /// while using GSO.
+285    #[inline]
+286    pub fn max_gso_segments(&self) -> NonZeroUsize {
+287        self.max_gso_segments
+288            .load(Ordering::Relaxed)
+289            .try_into()
+290            .expect("must have non zero GSO segments")
+291    }
+292
+293    /// The number of segments to read when GRO is enabled. Used as a factor to
+294    /// compute the receive buffer size.
+295    ///
+296    /// Returns 1 if the platform doesn't support GRO.
+297    #[inline]
+298    pub fn gro_segments(&self) -> NonZeroUsize {
+299        self.gro_segments
+300    }
+301
+302    /// Resize the send buffer of `socket` to `bytes`
+303    #[inline]
+304    pub fn set_send_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> {
+305        socket.0.set_send_buffer_size(bytes)
+306    }
+307
+308    /// Resize the receive buffer of `socket` to `bytes`
+309    #[inline]
+310    pub fn set_recv_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> {
+311        socket.0.set_recv_buffer_size(bytes)
+312    }
+313
+314    /// Get the size of the `socket` send buffer
+315    #[inline]
+316    pub fn send_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result<usize> {
+317        socket.0.send_buffer_size()
+318    }
+319
+320    /// Get the size of the `socket` receive buffer
+321    #[inline]
+322    pub fn recv_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result<usize> {
+323        socket.0.recv_buffer_size()
+324    }
+325
+326    /// Whether transmitted datagrams might get fragmented by the IP layer
+327    ///
+328    /// Returns `false` on targets which employ e.g. the `IPV6_DONTFRAG` socket option.
+329    #[inline]
+330    pub fn may_fragment(&self) -> bool {
+331        self.may_fragment
+332    }
+333
+334    /// Returns true if we previously got an EINVAL error from `sendmsg` syscall.
+335    fn sendmsg_einval(&self) -> bool {
+336        self.sendmsg_einval.load(Ordering::Relaxed)
+337    }
+338
+339    /// Sets the flag indicating we got EINVAL error from `sendmsg` syscall.
+340    #[cfg(not(any(apple, target_os = "openbsd", target_os = "netbsd")))]
+341    fn set_sendmsg_einval(&self) {
+342        self.sendmsg_einval.store(true, Ordering::Relaxed)
+343    }
+344
+345    /// Enables Apple's fast UDP datapath using private `sendmsg_x`/`recvmsg_x` APIs.
+346    /// Once enabled, this also updates [`max_gso_segments`] to allow batched sends.
+347    ///
+348    /// # Safety
+349    ///
+350    /// These APIs may crash on unsupported OS versions, so callers must verify
+351    /// availability before enabling.
+352    ///
+353    /// [`max_gso_segments`]: Self::max_gso_segments
+354    #[cfg(apple_fast)]
+355    pub unsafe fn set_apple_fast_path(&self) {
+356        self.apple_fast_path.store(true, Ordering::Relaxed);
+357        self.max_gso_segments.store(BATCH_SIZE, Ordering::Relaxed);
+358    }
+359
+360    /// Returns whether Apple's fast UDP datapath is enabled for this socket.
+361    #[cfg(apple_fast)]
+362    pub fn is_apple_fast_path_enabled(&self) -> bool {
+363        self.apple_fast_path.load(Ordering::Relaxed)
+364    }
+365
+366    /// Disables Apple's fast UDP datapath, reverting to `sendmsg`/`recvmsg`.
+367    #[cfg(apple_fast)]
+368    fn disable_apple_fast_path(&self) {
+369        self.apple_fast_path.store(false, Ordering::Relaxed);
+370        self.max_gso_segments.store(1, Ordering::Relaxed);
+371    }
+372
+373    /// Resolves an Apple fast-path function pointer via `resolver`, disabling the fast path if
+374    /// the symbol is absent so that future calls use the slow path directly.
+375    #[cfg(apple_fast)]
+376    fn resolve_apple_fast_fn<T>(&self, resolver: fn() -> Option<T>) -> Option<T> {
+377        let f = resolver();
+378        if f.is_none() {
+379            self.disable_apple_fast_path();
+380        }
+381        f
+382    }
+383}
+384
+385#[cfg(not(any(apple, target_os = "openbsd", target_os = "netbsd")))]
+386fn send(
+387    #[allow(unused_variables)] // only used on Linux
+388    state: &UdpSocketState,
+389    io: SockRef<'_>,
+390    transmit: &Transmit<'_>,
+391) -> io::Result<()> {
+392    #[allow(unused_mut)] // only mutable on FreeBSD
+393    let mut encode_src_ip = true;
+394    #[cfg(target_os = "freebsd")]
+395    {
+396        let addr = io.local_addr()?;
+397        let is_ipv4 = addr.family() == libc::AF_INET as libc::sa_family_t;
+398        if is_ipv4 {
+399            if let Some(socket) = addr.as_socket_ipv4() {
+400                encode_src_ip = socket.ip() == &Ipv4Addr::UNSPECIFIED;
+401            }
+402        }
+403    }
+404    let mut msg_hdr: libc::msghdr = unsafe { mem::zeroed() };
+405    let mut iovec: libc::iovec = unsafe { mem::zeroed() };
+406    let mut cmsgs = cmsg::Aligned([0u8; CMSG_LEN]);
+407    let dst_addr = socket2::SockAddr::from(transmit.destination);
+408    prepare_msg(
+409        transmit,
+410        &dst_addr,
+411        &mut msg_hdr,
+412        &mut iovec,
+413        &mut cmsgs,
+414        encode_src_ip,
+415        state.sendmsg_einval(),
+416    );
+417
+418    loop {
+419        let n = unsafe { libc::sendmsg(io.as_raw_fd(), &msg_hdr, 0) };
+420
+421        if n >= 0 {
+422            return Ok(());
+423        }
+424
+425        let e = io::Error::last_os_error();
+426        match e.kind() {
+427            // Retry the transmission
+428            io::ErrorKind::Interrupted => continue,
+429            io::ErrorKind::WouldBlock => return Err(e),
+430            _ => {
+431                // Some network adapters and drivers do not support GSO. Unfortunately, Linux
+432                // offers no easy way for us to detect this short of an EIO or sometimes EINVAL
+433                // when we try to actually send datagrams using it.
+434                #[cfg(any(target_os = "linux", target_os = "android"))]
+435                if let Some(libc::EIO) | Some(libc::EINVAL) = e.raw_os_error() {
+436                    // Prevent new transmits from being scheduled using GSO. Existing GSO transmits
+437                    // may already be in the pipeline, so we need to tolerate additional failures.
+438                    if state.max_gso_segments().get() > 1 {
+439                        crate::log::info!(
+440                            "`libc::sendmsg` failed with {e}; halting segmentation offload"
+441                        );
+442                        state
+443                            .max_gso_segments
+444                            .store(1, std::sync::atomic::Ordering::Relaxed);
+445                    }
+446                }
+447
+448                // Some arguments to `sendmsg` are not supported. Switch to
+449                // fallback mode and retry if we haven't already.
+450                if e.raw_os_error() == Some(libc::EINVAL) && !state.sendmsg_einval() {
+451                    state.set_sendmsg_einval();
+452                    prepare_msg(
+453                        transmit,
+454                        &dst_addr,
+455                        &mut msg_hdr,
+456                        &mut iovec,
+457                        &mut cmsgs,
+458                        encode_src_ip,
+459                        state.sendmsg_einval(),
+460                    );
+461                    continue;
+462                }
+463
+464                return Err(e);
+465            }
+466        }
+467    }
+468}
+469
+470#[cfg(apple_fast)]
+471fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+472    if state.is_apple_fast_path_enabled() {
+473        send_via_sendmsg_x(state, io, transmit)
+474    } else {
+475        send_single(state, io, transmit)
+476    }
+477}
+478
+479/// Send using the fast `sendmsg_x` API.
+480#[cfg(apple_fast)]
+481fn send_via_sendmsg_x(
+482    state: &UdpSocketState,
+483    io: SockRef<'_>,
+484    transmit: &Transmit<'_>,
+485) -> io::Result<()> {
+486    let mut hdrs = unsafe { mem::zeroed::<[msghdr_x; BATCH_SIZE]>() };
+487    let mut iovs = unsafe { mem::zeroed::<[libc::iovec; BATCH_SIZE]>() };
+488    let mut ctrls = [cmsg::Aligned([0u8; CMSG_LEN]); BATCH_SIZE];
+489    let addr = socket2::SockAddr::from(transmit.destination);
+490    let segment_size = transmit.segment_size.unwrap_or(transmit.contents.len());
+491    let mut cnt = 0;
+492    debug_assert!(transmit.contents.len().div_ceil(segment_size) <= BATCH_SIZE);
+493    for (i, chunk) in transmit
+494        .contents
+495        .chunks(segment_size)
+496        .enumerate()
+497        .take(BATCH_SIZE)
+498    {
+499        prepare_msg_x(
+500            &Transmit {
+501                destination: transmit.destination,
+502                ecn: transmit.ecn,
+503                contents: chunk,
+504                segment_size: Some(chunk.len()),
+505                src_ip: transmit.src_ip,
+506            },
+507            &addr,
+508            &mut hdrs[i],
+509            &mut iovs[i],
+510            &mut ctrls[i],
+511            true,
+512            state.sendmsg_einval(),
+513        );
+514        hdrs[i].msg_datalen = chunk.len();
+515        cnt += 1;
+516    }
+517    let Some(sendmsg_x) = state.resolve_apple_fast_fn(sendmsg_x_fn) else {
+518        return send_single(state, io, transmit);
+519    };
+520    retry_if_interrupted(|| unsafe { sendmsg_x(io.as_raw_fd(), hdrs.as_ptr(), cnt as u32, 0) })?;
+521    Ok(())
+522}
+523
+524#[cfg(any(target_os = "openbsd", target_os = "netbsd", apple_slow))]
+525fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+526    send_single(state, io, transmit)
+527}
+528
+529#[cfg(any(target_os = "openbsd", target_os = "netbsd", apple))]
+530#[cfg_attr(apple_fast, allow(dead_code))] // Unused when apple_fast is enabled
+531fn send_single(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+532    let mut hdr: libc::msghdr = unsafe { mem::zeroed() };
+533    let mut iov: libc::iovec = unsafe { mem::zeroed() };
+534    let mut ctrl = cmsg::Aligned([0u8; CMSG_LEN]);
+535    let addr = socket2::SockAddr::from(transmit.destination);
+536    prepare_msg(
+537        transmit,
+538        &addr,
+539        &mut hdr,
+540        &mut iov,
+541        &mut ctrl,
+542        cfg!(apple) || cfg!(target_os = "openbsd") || cfg!(target_os = "netbsd"),
+543        state.sendmsg_einval(),
+544    );
+545    retry_if_interrupted(|| unsafe { libc::sendmsg(io.as_raw_fd(), &hdr, 0) })?;
+546    Ok(())
+547}
+548
+549/// Receive using the batched `recvmmsg` syscall.
+550#[cfg(not(any(
+551    apple,
+552    target_os = "openbsd",
+553    target_os = "netbsd",
+554    target_os = "dragonfly",
+555    solarish
+556)))]
+557fn recv_via_recvmmsg(
+558    io: SockRef<'_>,
+559    bufs: &mut [IoSliceMut<'_>],
+560    meta: &mut [RecvMeta],
+561) -> io::Result<usize> {
+562    let mut names = [MaybeUninit::<libc::sockaddr_storage>::uninit(); BATCH_SIZE];
+563    let mut ctrls = [cmsg::Aligned(MaybeUninit::<[u8; CMSG_LEN]>::uninit()); BATCH_SIZE];
+564    let mut hdrs = unsafe { mem::zeroed::<[libc::mmsghdr; BATCH_SIZE]>() };
+565    let max_msg_count = bufs.len().min(BATCH_SIZE);
+566    for i in 0..max_msg_count {
+567        prepare_recv(
+568            &mut bufs[i],
+569            &mut names[i],
+570            &mut ctrls[i],
+571            &mut hdrs[i].msg_hdr,
+572        );
+573    }
+574    let msg_count = retry_if_interrupted(|| unsafe {
+575        libc::recvmmsg(
+576            io.as_raw_fd(),
+577            hdrs.as_mut_ptr(),
+578            bufs.len().min(BATCH_SIZE) as _,
+579            0,
+580            ptr::null_mut::<libc::timespec>(),
+581        ) as isize
+582    })?;
+583    for i in 0..(msg_count as usize) {
+584        meta[i] = decode_recv(&names[i], &hdrs[i].msg_hdr, hdrs[i].msg_len as usize)?;
+585    }
+586    Ok(msg_count as usize)
+587}
+588
+589/// Receive using the fast `recvmsg_x` API.
+590#[cfg(apple_fast)]
+591fn recv_via_recvmsg_x(
+592    state: &UdpSocketState,
+593    io: SockRef<'_>,
+594    bufs: &mut [IoSliceMut<'_>],
+595    meta: &mut [RecvMeta],
+596) -> io::Result<usize> {
+597    let mut names = [MaybeUninit::<libc::sockaddr_storage>::uninit(); BATCH_SIZE];
+598    // MacOS 10.15 `recvmsg_x` does not override the `msghdr_x`
+599    // `msg_controllen`. Thus, after the call to `recvmsg_x`, one does not know
+600    // which control messages have been written to. To prevent reading
+601    // uninitialized memory, do not use `MaybeUninit` for `ctrls`, instead
+602    // initialize `ctrls` with `0`s. A control message of all `0`s is
+603    // automatically skipped by `libc::CMSG_NXTHDR`.
+604    let mut ctrls = [cmsg::Aligned([0u8; CMSG_LEN]); BATCH_SIZE];
+605    let mut hdrs = unsafe { mem::zeroed::<[msghdr_x; BATCH_SIZE]>() };
+606    let max_msg_count = bufs.len().min(BATCH_SIZE);
+607    for i in 0..max_msg_count {
+608        prepare_recv_x(&mut bufs[i], &mut names[i], &mut ctrls[i], &mut hdrs[i]);
+609    }
+610    let Some(recvmsg_x) = state.resolve_apple_fast_fn(recvmsg_x_fn) else {
+611        return recv_single(io, bufs, meta);
+612    };
+613    let msg_count = retry_if_interrupted(|| unsafe {
+614        recvmsg_x(io.as_raw_fd(), hdrs.as_mut_ptr(), max_msg_count as _, 0)
+615    })?;
+616    for i in 0..(msg_count as usize) {
+617        meta[i] = decode_recv(&names[i], &hdrs[i], hdrs[i].msg_datalen as usize)?;
+618    }
+619    Ok(msg_count as usize)
+620}
+621
+622/// Returns the `sendmsg_x` function pointer, resolving it via `dlsym` on first call.
+623///
+624/// Returns `None` if the symbol is not available on the current OS version.
+625#[cfg(apple_fast)]
+626fn sendmsg_x_fn() -> Option<SendmsgXFn> {
+627    static ADDR: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
+628    // SAFETY: `resolve_symbol` only returns non-zero addresses obtained from `dlsym`, which
+629    // guarantees a callable symbol whose type matches the declaration above.
+630    resolve_symbol(&ADDR, c"sendmsg_x")
+631        .map(|addr| unsafe { std::mem::transmute::<usize, SendmsgXFn>(addr) })
+632}
+633
+634/// Returns the `recvmsg_x` function pointer, resolving it via `dlsym` on first call.
+635///
+636/// Returns `None` if the symbol is not available on the current OS version.
+637#[cfg(apple_fast)]
+638fn recvmsg_x_fn() -> Option<RecvmsgXFn> {
+639    static ADDR: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
+640    // SAFETY: `resolve_symbol` only returns non-zero addresses obtained from `dlsym`, which
+641    // guarantees a callable symbol whose type matches the declaration above.
+642    resolve_symbol(&ADDR, c"recvmsg_x")
+643        .map(|addr| unsafe { std::mem::transmute::<usize, RecvmsgXFn>(addr) })
+644}
+645
+646#[cfg(apple_fast)]
+647type SendmsgXFn =
+648    unsafe extern "C" fn(libc::c_int, *const msghdr_x, libc::c_uint, libc::c_int) -> isize;
+649#[cfg(apple_fast)]
+650type RecvmsgXFn =
+651    unsafe extern "C" fn(libc::c_int, *mut msghdr_x, libc::c_uint, libc::c_int) -> isize;
+652
+653/// Resolves a symbol via `dlsym` on first call, caching the result.
+654///
+655/// Returns `None` if the symbol is not available on the current OS version.
+656#[cfg(apple_fast)]
+657fn resolve_symbol(lock: &std::sync::OnceLock<usize>, name: &std::ffi::CStr) -> Option<usize> {
+658    let addr =
+659        *lock.get_or_init(|| unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize });
+660    (addr != 0).then_some(addr)
+661}
+662
+663#[cfg(any(
+664    target_os = "openbsd",
+665    target_os = "netbsd",
+666    target_os = "dragonfly",
+667    solarish,
+668    apple
+669))]
+670#[cfg_attr(apple_fast, allow(dead_code))] // Unused when apple_fast is enabled
+671fn recv_single(
+672    io: SockRef<'_>,
+673    bufs: &mut [IoSliceMut<'_>],
+674    meta: &mut [RecvMeta],
+675) -> io::Result<usize> {
+676    let mut name = MaybeUninit::<libc::sockaddr_storage>::uninit();
+677    let mut ctrl = cmsg::Aligned(MaybeUninit::<[u8; CMSG_LEN]>::uninit());
+678    let mut hdr = unsafe { mem::zeroed::<libc::msghdr>() };
+679    prepare_recv(&mut bufs[0], &mut name, &mut ctrl, &mut hdr);
+680    let n = loop {
+681        let n = unsafe { libc::recvmsg(io.as_raw_fd(), &mut hdr, 0) };
+682
+683        if hdr.msg_flags & libc::MSG_TRUNC != 0 {
+684            continue;
+685        }
+686
+687        if n >= 0 {
+688            break n;
+689        }
+690
+691        let e = io::Error::last_os_error();
+692        match e.kind() {
+693            // Retry receiving
+694            io::ErrorKind::Interrupted => continue,
+695            _ => return Err(e),
+696        }
+697    };
+698    meta[0] = decode_recv(&name, &hdr, n as usize)?;
+699    Ok(1)
+700}
+701
+702const CMSG_LEN: usize = 88;
+703
+704#[cfg_attr(apple_fast, allow(dead_code))] // Unused when apple_fast is enabled
+705fn prepare_msg(
+706    transmit: &Transmit<'_>,
+707    dst_addr: &socket2::SockAddr,
+708    hdr: &mut libc::msghdr,
+709    iov: &mut libc::iovec,
+710    ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>,
+711    #[allow(unused_variables)] // only used on FreeBSD & macOS
+712    encode_src_ip: bool,
+713    sendmsg_einval: bool,
+714) {
+715    iov.iov_base = transmit.contents.as_ptr() as *const _ as *mut _;
+716    iov.iov_len = transmit.contents.len();
+717
+718    // SAFETY: Casting the pointer to a mutable one is legal,
+719    // as sendmsg is guaranteed to not alter the mutable pointer
+720    // as per the POSIX spec. See the section on the sys/socket.h
+721    // header for details. The type is only mutable in the first
+722    // place because it is reused by recvmsg as well.
+723    let name = dst_addr.as_ptr() as *mut libc::c_void;
+724    let namelen = dst_addr.len();
+725    hdr.msg_name = name as *mut _;
+726    hdr.msg_namelen = namelen;
+727    hdr.msg_iov = iov;
+728    hdr.msg_iovlen = 1;
+729
+730    hdr.msg_control = ctrl.0.as_mut_ptr() as _;
+731    hdr.msg_controllen = CMSG_LEN as _;
+732    let mut encoder = unsafe { cmsg::Encoder::new(hdr) };
+733    let ecn = transmit.ecn.map_or(0, |x| x as libc::c_int);
+734    // True for IPv4 or IPv4-Mapped IPv6
+735    let is_ipv4 = transmit.destination.is_ipv4()
+736        || matches!(transmit.destination.ip(), IpAddr::V6(addr) if addr.to_ipv4_mapped().is_some());
+737    if is_ipv4 {
+738        if !sendmsg_einval {
+739            #[cfg(not(target_os = "netbsd"))]
+740            {
+741                encoder.push(libc::IPPROTO_IP, libc::IP_TOS, ecn as IpTosTy);
+742            }
+743        }
+744    } else {
+745        encoder.push(libc::IPPROTO_IPV6, libc::IPV6_TCLASS, ecn);
+746    }
+747
+748    // On apple_fast, prepare_msg is only compiled for send_single (fallback path), while the main
+749    // send path uses prepare_msg_x with msghdr_x. gso::set_segment_size has a different signature
+750    // when apple_fast is enabled, and it's a no-op on non-Linux platforms anyway.
+751    #[cfg(not(apple_fast))]
+752    if let Some(segment_size) = transmit.effective_segment_size() {
+753        gso::set_segment_size(&mut encoder, segment_size as u16);
+754    }
+755
+756    if let Some(ip) = &transmit.src_ip {
+757        match ip {
+758            IpAddr::V4(v4) => {
+759                #[cfg(any(target_os = "linux", target_os = "android"))]
+760                {
+761                    let pktinfo = libc::in_pktinfo {
+762                        ipi_ifindex: 0,
+763                        ipi_spec_dst: libc::in_addr {
+764                            s_addr: u32::from_ne_bytes(v4.octets()),
+765                        },
+766                        ipi_addr: libc::in_addr { s_addr: 0 },
+767                    };
+768                    encoder.push(libc::IPPROTO_IP, libc::IP_PKTINFO, pktinfo);
+769                }
+770                #[cfg(any(bsd, apple, solarish))]
+771                {
+772                    if encode_src_ip {
+773                        let addr = libc::in_addr {
+774                            s_addr: u32::from_ne_bytes(v4.octets()),
+775                        };
+776                        encoder.push(libc::IPPROTO_IP, libc::IP_RECVDSTADDR, addr);
+777                    }
+778                }
+779            }
+780            IpAddr::V6(v6) => {
+781                let pktinfo = libc::in6_pktinfo {
+782                    ipi6_ifindex: 0,
+783                    ipi6_addr: libc::in6_addr {
+784                        s6_addr: v6.octets(),
+785                    },
+786                };
+787                encoder.push(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO, pktinfo);
+788            }
+789        }
+790    }
+791
+792    encoder.finish();
+793}
+794
+795/// Prepares an `msghdr_x` for use with `sendmsg_x`.
+796#[cfg(apple_fast)]
+797fn prepare_msg_x(
+798    transmit: &Transmit<'_>,
+799    dst_addr: &socket2::SockAddr,
+800    hdr: &mut msghdr_x,
+801    iov: &mut libc::iovec,
+802    ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>,
+803    #[allow(unused_variables)] encode_src_ip: bool,
+804    sendmsg_einval: bool,
+805) {
+806    iov.iov_base = transmit.contents.as_ptr() as *const _ as *mut _;
+807    iov.iov_len = transmit.contents.len();
+808
+809    let name = dst_addr.as_ptr() as *mut libc::c_void;
+810    let namelen = dst_addr.len();
+811    hdr.msg_name = name as *mut _;
+812    hdr.msg_namelen = namelen;
+813    hdr.msg_iov = iov;
+814    hdr.msg_iovlen = 1;
+815
+816    hdr.msg_control = ctrl.0.as_mut_ptr() as _;
+817    hdr.msg_controllen = CMSG_LEN as _;
+818    let mut encoder = unsafe { cmsg::Encoder::new(hdr) };
+819    let ecn = transmit.ecn.map_or(0, |x| x as libc::c_int);
+820    let is_ipv4 = transmit.destination.is_ipv4()
+821        || matches!(transmit.destination.ip(), IpAddr::V6(addr) if addr.to_ipv4_mapped().is_some());
+822    if is_ipv4 {
+823        if !sendmsg_einval {
+824            encoder.push(libc::IPPROTO_IP, libc::IP_TOS, ecn as IpTosTy);
+825        }
+826    } else {
+827        encoder.push(libc::IPPROTO_IPV6, libc::IPV6_TCLASS, ecn);
+828    }
+829
+830    if let Some(ip) = &transmit.src_ip {
+831        match ip {
+832            IpAddr::V4(v4) => {
+833                if encode_src_ip {
+834                    let addr = libc::in_addr {
+835                        s_addr: u32::from_ne_bytes(v4.octets()),
+836                    };
+837                    encoder.push(libc::IPPROTO_IP, libc::IP_RECVDSTADDR, addr);
+838                }
+839            }
+840            IpAddr::V6(v6) => {
+841                let pktinfo = libc::in6_pktinfo {
+842                    ipi6_ifindex: 0,
+843                    ipi6_addr: libc::in6_addr {
+844                        s6_addr: v6.octets(),
+845                    },
+846                };
+847                encoder.push(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO, pktinfo);
+848            }
+849        }
+850    }
+851
+852    encoder.finish();
+853}
+854
+855#[cfg_attr(apple_fast, allow(dead_code))] // Unused when apple_fast is enabled
+856fn prepare_recv(
+857    buf: &mut IoSliceMut<'_>,
+858    name: &mut MaybeUninit<libc::sockaddr_storage>,
+859    ctrl: &mut cmsg::Aligned<MaybeUninit<[u8; CMSG_LEN]>>,
+860    hdr: &mut libc::msghdr,
+861) {
+862    hdr.msg_name = name.as_mut_ptr() as _;
+863    hdr.msg_namelen = mem::size_of::<libc::sockaddr_storage>() as _;
+864    hdr.msg_iov = buf as *mut IoSliceMut<'_> as *mut libc::iovec;
+865    hdr.msg_iovlen = 1;
+866    hdr.msg_control = ctrl.0.as_mut_ptr() as _;
+867    hdr.msg_controllen = CMSG_LEN as _;
+868    hdr.msg_flags = 0;
+869}
+870
+871/// Prepares an `msghdr_x` for receiving with `recvmsg_x`.
+872#[cfg(apple_fast)]
+873fn prepare_recv_x(
+874    buf: &mut IoSliceMut<'_>,
+875    name: &mut MaybeUninit<libc::sockaddr_storage>,
+876    ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>,
+877    hdr: &mut msghdr_x,
+878) {
+879    hdr.msg_name = name.as_mut_ptr() as _;
+880    hdr.msg_namelen = mem::size_of::<libc::sockaddr_storage>() as _;
+881    hdr.msg_iov = buf as *mut IoSliceMut<'_> as *mut libc::iovec;
+882    hdr.msg_iovlen = 1;
+883    hdr.msg_control = ctrl.0.as_mut_ptr() as _;
+884    hdr.msg_controllen = CMSG_LEN as _;
+885    hdr.msg_flags = 0;
+886    hdr.msg_datalen = buf.len();
+887}
+888
+889fn decode_recv<M: cmsg::MsgHdr<ControlMessage = libc::cmsghdr>>(
+890    name: &MaybeUninit<libc::sockaddr_storage>,
+891    hdr: &M,
+892    len: usize,
+893) -> io::Result<RecvMeta> {
+894    let name = unsafe { name.assume_init() };
+895    let mut ctrl = ControlMetadata {
+896        ecn_bits: 0,
+897        dst_ip: None,
+898        interface_index: None,
+899        stride: len,
+900    };
+901
+902    let cmsg_iter = unsafe { cmsg::Iter::new(hdr) };
+903    for cmsg in cmsg_iter {
+904        ctrl.decode(cmsg);
+905    }
+906
+907    Ok(RecvMeta {
+908        len,
+909        stride: ctrl.stride,
+910        addr: decode_socket_addr(&name)?,
+911        ecn: EcnCodepoint::from_bits(ctrl.ecn_bits),
+912        dst_ip: ctrl.dst_ip,
+913        interface_index: ctrl.interface_index,
+914    })
+915}
+916
+917/// Metadata decoded from control messages
+918struct ControlMetadata {
+919    ecn_bits: u8,
+920    dst_ip: Option<IpAddr>,
+921    interface_index: Option<u32>,
+922    stride: usize,
+923}
+924
+925impl ControlMetadata {
+926    /// Decodes a control message and updates the metadata state
+927    fn decode(&mut self, cmsg: &libc::cmsghdr) {
+928        match (cmsg.cmsg_level, cmsg.cmsg_type) {
+929            (libc::IPPROTO_IP, libc::IP_TOS) => unsafe {
+930                self.ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
+931            },
+932            // FreeBSD uses IP_RECVTOS here, and we can be liberal because cmsgs are opt-in.
+933            #[cfg(not(any(
+934                target_os = "openbsd",
+935                target_os = "netbsd",
+936                target_os = "dragonfly",
+937                solarish
+938            )))]
+939            (libc::IPPROTO_IP, libc::IP_RECVTOS) => unsafe {
+940                self.ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
+941            },
+942            (libc::IPPROTO_IPV6, libc::IPV6_TCLASS) => unsafe {
+943                // Temporary hack around broken macos ABI. Remove once upstream fixes it.
+944                // https://bugreport.apple.com/web/?problemID=48761855
+945                #[allow(clippy::unnecessary_cast)] // cmsg.cmsg_len defined as size_t
+946                if cfg!(apple)
+947                    && cmsg.cmsg_len as usize == libc::CMSG_LEN(mem::size_of::<u8>() as _) as usize
+948                {
+949                    self.ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
+950                } else {
+951                    self.ecn_bits = cmsg::decode::<libc::c_int, libc::cmsghdr>(cmsg) as u8;
+952                }
+953            },
+954            #[cfg(any(target_os = "linux", target_os = "android"))]
+955            (libc::IPPROTO_IP, libc::IP_PKTINFO) => {
+956                let pktinfo = unsafe { cmsg::decode::<libc::in_pktinfo, libc::cmsghdr>(cmsg) };
+957                self.dst_ip = Some(IpAddr::V4(Ipv4Addr::from(
+958                    pktinfo.ipi_addr.s_addr.to_ne_bytes(),
+959                )));
+960                self.interface_index = Some(pktinfo.ipi_ifindex as u32);
+961            }
+962            #[cfg(any(bsd, apple))]
+963            (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
+964                let in_addr = unsafe { cmsg::decode::<libc::in_addr, libc::cmsghdr>(cmsg) };
+965                self.dst_ip = Some(IpAddr::V4(Ipv4Addr::from(in_addr.s_addr.to_ne_bytes())));
+966            }
+967            (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => {
+968                let pktinfo = unsafe { cmsg::decode::<libc::in6_pktinfo, libc::cmsghdr>(cmsg) };
+969                self.dst_ip = Some(IpAddr::V6(Ipv6Addr::from(pktinfo.ipi6_addr.s6_addr)));
+970                #[cfg_attr(not(target_os = "android"), expect(clippy::unnecessary_cast))]
+971                {
+972                    self.interface_index = Some(pktinfo.ipi6_ifindex as u32);
+973                }
+974            }
+975            #[cfg(any(target_os = "linux", target_os = "android"))]
+976            (libc::SOL_UDP, libc::UDP_GRO) => unsafe {
+977                self.stride = cmsg::decode::<libc::c_int, libc::cmsghdr>(cmsg) as usize;
+978            },
+979            _ => {}
+980        }
+981    }
+982}
+983
+984/// Decodes a `sockaddr_storage` into a `SocketAddr`
+985fn decode_socket_addr(name: &libc::sockaddr_storage) -> io::Result<SocketAddr> {
+986    match libc::c_int::from(name.ss_family) {
+987        libc::AF_INET => {
+988            // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in.
+989            let addr: &libc::sockaddr_in =
+990                unsafe { &*(name as *const _ as *const libc::sockaddr_in) };
+991            Ok(SocketAddr::V4(SocketAddrV4::new(
+992                Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes()),
+993                u16::from_be(addr.sin_port),
+994            )))
+995        }
+996        libc::AF_INET6 => {
+997            // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6.
+998            let addr: &libc::sockaddr_in6 =
+999                unsafe { &*(name as *const _ as *const libc::sockaddr_in6) };
+1000            Ok(SocketAddr::V6(SocketAddrV6::new(
+1001                Ipv6Addr::from(addr.sin6_addr.s6_addr),
+1002                u16::from_be(addr.sin6_port),
+1003                addr.sin6_flowinfo,
+1004                addr.sin6_scope_id,
+1005            )))
+1006        }
+1007        f => Err(io::Error::other(format!(
+1008            "expected AF_INET or AF_INET6, got {f}"
+1009        ))),
+1010    }
+1011}
+1012
+1013#[cfg(not(apple_slow))]
+1014// Chosen somewhat arbitrarily; might benefit from additional tuning.
+1015pub(crate) const BATCH_SIZE: usize = 32;
+1016
+1017#[cfg(apple_slow)]
+1018pub(crate) const BATCH_SIZE: usize = 1;
+1019
+1020#[cfg(any(target_os = "linux", target_os = "android"))]
+1021mod gso {
+1022    use super::*;
+1023    use std::{ffi::CStr, mem, str::FromStr, sync::OnceLock};
+1024
+1025    // Support for UDP GSO has been added to linux kernel in version 4.18
+1026    // https://github.com/torvalds/linux/commit/cb586c63e3fc5b227c51fd8c4cb40b34d3750645
+1027    const SUPPORTED_SINCE: KernelVersion = KernelVersion {
+1028        version: 4,
+1029        major_revision: 18,
+1030    };
+1031
+1032    /// Checks whether GSO support is available by checking the kernel version followed by setting
+1033    /// the UDP_SEGMENT option on a socket
+1034    pub(crate) fn max_gso_segments(socket: &impl AsRawFd) -> usize {
+1035        const GSO_SIZE: libc::c_int = 1500;
+1036
+1037        if !SUPPORTED_BY_CURRENT_KERNEL.get_or_init(supported_by_current_kernel) {
+1038            return 1;
+1039        }
+1040
+1041        // As defined in linux/udp.h
+1042        // #define UDP_MAX_SEGMENTS        (1 << 6UL)
+1043        match set_socket_option(socket, libc::SOL_UDP, libc::UDP_SEGMENT, GSO_SIZE) {
+1044            Ok(()) => {
+1045                // Disable GSO again globally to ensure we can selectively enable it via cmsg.
+1046                // See:
+1047                // - https://github.com/quinn-rs/quinn/issues/2575
+1048                // - https://man7.org/linux/man-pages/man7/udp.7.html
+1049                let _ = set_socket_option(socket, libc::SOL_UDP, libc::UDP_SEGMENT, 0);
+1050
+1051                64
+1052            }
+1053            Err(_e) => {
+1054                crate::log::debug!(
+1055                    "failed to set `UDP_SEGMENT` socket option ({_e}); setting `max_gso_segments = 1`"
+1056                );
+1057
+1058                1
+1059            }
+1060        }
+1061    }
+1062
+1063    pub(crate) fn set_segment_size(
+1064        encoder: &mut cmsg::Encoder<'_, libc::msghdr>,
+1065        segment_size: u16,
+1066    ) {
+1067        encoder.push(libc::SOL_UDP, libc::UDP_SEGMENT, segment_size);
+1068    }
+1069
+1070    // Avoid calling `supported_by_current_kernel` for each socket by using `OnceLock`.
+1071    static SUPPORTED_BY_CURRENT_KERNEL: OnceLock<bool> = OnceLock::new();
+1072
+1073    fn supported_by_current_kernel() -> bool {
+1074        let kernel_version_string = match kernel_version_string() {
+1075            Ok(kernel_version_string) => kernel_version_string,
+1076            Err(_e) => {
+1077                crate::log::warn!("GSO disabled: uname returned {_e}");
+1078                return false;
+1079            }
+1080        };
+1081
+1082        let Some(kernel_version) = KernelVersion::from_str(&kernel_version_string) else {
+1083            crate::log::warn!(
+1084                "GSO disabled: failed to parse kernel version ({kernel_version_string})"
+1085            );
+1086            return false;
+1087        };
+1088
+1089        if kernel_version < SUPPORTED_SINCE {
+1090            crate::log::info!("GSO disabled: kernel too old ({kernel_version_string}); need 4.18+",);
+1091            return false;
+1092        }
+1093
+1094        true
+1095    }
+1096
+1097    fn kernel_version_string() -> io::Result<String> {
+1098        let mut n = unsafe { mem::zeroed() };
+1099        let r = unsafe { libc::uname(&mut n) };
+1100        if r != 0 {
+1101            return Err(io::Error::last_os_error());
+1102        }
+1103        Ok(unsafe {
+1104            CStr::from_ptr(n.release[..].as_ptr())
+1105                .to_string_lossy()
+1106                .into_owned()
+1107        })
+1108    }
+1109
+1110    // https://www.linfo.org/kernel_version_numbering.html
+1111    #[derive(Eq, PartialEq, Ord, PartialOrd, Debug)]
+1112    struct KernelVersion {
+1113        version: u8,
+1114        major_revision: u8,
+1115    }
+1116
+1117    impl KernelVersion {
+1118        fn from_str(release: &str) -> Option<Self> {
+1119            let mut split = release
+1120                .split_once('-')
+1121                .map(|pair| pair.0)
+1122                .unwrap_or(release)
+1123                .split('.');
+1124
+1125            let version = u8::from_str(split.next()?).ok()?;
+1126            let major_revision = u8::from_str(split.next()?).ok()?;
+1127
+1128            Some(Self {
+1129                version,
+1130                major_revision,
+1131            })
+1132        }
+1133    }
+1134
+1135    #[cfg(test)]
+1136    mod test {
+1137        use super::*;
+1138
+1139        #[test]
+1140        fn parse_current_kernel_version_release_string() {
+1141            let release = kernel_version_string().unwrap();
+1142            KernelVersion::from_str(&release).unwrap();
+1143        }
+1144
+1145        #[test]
+1146        fn parse_kernel_version_release_string() {
+1147            // These are made up for the test
+1148            assert_eq!(
+1149                KernelVersion::from_str("4.14"),
+1150                Some(KernelVersion {
+1151                    version: 4,
+1152                    major_revision: 14
+1153                })
+1154            );
+1155            assert_eq!(
+1156                KernelVersion::from_str("4.18"),
+1157                Some(KernelVersion {
+1158                    version: 4,
+1159                    major_revision: 18
+1160                })
+1161            );
+1162            // These were seen in the wild
+1163            assert_eq!(
+1164                KernelVersion::from_str("4.14.186-27095505"),
+1165                Some(KernelVersion {
+1166                    version: 4,
+1167                    major_revision: 14
+1168                })
+1169            );
+1170            assert_eq!(
+1171                KernelVersion::from_str("6.8.0-59-generic"),
+1172                Some(KernelVersion {
+1173                    version: 6,
+1174                    major_revision: 8
+1175                })
+1176            );
+1177        }
+1178    }
+1179}
+1180
+1181// On Apple platforms using the `sendmsg_x` call, UDP datagram segmentation is not
+1182// offloaded to the NIC or even the kernel, but instead done here in user space in
+1183// [`send`]) and then passed to the OS as individual `iovec`s (up to `BATCH_SIZE`).
+1184// The initial value is 1 (no batching); callers can enable batching via
+1185// `UdpSocketState::set_apple_fast_path()` which updates `max_gso_segments`.
+1186#[cfg(not(any(target_os = "linux", target_os = "android")))]
+1187mod gso {
+1188    use super::*;
+1189
+1190    pub(super) fn max_gso_segments(_socket: &impl AsRawFd) -> usize {
+1191        1
+1192    }
+1193
+1194    #[cfg_attr(apple_fast, allow(dead_code))] // Unused when apple_fast is enabled
+1195    pub(super) fn set_segment_size(
+1196        #[cfg(not(apple_fast))] _encoder: &mut cmsg::Encoder<'_, libc::msghdr>,
+1197        #[cfg(apple_fast)] _encoder: &mut cmsg::Encoder<'_, msghdr_x>,
+1198        _segment_size: u16,
+1199    ) {
+1200    }
+1201}
+1202
+1203/// Returns whether the given socket option is supported on the current platform
+1204///
+1205/// Yields `Ok(true)` if the option was set successfully, `Ok(false)` if setting
+1206/// the option raised an `ENOPROTOOPT` or `EOPNOTSUPP` error, and `Err` for any other error.
+1207fn set_socket_option_supported(
+1208    socket: &impl AsRawFd,
+1209    level: libc::c_int,
+1210    name: libc::c_int,
+1211    value: libc::c_int,
+1212) -> io::Result<bool> {
+1213    match set_socket_option(socket, level, name, value) {
+1214        Ok(()) => Ok(true),
+1215        Err(err) if err.raw_os_error() == Some(libc::ENOPROTOOPT) => Ok(false),
+1216        Err(err) if err.raw_os_error() == Some(libc::EOPNOTSUPP) => Ok(false),
+1217        Err(err) => Err(err),
+1218    }
+1219}
+1220
+1221fn set_socket_option(
+1222    socket: &impl AsRawFd,
+1223    level: libc::c_int,
+1224    name: libc::c_int,
+1225    value: libc::c_int,
+1226) -> io::Result<()> {
+1227    let rc = unsafe {
+1228        libc::setsockopt(
+1229            socket.as_raw_fd(),
+1230            level,
+1231            name,
+1232            &value as *const _ as _,
+1233            mem::size_of_val(&value) as _,
+1234        )
+1235    };
+1236
+1237    match rc == 0 {
+1238        true => Ok(()),
+1239        false => Err(io::Error::last_os_error()),
+1240    }
+1241}
+1242
+1243const OPTION_ON: libc::c_int = 1;
+1244
+1245/// Calls `f` in a loop, retrying on `EINTR`, and returns the non-negative result or the first
+1246/// non-`EINTR` error.
+1247fn retry_if_interrupted(mut f: impl FnMut() -> isize) -> io::Result<isize> {
+1248    loop {
+1249        let n = f();
+1250        if n >= 0 {
+1251            return Ok(n);
+1252        }
+1253        let e = io::Error::last_os_error();
+1254        if e.kind() != io::ErrorKind::Interrupted {
+1255            return Err(e);
+1256        }
+1257    }
+1258}
\ No newline at end of file diff --git a/pr/672/docs/src/perf/client.rs.html b/pr/672/docs/src/perf/client.rs.html new file mode 100644 index 000000000..1ce45266e --- /dev/null +++ b/pr/672/docs/src/perf/client.rs.html @@ -0,0 +1,426 @@ +client.rs - source

perf/
client.rs

1#[cfg(feature = "json-output")]
+2use std::path::{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 noq::{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 = noq::EndpointConfig::default();
+102    endpoint_cfg.max_udp_payload_size(opt.common.max_udp_payload_size)?;
+103
+104    let endpoint = noq::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 = QuicClientConfig::try_from(crypto)?;
+130    let mut config = noq::ClientConfig::new(match opt.common.no_protection {
+131        true => Arc::new(NoProtectionClientConfig::new(crypto)),
+132        false => Arc::new(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        #[cfg(feature = "json-output")]
+170        let allow_table_output = opt.json.clone().is_none_or(|path| path != Path::new("-"));
+171        #[cfg(not(feature = "json-output"))]
+172        let allow_table_output = true;
+173
+174        loop {
+175            let start = Instant::now();
+176            tokio::time::sleep(interval_duration).await;
+177            {
+178                stats.on_interval(start, &stream_stats);
+179
+180                if allow_table_output {
+181                    stats.print();
+182                    if opt.common.conn_stats {
+183                        println!("{:?}\n", connection.stats());
+184                    }
+185                }
+186            }
+187        }
+188    };
+189
+190    tokio::select! {
+191        _ = drive_fut => {}
+192        _ = stats_fut => {}
+193        _ = tokio::signal::ctrl_c() => {
+194            info!("shutting down");
+195            connection.close(0u32.into(), b"interrupted");
+196        }
+197        // Add a small duration so the final interval can be reported
+198        _ = tokio::time::sleep(Duration::from_secs(opt.duration) + Duration::from_millis(200)) => {
+199            info!("shutting down");
+200            connection.close(0u32.into(), b"done");
+201        }
+202    }
+203
+204    endpoint.wait_all_draining().await;
+205
+206    #[cfg(feature = "json-output")]
+207    if let Some(path) = opt.json {
+208        stats.print_json(path.as_path())?;
+209    }
+210
+211    Ok(())
+212}
+213
+214async fn drain_stream(
+215    mut stream: noq::RecvStream,
+216    download: u64,
+217    stream_stats: OpenStreamStats,
+218) -> Result<()> {
+219    if download == 0 {
+220        return Ok(());
+221    }
+222
+223    #[rustfmt::skip]
+224    let mut bufs = [
+225        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+226        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+227        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+228        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+229        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+230        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+231        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+232        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+233    ];
+234    let download_start = Instant::now();
+235    let recv_stream_stats = stream_stats.new_receiver(&stream, download);
+236
+237    let mut first_byte = true;
+238
+239    while let Some(size) = stream.read_many_chunks(&mut bufs[..]).await? {
+240        if first_byte {
+241            recv_stream_stats.on_first_byte(download_start.elapsed());
+242            first_byte = false;
+243        }
+244        let bytes_received = bufs[..size].iter().map(|b| b.len()).sum();
+245        recv_stream_stats.on_bytes(bytes_received);
+246    }
+247
+248    if first_byte {
+249        recv_stream_stats.on_first_byte(download_start.elapsed());
+250    }
+251    recv_stream_stats.finish(download_start.elapsed());
+252
+253    debug!("response finished on {}", stream.id());
+254    Ok(())
+255}
+256
+257async fn drive_uni(
+258    connection: noq::Connection,
+259    stream_stats: OpenStreamStats,
+260    concurrency: u64,
+261    upload: u64,
+262    download: u64,
+263) -> Result<()> {
+264    if concurrency == 0 {
+265        return Ok(());
+266    }
+267
+268    let sem = Arc::new(Semaphore::new(concurrency as usize));
+269
+270    loop {
+271        let permit = sem.clone().acquire_owned().await.unwrap();
+272        let send = connection.open_uni().await?;
+273        let stream_stats = stream_stats.clone();
+274
+275        debug!("sending request on {}", send.id());
+276        let connection = connection.clone();
+277        tokio::spawn(async move {
+278            if let Err(e) = request_uni(send, connection, upload, download, stream_stats).await {
+279                error!("sending request failed: {:#}", e);
+280            }
+281
+282            drop(permit);
+283        });
+284    }
+285}
+286
+287async fn request_uni(
+288    send: noq::SendStream,
+289    conn: noq::Connection,
+290    upload: u64,
+291    download: u64,
+292    stream_stats: OpenStreamStats,
+293) -> Result<()> {
+294    request(send, upload, download, stream_stats.clone()).await?;
+295    let recv = conn.accept_uni().await?;
+296    drain_stream(recv, download, stream_stats).await?;
+297    Ok(())
+298}
+299
+300async fn request(
+301    mut send: noq::SendStream,
+302    mut upload: u64,
+303    download: u64,
+304    stream_stats: OpenStreamStats,
+305) -> Result<()> {
+306    let upload_start = Instant::now();
+307    send.write_all(&download.to_be_bytes()).await?;
+308    if upload == 0 {
+309        send.finish().unwrap();
+310        return Ok(());
+311    }
+312
+313    let send_stream_stats = stream_stats.new_sender(&send, upload);
+314
+315    static DATA: [u8; 1024 * 1024] = [42; 1024 * 1024];
+316    while upload > 0 {
+317        let chunk_len = upload.min(DATA.len() as u64);
+318        send.write_chunk(Bytes::from_static(&DATA[..chunk_len as usize]))
+319            .await
+320            .context("sending response")?;
+321        send_stream_stats.on_bytes(chunk_len as usize);
+322        upload -= chunk_len;
+323    }
+324    send.finish().unwrap();
+325    // Wait for stream to close
+326    _ = send.stopped().await;
+327    send_stream_stats.finish(upload_start.elapsed());
+328
+329    debug!("upload finished on {}", send.id());
+330    Ok(())
+331}
+332
+333async fn drive_bi(
+334    connection: noq::Connection,
+335    stream_stats: OpenStreamStats,
+336    concurrency: u64,
+337    upload: u64,
+338    download: u64,
+339) -> Result<()> {
+340    if concurrency == 0 {
+341        return Ok(());
+342    }
+343
+344    let sem = Arc::new(Semaphore::new(concurrency as usize));
+345
+346    loop {
+347        let permit = sem.clone().acquire_owned().await.unwrap();
+348        let (send, recv) = connection.open_bi().await?;
+349        let stream_stats = stream_stats.clone();
+350
+351        debug!("sending request on {}", send.id());
+352        tokio::spawn(async move {
+353            if let Err(e) = request_bi(send, recv, upload, download, stream_stats).await {
+354                error!("request failed: {:#}", e);
+355            }
+356
+357            drop(permit);
+358        });
+359    }
+360}
+361
+362async fn request_bi(
+363    send: noq::SendStream,
+364    recv: noq::RecvStream,
+365    upload: u64,
+366    download: u64,
+367    stream_stats: OpenStreamStats,
+368) -> Result<()> {
+369    request(send, upload, download, stream_stats.clone()).await?;
+370    drain_stream(recv, download, stream_stats).await?;
+371    Ok(())
+372}
+373
+374#[derive(Debug)]
+375struct SkipServerVerification(Arc<rustls::crypto::CryptoProvider>);
+376
+377impl SkipServerVerification {
+378    fn new(provider: Arc<rustls::crypto::CryptoProvider>) -> Arc<Self> {
+379        Arc::new(Self(provider))
+380    }
+381}
+382
+383impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
+384    fn verify_server_cert(
+385        &self,
+386        _end_entity: &CertificateDer<'_>,
+387        _intermediates: &[CertificateDer<'_>],
+388        _server_name: &ServerName<'_>,
+389        _ocsp: &[u8],
+390        _now: UnixTime,
+391    ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
+392        Ok(rustls::client::danger::ServerCertVerified::assertion())
+393    }
+394
+395    fn verify_tls12_signature(
+396        &self,
+397        message: &[u8],
+398        cert: &CertificateDer<'_>,
+399        dss: &rustls::DigitallySignedStruct,
+400    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
+401        rustls::crypto::verify_tls12_signature(
+402            message,
+403            cert,
+404            dss,
+405            &self.0.signature_verification_algorithms,
+406        )
+407    }
+408
+409    fn verify_tls13_signature(
+410        &self,
+411        message: &[u8],
+412        cert: &CertificateDer<'_>,
+413        dss: &rustls::DigitallySignedStruct,
+414    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
+415        rustls::crypto::verify_tls13_signature(
+416            message,
+417            cert,
+418            dss,
+419            &self.0.signature_verification_algorithms,
+420        )
+421    }
+422
+423    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
+424        self.0.signature_verification_algorithms.supported_schemes()
+425    }
+426}
\ No newline at end of file diff --git a/pr/672/docs/src/perf/lib.rs.html b/pr/672/docs/src/perf/lib.rs.html new file mode 100644 index 000000000..41cb70712 --- /dev/null +++ b/pr/672/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 noq::{
+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    Bbr3,
+202    NewReno,
+203}
+204
+205impl CongestionAlgorithm {
+206    pub fn build(self) -> Arc<dyn ControllerFactory + Send + Sync + 'static> {
+207        match self {
+208            Self::Cubic => Arc::new(congestion::CubicConfig::default()),
+209            Self::Bbr3 => Arc::new(congestion::Bbr3Config::default()),
+210            Self::NewReno => Arc::new(congestion::NewRenoConfig::default()),
+211        }
+212    }
+213}
+214
+215pub static PERF_CIPHER_SUITES: &[rustls::SupportedCipherSuite] = &[
+216    cipher_suite::TLS13_AES_128_GCM_SHA256,
+217    cipher_suite::TLS13_AES_256_GCM_SHA384,
+218    cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
+219];
\ No newline at end of file diff --git a/pr/672/docs/src/perf/noprotection.rs.html b/pr/672/docs/src/perf/noprotection.rs.html new file mode 100644 index 000000000..7f8c771c9 --- /dev/null +++ b/pr/672/docs/src/perf/noprotection.rs.html @@ -0,0 +1,205 @@ +noprotection.rs - source

perf/
noprotection.rs

1use bytes::BytesMut;
+2
+3use noq_proto::{
+4    ConnectionId, PathId, Side, TransportError,
+5    crypto::{
+6        self, CryptoError,
+7        rustls::{QuicClientConfig, QuicServerConfig},
+8    },
+9    transport_parameters,
+10};
+11
+12/// A rustls TLS session which does not perform packet encryption/decryption (for debugging purpose)
+13struct NoProtectionSession {
+14    inner: Box<dyn crypto::Session>,
+15}
+16
+17impl NoProtectionSession {
+18    fn new(tls: Box<dyn crypto::Session>) -> Self {
+19        Self { inner: tls }
+20    }
+21
+22    /// Wraps the provided keys in `NoProtectionPacketKey` to disable packet encryption / decryption
+23    fn wrap_packet_keys(
+24        keys: crypto::KeyPair<Box<dyn crypto::PacketKey>>,
+25    ) -> crypto::KeyPair<Box<dyn crypto::PacketKey>> {
+26        crypto::KeyPair {
+27            local: Box::new(NoProtectionPacketKey::new(keys.local)),
+28            remote: Box::new(NoProtectionPacketKey::new(keys.remote)),
+29        }
+30    }
+31}
+32
+33struct NoProtectionPacketKey {
+34    inner: Box<dyn crypto::PacketKey>,
+35}
+36
+37impl NoProtectionPacketKey {
+38    fn new(key: Box<dyn crypto::PacketKey>) -> Self {
+39        Self { inner: key }
+40    }
+41}
+42
+43#[derive(Clone)]
+44pub struct NoProtectionClientConfig {
+45    inner: QuicClientConfig,
+46}
+47
+48impl NoProtectionClientConfig {
+49    pub fn new(config: QuicClientConfig) -> Self {
+50        Self { inner: config }
+51    }
+52}
+53
+54#[derive(Clone)]
+55pub struct NoProtectionServerConfig {
+56    inner: QuicServerConfig,
+57}
+58
+59impl NoProtectionServerConfig {
+60    pub fn new(config: 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,
+135        version: u32,
+136        server_name: &str,
+137        params: &transport_parameters::TransportParameters,
+138    ) -> Result<Box<dyn crypto::Session>, noq::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,
+163        version: u32,
+164        params: &transport_parameters::TransportParameters,
+165    ) -> Box<dyn crypto::Session> {
+166        let tls = self.inner.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/672/docs/src/perf/server.rs.html b/pr/672/docs/src/perf/server.rs.html new file mode 100644 index 000000000..be272ccb4 --- /dev/null +++ b/pr/672/docs/src/perf/server.rs.html @@ -0,0 +1,209 @@ +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 noq::{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 = QuicServerConfig::try_from(crypto)?;
+71    let mut config = noq::ServerConfig::with_crypto(match opt.common.no_protection {
+72        true => Arc::new(NoProtectionServerConfig::new(crypto)),
+73        false => Arc::new(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 = noq::EndpointConfig::default();
+80    endpoint_cfg.max_udp_payload_size(opt.common.max_udp_payload_size)?;
+81
+82    let endpoint = noq::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: noq::Incoming, opt: Arc<Opt>) -> Result<()> {
+102    let connection = handshake.await.context("handshake failed")?;
+103
+104    debug!(
+105        "{} connected",
+106        connection
+107            .path(noq::PathId::ZERO)
+108            .expect("path open after connect")
+109            .remote_address()
+110            .expect("path is alive")
+111    );
+112    tokio::try_join!(
+113        drive_uni(connection.clone()),
+114        drive_bi(connection.clone()),
+115        conn_stats(connection, opt)
+116    )?;
+117    Ok(())
+118}
+119
+120async fn conn_stats(connection: noq::Connection, opt: Arc<Opt>) -> Result<()> {
+121    if opt.common.conn_stats {
+122        loop {
+123            tokio::time::sleep(Duration::from_secs(2)).await;
+124            println!("{:?}\n", connection.stats());
+125        }
+126    }
+127
+128    Ok(())
+129}
+130
+131async fn drive_uni(connection: noq::Connection) -> Result<()> {
+132    while let Ok(stream) = connection.accept_uni().await {
+133        let connection = connection.clone();
+134        tokio::spawn(async move {
+135            if let Err(e) = handle_uni(connection, stream).await {
+136                error!("request failed: {:#}", e);
+137            }
+138        });
+139    }
+140    Ok(())
+141}
+142
+143async fn handle_uni(connection: noq::Connection, stream: noq::RecvStream) -> Result<()> {
+144    let bytes = read_req(stream).await?;
+145    let response = connection.open_uni().await?;
+146    respond(bytes, response).await?;
+147    Ok(())
+148}
+149
+150async fn drive_bi(connection: noq::Connection) -> Result<()> {
+151    while let Ok((send, recv)) = connection.accept_bi().await {
+152        tokio::spawn(async move {
+153            if let Err(e) = handle_bi(send, recv).await {
+154                error!("request failed: {:#}", e);
+155            }
+156        });
+157    }
+158    Ok(())
+159}
+160
+161async fn handle_bi(send: noq::SendStream, recv: noq::RecvStream) -> Result<()> {
+162    let bytes = read_req(recv).await?;
+163    respond(bytes, send).await?;
+164    Ok(())
+165}
+166
+167async fn read_req(mut stream: noq::RecvStream) -> Result<u64> {
+168    let mut buf = [0; 8];
+169    stream
+170        .read_exact(&mut buf)
+171        .await
+172        .context("reading request")?;
+173    let n = u64::from_be_bytes(buf);
+174    debug!("got req for {} bytes on {}", n, stream.id());
+175    drain_stream(stream).await?;
+176    Ok(n)
+177}
+178
+179async fn drain_stream(mut stream: noq::RecvStream) -> Result<()> {
+180    #[rustfmt::skip]
+181    let mut bufs = [
+182        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+183        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+184        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+185        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+186        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+187        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+188        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+189        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+190    ];
+191    while stream.read_many_chunks(&mut bufs[..]).await?.is_some() {}
+192    debug!("finished reading {}", stream.id());
+193    Ok(())
+194}
+195
+196async fn respond(mut bytes: u64, mut stream: noq::SendStream) -> Result<()> {
+197    static DATA: [u8; 1024 * 1024] = [42; 1024 * 1024];
+198
+199    while bytes > 0 {
+200        let chunk_len = bytes.min(DATA.len() as u64);
+201        stream
+202            .write_chunk(Bytes::from_static(&DATA[..chunk_len as usize]))
+203            .await
+204            .context("sending response")?;
+205        bytes -= chunk_len;
+206    }
+207    debug!("finished responding on {}", stream.id());
+208    Ok(())
+209}
\ No newline at end of file diff --git a/pr/672/docs/src/perf/stats.rs.html b/pr/672/docs/src/perf/stats.rs.html new file mode 100644 index 000000000..4dbddb4ce --- /dev/null +++ b/pr/672/docs/src/perf/stats.rs.html @@ -0,0 +1,412 @@ +stats.rs - source

perf/
stats.rs

1use hdrhistogram::Histogram;
+2use noq::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        if path == Path::new("-") {
+124            json::print(self, std::io::stdout());
+125        } else {
+126            let file = File::create(path)?;
+127            json::print(self, file)
+128        }
+129        Ok(())
+130    }
+131}
+132
+133/// Statistics for the currently open streams
+134#[derive(Clone, Default)]
+135pub struct OpenStreamStats(Arc<Mutex<Vec<Arc<StreamStats>>>>);
+136
+137impl OpenStreamStats {
+138    pub fn new_sender(&self, stream: &noq::SendStream, upload_size: u64) -> Arc<StreamStats> {
+139        let send_stream_stats = StreamStats {
+140            id: stream.id(),
+141            request_size: upload_size,
+142            bytes: Default::default(),
+143            sender: true,
+144            finished: Default::default(),
+145            duration: Default::default(),
+146            first_byte_latency: Default::default(),
+147        };
+148        let send_stream_stats = Arc::new(send_stream_stats);
+149        self.push(send_stream_stats.clone());
+150        send_stream_stats
+151    }
+152
+153    pub fn new_receiver(&self, stream: &noq::RecvStream, download_size: u64) -> Arc<StreamStats> {
+154        let recv_stream_stats = StreamStats {
+155            id: stream.id(),
+156            request_size: download_size,
+157            bytes: Default::default(),
+158            sender: false,
+159            finished: Default::default(),
+160            duration: Default::default(),
+161            first_byte_latency: Default::default(),
+162        };
+163        let recv_stream_stats = Arc::new(recv_stream_stats);
+164        self.push(recv_stream_stats.clone());
+165        recv_stream_stats
+166    }
+167
+168    fn push(&self, stream_stats: Arc<StreamStats>) {
+169        self.0.lock().unwrap().push(stream_stats);
+170    }
+171}
+172
+173pub struct StreamStats {
+174    id: StreamId,
+175    request_size: u64,
+176    bytes: AtomicUsize,
+177    sender: bool,
+178    finished: AtomicBool,
+179    duration: AtomicU64,
+180    first_byte_latency: AtomicU64,
+181}
+182
+183impl StreamStats {
+184    pub fn on_first_byte(&self, latency: Duration) {
+185        self.first_byte_latency
+186            .store(latency.as_micros() as u64, Ordering::SeqCst);
+187    }
+188
+189    pub fn on_bytes(&self, bytes: usize) {
+190        self.bytes.fetch_add(bytes, Ordering::SeqCst);
+191    }
+192
+193    pub fn finish(&self, duration: Duration) {
+194        self.duration
+195            .store(duration.as_micros() as u64, Ordering::SeqCst);
+196        self.finished.store(true, Ordering::SeqCst);
+197    }
+198}
+199
+200struct Interval {
+201    streams: Vec<StreamIntervalStats>,
+202    period: IntervalPeriod,
+203}
+204
+205impl Interval {
+206    fn new(start: Duration, end: Duration) -> Self {
+207        let period = IntervalPeriod {
+208            start: start.as_secs_f64(),
+209            end: end.as_secs_f64(),
+210            seconds: (end - start).as_secs_f64(),
+211        };
+212
+213        Self {
+214            streams: vec![],
+215            period,
+216        }
+217    }
+218
+219    fn record_stream_stats(&mut self, stream_stats: Arc<StreamStats>) {
+220        let bytes = stream_stats.bytes.swap(0, Ordering::SeqCst);
+221        self.streams.push(StreamIntervalStats {
+222            id: stream_stats.id,
+223            bytes,
+224            sender: stream_stats.sender,
+225        })
+226    }
+227}
+228
+229struct IntervalPeriod {
+230    start: f64,
+231    end: f64,
+232    seconds: f64,
+233}
+234
+235struct StreamIntervalStats {
+236    id: StreamId,
+237    bytes: usize,
+238    sender: bool,
+239}
+240
+241fn throughput_bytes_per_second(duration_in_micros: u64, size: u64) -> f64 {
+242    (size as f64) / (duration_in_micros as f64 / 1000000.0)
+243}
+244
+245#[cfg(feature = "json-output")]
+246mod json {
+247    use crate::stats;
+248    use crate::stats::{Stats, StreamIntervalStats};
+249    use noq::StreamId;
+250    use serde::{self, Serialize, Serializer, ser::SerializeStruct};
+251    use std::io::Write;
+252    use std::time::{SystemTime, UNIX_EPOCH};
+253
+254    pub(crate) fn print<W: Write>(stats: &Stats, out: W) {
+255        let report = Report {
+256            start: Start {
+257                timestamp: stats.start,
+258            },
+259            intervals: &stats
+260                .intervals
+261                .iter()
+262                .map(Interval::from_stats_interval)
+263                .collect(),
+264        };
+265
+266        serde_json::to_writer(out, &report).unwrap();
+267    }
+268
+269    #[derive(Serialize)]
+270    struct Report<'a> {
+271        start: Start,
+272        intervals: &'a Vec<Interval>,
+273        // TODO: add end stats
+274    }
+275
+276    #[derive(Serialize)]
+277    struct Start {
+278        #[serde(serialize_with = "serialize_timestamp")]
+279        timestamp: SystemTime,
+280    }
+281
+282    fn serialize_timestamp<S>(time: &SystemTime, s: S) -> Result<S::Ok, S::Error>
+283    where
+284        S: serde::Serializer,
+285    {
+286        use serde::ser::SerializeMap;
+287        let mut state = s.serialize_map(Some(1))?;
+288        state.serialize_entry(
+289            "timesecs",
+290            &time.duration_since(UNIX_EPOCH).unwrap().as_secs(),
+291        )?;
+292        state.end()
+293    }
+294
+295    struct Interval {
+296        streams: Vec<Stream>,
+297        recv_sum: Sum,
+298        send_sum: Sum,
+299    }
+300
+301    impl Interval {
+302        fn from_stats_interval(interval: &stats::Interval) -> Self {
+303            Self {
+304                streams: interval
+305                    .streams
+306                    .iter()
+307                    .map(|stats| Stream::from_stream_interval_stats(stats, &interval.period))
+308                    .collect(),
+309                recv_sum: Sum::from_stream_interval_stats(
+310                    &interval.streams,
+311                    &interval.period,
+312                    false,
+313                ),
+314                send_sum: Sum::from_stream_interval_stats(
+315                    &interval.streams,
+316                    &interval.period,
+317                    true,
+318                ),
+319            }
+320        }
+321    }
+322
+323    impl Serialize for Interval {
+324        fn serialize<S>(
+325            &self,
+326            serializer: S,
+327        ) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
+328        where
+329            S: Serializer,
+330        {
+331            let mut state = serializer.serialize_struct("Interval", 2)?;
+332            state.serialize_field("streams", &self.streams)?;
+333            // iperf3 outputs duplicate "sum" entries when run in bidirectional mode
+334            // serde does not support duplicate keys, so only output one of the sums
+335            if self.send_sum.bytes > 0 {
+336                state.serialize_field("sum", &self.send_sum)?;
+337            } else {
+338                state.serialize_field("sum", &self.recv_sum)?;
+339            }
+340            state.end()
+341        }
+342    }
+343
+344    #[derive(Serialize)]
+345    struct Stream {
+346        #[serde(serialize_with = "serialize_stream_id")]
+347        id: StreamId,
+348        start: f64,
+349        end: f64,
+350        seconds: f64,
+351        bytes: usize,
+352        bits_per_second: f64,
+353        sender: bool,
+354    }
+355
+356    impl Stream {
+357        fn from_stream_interval_stats(
+358            stats: &stats::StreamIntervalStats,
+359            period: &stats::IntervalPeriod,
+360        ) -> Self {
+361            let bits_per_second = stats.bytes as f64 * 8.0 / period.seconds;
+362
+363            Self {
+364                id: stats.id,
+365                start: period.start,
+366                end: period.end,
+367                seconds: period.seconds,
+368                bytes: stats.bytes,
+369                bits_per_second,
+370                sender: stats.sender,
+371            }
+372        }
+373    }
+374
+375    fn serialize_stream_id<S: Serializer>(id: &StreamId, serializer: S) -> Result<S::Ok, S::Error> {
+376        serializer.serialize_u64(u64::from(*id))
+377    }
+378
+379    #[derive(Serialize)]
+380    struct Sum {
+381        start: f64,
+382        end: f64,
+383        seconds: f64,
+384        bytes: usize,
+385        bits_per_second: f64,
+386        sender: bool,
+387    }
+388
+389    impl Sum {
+390        fn from_stream_interval_stats(
+391            stats: &[StreamIntervalStats],
+392            period: &stats::IntervalPeriod,
+393            sender: bool,
+394        ) -> Self {
+395            let bytes = stats
+396                .iter()
+397                .filter(|stat| stat.sender == sender)
+398                .map(|stat| stat.bytes)
+399                .sum();
+400            let bits_per_second = bytes as f64 * 8.0 / period.seconds;
+401
+402            Self {
+403                start: period.start,
+404                end: period.end,
+405                seconds: period.seconds,
+406                bytes,
+407                bits_per_second,
+408                sender,
+409            }
+410        }
+411    }
+412}
\ No newline at end of file diff --git a/pr/672/docs/src/set_up_connection/set-up-connection.rs.html b/pr/672/docs/src/set_up_connection/set-up-connection.rs.html new file mode 100644 index 000000000..039f258a7 --- /dev/null +++ b/pr/672/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 noq::{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/672/docs/static.files/COPYRIGHT-7fb11f4e.txt b/pr/672/docs/static.files/COPYRIGHT-7fb11f4e.txt new file mode 100644 index 000000000..752dab0a3 --- /dev/null +++ b/pr/672/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/672/docs/static.files/FiraMono-Medium-86f75c8c.woff2 b/pr/672/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/672/docs/static.files/FiraMono-Regular-87c26294.woff2 b/pr/672/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/672/docs/static.files/FiraSans-Italic-81dc35de.woff2 b/pr/672/docs/static.files/FiraSans-Italic-81dc35de.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..3f63664fee6ddf221f71a57461a7f4d1de281177 GIT binary patch literal 136300 zcmV)LK)JtnPew8T0RR910u*cj5dZ)H21ncg0u%!P1REj%00000000000000000000 z0000Qg9sah=u{kmi9!Zo0D+=z2!T=wmlqKT3XacgjO8BzHUcCA(liUwH~<771&mP# zf!B2mfjL_!sI}awv>@!n`(|ou47_WSJP$%jZL50){F{vVenHY8P||r7-3=UQ$B>2t zQmbE2WJ2i}-UCumA+1f+WuL>nKt(t|KKuXw|NsC0|NsC0|NsC0zs+P4PL=L$J=hL{ z0|4#PLD~@z6Q+fX@ykR>zZ_0f1T1v8p!P|wG~f?HhRJ>KTRmzr#QrOCRr ztT$US3cLM@p**wp6ltxI)~XXKtS%jBFq*^}GX3~0=^8*&Zg`npIk^hYLKt<>PL^~H zt zj+~UGZ#7vNcFA6NFufiu3vK4KMK^-}O)Ko&LJ=sUeCrmveW#bo%7*SnhG&>Qhayk} zil~OHS>&1mG!~q_SEjPz2JR$)qI!SP7q%U0TFk3vPRsgGKZw}<6Nl2eKJIbz(B**y zSi@!#g~HQ9h7LNvTLb|G!BUtZW2{)qZ0N{Ya$+qrMkotJsavH-{3^ci&-s}Pgr!qF zA-(u96oDyG)-5RbETo7@U?OBQriOGwKVd=@qx`7OiqxQEl5RX=$EUYTaZE`{SVPY$ zr<8c^eWZSJCyLXz==t4ju-W7&z%RtRt@^!Yt~Ku*QP5@|#0PKqz6e*>q%k8zdx1k`IyA28z;>=kLHy zl$J@BYb9g2CTUf!Gh@ZtD?Hp9wYN`#a5kBjs+pXw8+}Zl!~5c!MfyxwX_7FF%H0eL z+OUv>(*zU^je^4smZS*{%B?qgcp&#q{TE;JA7K|~u~8oH zPqH9q#q#)rdHc?A^S%U|xlbNE0CU#mpTFRfB{kdZdP%*3#k0p)=6cKul)D74iFa$| z>=U?-uP0cZ!mRp6S=3tz5Ru6*P}7GFbbZ}o_rJ2DQ5%Af9$fQkJ87RJ*!j2qUbyk! zjhyc@7ZFcU<Ci+bQWUDW9UmGN3# zycH7-$ac9fGFrcDlRL?I1DeeA((Iuc&PPqAR}h1sV(vDkVS!>-)?9DTi+qrku4 zj@d9fXC>c7KVIl6Pc6^$sh~pjAjk%>qbMD0Danm_eQsHla5`zzEON|v^Hb?365N5> z^E;1HXEAu;y9ZQ5c?I#^`_eCpxR%qLp_m-kT=0upxMF2@hrd?QtO)z-9ynb1H>6vMw$1dQ_5Q;z%s73t=Os6aVH(ydL^xrLz zp+rC5uwgk#@4bJ7lj?~!)P4+CHU6|%r&fdggIR$`1&yD0_@@kqL!`y{WH$JZaro|6 zxsw3Kwha?0L$Qi9FgTQe6eFTBm5zwHFDsG3+J^dmoEO*#RIlv?R&JyR&%)pE+#mD> z4}N(`|2#Z@>^=AWTm|s_1CBFGaYVEB;{RV3|8B2y-=Ar#97<3_NC?JZ(F!3{f#|VS zpAnHYz1wp@BJ36-Tf`QEg&q*Oda}jtc>;j_l_+jllf^7@av zTMy{f?9O<3kgA|dCH|jI)vdbqgB&g)%gx(6E%NHDcV{fyjum7TIG~1ffwdhwj2)i8 z=KEJ&SgVy+L)0>tpi9&uhh$6*EsW~np6!RadE$X{j$u4~+~|RZnoyi`6ySj##COf? z{%PX>@elOChj&?PRN$sSn$ZZB(TK*392WKAs;&2_iux7@}fKH>|0D6IG)dThM^MMpd{9$7rhwtP2!GhgH?t>Z}|LjbJK39XkpH6Zv^Hx$w~I~Rr$ID+}@ zS0(A+=|QT@tL*Lppisw(!IDzv+z|k9tEN?TviTs^OiGn*FTPY&2FN^9GB&;Otz($J ztykh-h_TFkmSyEYjBl(A!(X*Px+g4J{l8TE2jo&?E=3Y5mJk=zT$0T6-*|uf7Q0oB$9Wd7 zE5B-ufJV{-Ar5=j#Qot2(oM31II$TX`P}+Hy<#m=0Ul%=80L#=7OM=cS>`aG4e;VD z%@WQMCpmsVDk@W@Pt}PES5=|P1JAMiSHVe9y(+NLy%SoP&ksks0VWRW6Stqf8lroA zttgftA;h8u19mRFIhqYwi*%+>vQGxo>rW;W?gLITYq~(u)G3-K^RE3~CuO==qI#zj zz0*1&pZMQ);(|FvrnvKG0^WRV5TdK7S_p7kn|k8&%}~t|Wnn8;j2(WqCPYa2n_bA; zKKy^}XPxdJsNCVMH$SuVmH>40=-3s?GAmM~FMNIb@p-$tdf!YhL4YTOBpi@11bU50 zJOC)cLg&POl`aJH2|PcK$H(PzWm0*GOd3sYPN|cjuvnf2Cfy@ z>jpNvD=_~WFbD(4aF&pbu%FCYcHwEuHI{|%=@9}SGFj!!i5=dFN$|4$4I zq_Xziyqt<=5(w%*^~2Zye*ae8`RS8)f_QhyL}=Col7-FkC>)_8p-=(Q0IFd%90jOu zf$^fg==A&8JV*n_0Ra-FSt<1o%XoF)Zd$Jk00;t_HBH^Oj#|~o;IssJ{X7MwyADqP zdO#jPoH+RZ|84Uj>3iQ3UZEs?bkYh!B+WvxmVM4XaAtYY8FGD@*LiE{a$e(Ceh0dp zZfVd}G?ZqwJmD$;OQPxekyOZNo|px%2pT2-#uNm%&u^zhM5t_d23bV+s6@>w`0sV=&)j4}&^7Rf)$Mj){?@c_Hn8pX%PsSO*pB^|Qi(y=S(L2eFaDdZ z4B(7>SO%!5Q+4(t%_$S$1N>5||Fl(~tI~4Oc?^rkK4}7c;83uOrd}^4 z&H<~A;;3~b*ARN#)`$O3-6vtUVK>)#34gf@JETtu@3It>m+F)$${lyrkwf6WsWn2p zNDM1nG=!20iu%pGzy5PKi@az+L&!eiqFakP$Ce7}SYs1%+W%|Q$lKd+Z9>gp&1_&+ zu4lJUbG)G%0SbTqU-2L3@_+lEEvG$BQo3<^1Cnb8UQbN}IbwgCo8PC?R_**cgbI-C zgk|$PE5x%q@49P?YZ?F!(n|AS+};s9{Qtdgd=g4*CtdIf1^JhH@YBsr>0J7Z(0C($ zIQTFaoHd{Wg57XKDs4N__clst%% zoFlSjA9@x5QnE;?CrTNIRhC(7D7!PvZD$zw*=#+GJ1=vx@-oYKnf)#&KX22rm1X$| z+k{IlN^?;K>ZDHk%!l_^8Zyb|CGswN1Nc79%-75qj=8*)fZR2JZ7JPxmjwQ)X(iR$ zEI4)e&TeSu0qg=Lqn_DueYJYeszrTrN_|m_^u96It5d~4VvCGjjPZWN5ucy}io@5y9ESq4~s5Nlwo1To{8?{+R${gAu zU3lR|`GuE;5M7-gzn0n*e_7qQI~72uB4k{J>xL#cQ@WYZ4^J7~D*&xr8CfG*VQmQh zzgMfpyo;5h*&rpG$=E!h`+s5nkubW|i)s9N8ap4~m=;#mxM7Qk5FZl}I~P%8IR#Ij zCxPv=EY%7mg`3(B2I!2+KYT+!#qxE9%M6X6)|Bo1aeX^(auVe%KrS5+$#oXN%b!Xi1!vv=w*^Qpq%|jIXJ5MqUBx&_Q@J(_jSd?+-kmn2 zaNV8LX8kHjs8mRx5;BxTk_6qBgxg308zF3?U|L9^76N1}pxXrRbC^JyWKU2@qOIT{ zfs>#E0(YX5!=&KU^W5$UZcj>X_okGNbKG<~ObVyH-u8N*)4n}l_HtOi|Fzz9og117 zPOw~*jg`MD^xnIno-*}MhoC1s0qPLhC>3Fs!R-pUa{GyIV#7@=&`*5B6KZ+BZ}rY| z`evP$NQCig73)jFQc;Zj#v>$1q=4xAp&OsRDv5eRM+ntA<)Ev8aMMQaz5K_e_b6SBJCp^w3eeR#CKeoAN<}2#9{Audlds{4?X&&hM|h$;*HZpis-;uy zln{or)QTYpaOnBEamIZNK<;Mc&6Gea0df?$)7uA%v9*u`Nir@4NgxrYY?Whhvpuxv zNu1y;$?VZwX_McZ$9>!q#T8KKuM-tAv$Hvx%v0|F+vZbXI7KB^Rcs7{(5n9bn>Jes z{YxA{?zZFf6zxhCZRnZ_|Gh!X+)tV}Y#KsH4oP-J3}T&xDwfWjOXmugE(-${&ZY~T zM1gRkH@i_y_PFMz`_dHIe#L9N(0J!YM=*nHP z-L)7fVYWa$UJ&|@bP6aef%@&Sj$I^|tSXA4RUVY!3+~`+Z_IsrMU&1_f*caWGQ}|r z%kcRLf+!A+;pjO#9U7H*y0_8SpBv=;dQ3!gzupibAtDkI8v);mJ&J5dk1Oe|c3b!a z6O1uNh=&kDg!!FVFa5{0OZQ*bid9t=5fw4UGoF!u!2fUg@qN}lgUJO4XAqJ7-Rg0)kVudxXLO*f_)ixPUplR zL?eo%DVib?Nsd*QQH9+>e9;Bbb1cKK6h;_fgb_uDkCAS@pO``r^n#Ft-TZi)WSDw5 zIf#fz5D6iOynnTK2-4lq?;8urjkU@{#2Y(^h=@oC`Qzu==D&yPH&hIwE-6X~+K6*F zoxcBjkkkMF8y#n?_xIhe9NOwsLc~l+2r~39Vl6}V(-vYi$PoyqQf0DDeZ?2y2uE0q z5n>R+EdN*gF?IeL}G9b0K}M*-SOAgLFVlL1*PrP z_IhvZ_D+$d0)osy0tp$*zW?FreE<1BYjPg<(SFwER#a3(#28UgQBhUfRi}}4GK?8A zlpo3yj&KBHZg`9k!gLu*vCzR7Vs9;35W2Z@Kff}vt^6H{x8`gp3iv>ZAdX%)BezH= zXX!0GE24<#Hx^MDj9XP@&lR4l`J?mQy`TAXzH0OJ_Z@$&9kEl#>BSWxnVQXQBJD6h z`uV*mrAh~DJ7ZTZ(;jq2+r0n*2@MD?V;W|SDS|Sl8lY}rmI+od>jCZFZ?ZpE zjRQL?Kf5#j%YFRgvH8!{_}>lb-F?^ne8B%q5#{~PIbimFKXZzKWaEJ;U;mpRvJp!|Nn=0Q&eRqCLyP!qM-vZB%Fci-pmF?@Ci$x<(0Dj{>8WZHqOwb z=8X&qtdmgKyX)5Szx-xq=SB*OqundZ@@Hog1pME%0)l`BP=((Z8bBjx0?oeve@7!I zpazS9#laHcAI_3s4OMdwhRs^F>(s5Vw6XvI!4ZN0n!r&gUZ2{7y;AxPMS8nk(b!Mf;M|{wN=x1n?k6hDXr{S60X2i{;v{2@lruIF6Kud664LmK=`CuUTNyX2HLC zv*wGsafY$gUoai}vQ%tt)ZU?;m z&qv`%jXml>fGv$V-4b_ejO7IU-+BS=fgAx;*&Dw~ULCYcofVv)yg`&=Go~PE;dJAv&-Gg?H$@ou?)rVOGgvOJ#}%-aUTIQYJ6~wV)=cF zL4;3V@cE=LIuG?{1q?06C9GA7&0|1fi)$P}!9pwD?!xB6V#*%iV?nH6)iX0waQrfl zupVwIp*6bRehukEx>MM)8_4CLI%rj7C|OFg4*2ZRzp;k3#Dsmg64Ln!2WBAMvtdUO z@cKi?UFjf{P*z$MUI(SU2%$O5Gc`5&Hjna0sCrAyAd%zBsD($v>WV_f%u4`q{;5?0J>?(1KBJS)`M`|iCHqYOFvvq;3wbZI1HoNnJT4=0xl$kh?E&1FxF zzggth=gdlkRJ+nEIpXwcRB=S`shRvyfM_1}1J0rks7aXNI#SNivqX|+c&6l~gGQJ# z86|yueK8o1e?IRCxCFpMCmt@xmu1B`Y7;a$M|@t8hYzJluEU6X>%k!UX1RwYjZxp0 zgs2QX40O*l0az!e()tCEy4bCQv=WpXQS>Fir(nfaTdo0H`x+^$y?vEgvYD)E)x0r8 z=}J@{ipJ%~h<4Uc`4L@JtR=2+K-^N^*$5&50`+kTYAAsM{^nb`4D0Ur7&#c$GKrD^ z)vm%JN2}S6f}h1|v?K|U$TCNYwgj6dwQW+<43@^Hg@poIwA*hgv8X@IbMW_&8^!?&`pSSi1u{###7BihQ_Z!S*JF<%#Tc%@%x8v>Izr z1y7GvpK>k?zd6Yu?1Cs2UNv}B?19&H3JKusnJp`|0zS)Qd6#_1x<;jA)7tZ%NkkG? zQCyaE8fA;(fu7nUfTSWT9;M%iOedNI%BdJslC7>MFm!U7!XOJ;0M-z%rC33*gC5)n zi;@~Q8n>wIBma#dEmG8`M*AjeY$mDQW+b2_(xXi@a~tz3+3ItojYkhhX)yKx$BVYu znx=RNXP((|eDx8K;847t0KV)3uY!sIImgZBxJ*KgFp_pb4LJ|uB+?HrzOT|)7c^0+ zyO^6YbdNd>=-mkV82VjFWYv1mur)EB4UphWGxUhmbx`?#L(aDCOe#xWvJlr+lrA|M zCDECFXEWgp3?q;|ogiBmYSIvIO4adKxPRb7$Qn?}#g=}DrQUNez>EG%S|6NdbA7P& zMw#G`MC^he?(t(R%(x{FH_cZzp9{midJW7aEPzMgzFYiuUFh}T?CzY6K&&zFN=>pQ zzy<(4FYcT3unn0)yJ>YQCIW{f)D6NSF;yEY;uaIgl~U!-#L0+%6>2KftX8^2|C`)3 zCBKDE%7A0$1dDQ(FK??WV`EdP6*NLPd8@7#Jnp-Z|tCrPnh^ zj~-}$UcD~2@mg>lsvT)*c9j5Bw$n&;F!T$e7UgI{Z3pmg=px`}9thB{pY$}VBt7wh z7s>+LPdIU6;W%1n)Gdqg0tE=wgTP(Od1)Tl!F-gs+J2yCPnbv4RJ7@NGIRy{MXn;| z^Oca};%Yz|1*Q#YOcqO`B5~i)W1Hjp?9V9|u{N044)%aU-~>1au7F$jJ<=IpFP?te zdU0aBwXd3B=m3BKk_Ipw>Rylo2<4^33gm<(Hb{z??5&kXh6(eo%UFOB36OPX+ag$} zB9Xw&<)Vp}0WpOz(V?p&4pLiYrB`BQj@{i)s%IYtcpp2z14ho)^z4?nxW|081y z(JRo#ko|Tc?58{|VA>8(bvn_RS+jh+^^W58#3p95!c@{hj@32CdDLMZ6DGBHVc0#^ z#}HN}tP>N3J_!u!8j_Q5^~(1Orvc)`ylC*>q@JYO`25np)UXFYP$7EY62RJ$Mk{;c z6dlwme}yS)G(?3S%8`s#z+luRETv#H?s6IzG06g*Y@~+@3;;tOs+^CRW3xxh*SH0H z03B}Q)S79%XS%w4_Y=<6u)>qEvN}XR;{z9cByZp%KJF15}V1VC?R0leTYRwh!+vrjK z8AeXOb#jgw9j~;ytbiJ%ZlJ3aMPMLPtVGDbTygtGVV8BdcW53Q8h;#9|1UvDaQZbv zVF@h#w7mD~L7{H)#q%%FumYZIn*u`Rf{Lr3sqTLo;Q!tKCngq~TKk(l*}p-NTzXs_ z>O$?~k}Jvdn)X-#4VTt}UqZ=M)sm4P$JbPf|9ssq$lu|A;s2hdF`7k)h4MPfKG^Sr zp~c1#*omR7Q@K{2sL@a>vZ>B2so0J?u*RW1w4c;RBRCL0^OGrun!~aoi4al)?s?#w$dLOj8QVQ(%^lRqMs;Jdc=o)3O?au=#=gGoX<=zYRP3k zgM;iR$N0+9^};RRG&xlQ4ai*d9-n6c0?G$JaIw-OS4*GyiJV{bW^#TN=UG&!)Zx7E zIn*0!HQ)0aFMRO3g@8aQSPNHurV*KOB;d_9HU6w%x^;d;3fvqA<%YKQX zICfdo#sM82+9G-g4Zp?t28_lrm?j6OFj$ZW<)3LfuuI)JSOYFNSltU`4{%xvz-zF# z%9XD~1R=2v@i_u%Bv~^ur_}{exD(zYc1Agyxva@&R}V zO+ynGYLsHah|LP3K4;kFGvapH5I*^K9LAI zQW5#cLbhP25~co|Q6Fiq`wEL*bXgq7;0Yd$3#>53!E3XaL<|OzK`>E})>?$2eu)hxymwPkV52NeAe~$-Rmd^! zUj8lBO7-nE;|fkwe?S!G1ybio1{h3dv@qD}F}i3ij1EX{!RZ7&!2rD^A#z4Yr+$(BItbpv=>il^wv77RU2r&!GoP9Qge>B`j&xp9kYyb0u&ALHV7|x( zI*A$EfsIAv;*gIg0p3O!? zh-%*2_~}&UL}UFu&ut@F;zr^P)PyX$n-_YGIaTTnzQH;yWyA*oQF#>WEi{`fl2pne ztMX7?uFA>gnbm~b9xCW<8BQ3W{AV)8>?p4&AWwb6OQ4izVnM(b<^|^d05zD?6{@9i zMNGG@w35Y@DUu6oQ^8E2kBhBRhRal2`WtE(!ql1#9Kh5;85Bjk05ehz^~GggBN`F7 zfV|ZFjR^f!%S)}qX|b~8a*3Q#D!GzNb#e{BV86;T6yz;la0fAKVTH_VW%Jh)<^1kK z6}?T(AOYIsu`OXAQVB5N&6O=H6NY86F-}mVbYd2S(2>p57Wb|8X7Pdq+F>YDeR=PU z#JP3GJi3xYr@r~_#+ftcqC{)at;r{|=6v$mbzRLhKENgw7_{;ZyVvBA2#4+!ACeNv zcAnp9ut1auX|Z!v1o-?Fh)`-swCGATw<62~oDhm+Gfm!1?lF!x-5g}19C0e-vZ09c zz<@3Z=fLm4S4Rikp;w9;T9IcRe@Q{wt?8Z|NgvnD{fG-B!FG}Q5zWJFN`!L(jP}6UuR)MmB9r=Kz`wfIBee3I*C7`0!w1n7t_)+9mp${F4x5 zI%i1N-KHTFDZgC$4#LLifAe}|*Z|URt=O=|696?1;3T<=E3(~NC@a+^sGjbVtrN}{ zMX!iU+38<-S0@yR7H3fddL!-J~K>BpS zDzF8m-~lTpc-XpEdafbeplC^~xBl%g{TrBG%VkZc$^u>}q%@D3ylZWWCh=C2S}dh9GB36fNi))?S4{sXA&(ZjWal&^0A zXqYoC`Z7U$n(I3ZV@d>EnJBaoRIS+3m9&y+gwLfK1Wz$;#+<*Fo(U7AA6^1Lq!@S|J(*Yny zH=#6i07-XB$>R&WHBwGYsI}ep!n8+b;2Ruk;*_xJ3Z}J8!qnH$8xt`bFK!dQwSn6Z z@-Qt;FQURu8%>f5|T90?3BeNj)YDe|heQ{MxY0kN+TVforwI z`u#+ahgel2H1nuLBr%e_@`PMU=^0+6BEBloz!06@;N3$t_>f_apXdcLFsI&JL%*TK zEFo&z&~HI6BRVO0gyOJ01HFPsh>&<+TcWgomoAlk(q64FX#4fh|AyY^qcf@lCiIq^ z^li|@Du;042FHV!03jlZqr_xV^hu==ZOVuo1z5_|Xwaf#I5mt)QXfX}Oz$;Ul5N)E zZ1MN9Iw_&VWr|mO%x9%Q7p1lMkCu_ID#+|j#eDo4x~to(4|QKf`|Yu+^+%xRdZO2Q zp|=`Nz1v^bM+Eh`dt@VPnpI50$7N*o^gzt|=^4lvo-8>*j+|=6b9a_o1M%dk*?2-W z8L6L>N!w&*zMc!4f~c0VG!E!#?Vh=G$o~99IiJz7-ZAZQJT$wKvHL=%zSu0=x_WvBfTL_Ma@SXT$L4x0*nhbt4Ly8o@RBaQs_<1Aj9B=W7fZ(jA<&a1R1lk*c>jGok5VZ z{ykCmW{6+!%M>Y4C~r>`Yxv3L3NV2X{qC)Qck+M|Gp2bD6f zQfLM!J|pGnnWW#`%s>Hl#*$+e4oYk)r&?;fy``tTy#w_pH_|qR#LKi5*5`80F$sf^ z_`3*_f0wAzljggMpzpfZ#Pv-XI0&DYxtHz}BByL98oY)bIofj}QUpZFr{Ro7EhWln zrH0fj;Gs(M0s=#_;wi#8!Rb&*j8?|5I`6-GSIsq4`+sBg)=+~r)WpLZX~Ez@ov`SZP+3K}f46E&7aEM8>W4nOII*+n{ir?7nkHbFS4(r>=}L zZmrM6a?;uc#cqC#eHbU)=9Kg9whUyPhgaY$H0>|lA`_pwvn7$1kyF4b|2q0w!|gGx zMmbvtoW2p>)I7_!kcjw8(rGVk`6%1%vAw3@AXskXDZ)Qq33|9)R(4ZGc$UeIlKyC;K{b+*SZLWZn1 z>&apa3u@_e*}Ty&1hvwRv|DW~Q*Ba(UVO+Tw+L=9?IN&jA~8WsuCbx&9mz|| zX@Ig|Ly8D8u{akq(&c>n_4S7rylPftZ#S)qoHGnkS3A}x^7B}NXCKNAin2bDTv#;X z6oYgu81zCdq?pooW+Gm?x}>4r*<0ms-sV|j=}>2sdNJ_sH5lg_bQy7>QR-fwm= z@Xz#M=#y z0>vtAMT<%b5nyc_V$Nf>O-~k~+YJN3Rf=KC@3HB%nf0}39ml{3HhNC7ocF2au#OL4 zpCH3{Y%5*9CNic6Z44zfF*Ff)#4L|raJm-6&fmnCu9u@wL|VJEl11uk>x~LT7ol{^ z>2Y_j4X-4Shr@8_7URwWGQ=9G1(ziukEEP;+F`>Sqh@LPwjWrKS-kJY(b5=%sgus( z;Og0wVLU;i-4T{0!xAU4u)VXdf25K?2za@c`QbRXj>jtDZFm_5cb_&n$^%+m+I|Y zVBciwasYB$Lln$xgJXz?ca^j+Hohf^PKMPy-~M&TPyDy6WBj5H-uM#0JVS?0U=WeC zdv3<30gTVS547;(o$;D-nKYl zgG0=TKAd>ZFxM3l$yF%ZDxJy>^o+0WV+IzdVXTQX)Bc4y-E3PeJ(&DzZa_~wpMc`X zfpzyXX9CM!TRQkXwv6se%0V_;%o8z(f+=pwjy_yivx2Il{>-ef7BI=tJf zMb|Y>;5I%rK3{@UxZ9Qiu=vWedxyXts!+rsvd38^_w*{W=TI=#P@bzbG}kx+THNWl z$@*YaQ~-b3bVoMFE&EQ0i57^RL|;r!P<|V$qG=R#>LA!aFw8*MHg;xK=z%doB>RX8 zVJOXq?qg0+_ojdZFH$ewL^ON#B0Bg0zJY1IiY{TD2cvB> zBWmU_v=(GpvZChbB{Rp3yKw;Rh_!fqltF5CGo^y53u_;x+Xi$qm@pgaTW|_jq8MLy zH!{E21*zSiwOG@N?xFf@qB`*ytM+8i*h>|Al?{crOTV{|ZSJ%0f+?(tHh7CmLU$9sba&4gFMRC+gpgey>G7UK8X4qJSh2lqHRc+%xHay? zqAED_r$!ZI3RA9|_vH|VakE8Xpv&hZ3q-7j(o{@oARR;RG(b-d^ME-zkh*93pD15; z4s_T`Mp$vVjR_;w`3My64#S87_yq}05goHvAdfs?P;!<1qizMRxqr0dTuG{GbgkDr z8wlg&WXxV#zyz_Lt7=$xn`ufPZq=~8Nb148MY^FucX5 z?UWQpo(CSh?HTqb4mQ``eg+@XQ8=b!#FLXqX5T+*JAIvKkSneE(pX{Yrc1~ZnE$Zo|s_Ddn{T)n$r1l-62F9I>N;bxEE;>nY~^0e+-`k#;!IViFZ zK70ALSlflNHw>-NkuewOqVIjgcy-gt=Wh8kLWn|Cs+a3Idq@U5s6+lULJ6Kw1!=Hg zh2H=V_|Rnz1vEaW0sg%+W28Hi+9N4Ys=+D~L)Zul|&v)OBY|Nw@ z7?eUq#`O%EKi!Tl_~`jz5M$PpKzVQ$3(^AQwQ6o%3)*`Bpfs>egLJX&(;I6CJZtZo z)oZ_B2N8d66Bk*BUS9H?SVz!tI5D!vQ%URWWlLC{K`XDz!=3+T!mi-DqL^LV#EsYg zgj?v&?v;!5z*Tz06F<|7y<$V~HuruTe_ZtmThQ+{yg^%2G9?A?l6p#;X@jCZf$zwZ zTR&@$?%&Me=q$I_CVyyK#g>lkvgHgt5IytkC2j9Tf87qi4sm^C$9EEGWROE)j!n;% zy)PZt>d`=pJH(+sH7bx_Vaj#$y*$Ejnxa4AFo{79rAmJ^SVs^WB77ay`$gIX6Uc;3 zs^bhOgfT(n)zk?eA4g;2680w*ug;$Qp$`*ZuAKTQrN_G~h8!baU7^ z3rOgIj{H{uvk1GH#lS&pwC9@y2UC0=XmG$7I0jJ&wA4hRbR9Ef3yMgosRWI&o>9}M z%#dKpco}CU*uE4vGVqZD_aMja#m|{koG7F(vZIfo)Nwp^!>XaXRh#pNX_2$~0q7xrl|puN{3V zutH_488@h99p<z+62?Z9iFW73ave-(FxdsnD0uE#J0D6hpa5{BjlO!8jmb6|B87^}^E;6yK%2Ht?;yCaV zwxR36qZ8D+?IwzXqj*K9tYg@%z1eGzvFi8~;nJ8+;8%W1lix@Sq*c8YFJOwrSM+N! z#l;vW!Ebl(0-p{@7vv7OIZu^d3TSU`WCSwaZhx3*$Yf?9b7}>>aT$`$njc6`!oTVM zE*W@|%YL@w1NlMP)8I+|NNvH5!{j_Ym8CVe5F+IOlXw-#p@a&WLH7$PC}c3)Fl%$5 ztdQFVCs4yQ5(AZkgM`m05T1+PH4-k>8~wtv8uFePD;X-WK6NLoDb^P1a*qbubNz8) zYH}HNG&XaKeTjiXLH5nu4-d5W@~bG2H+6nQf8QLrG~#Q=4&Yls8j@G>6gbZK0-EPC z#kjo#K}Zu=i#*?79%<%44GL6fg+TvlW6a&$@`F7NYd25^Z+tAkLUWN86!nO0C`cyK zfnLY=?&cZsgIU`)u7A`0%K!H9o);2@A}^L}uZoGiiH~L+O-bousg$rGwHnj+?KkGj<}}Mxt9^t6VtmkN>V(5=G{1*LB_3BjUg9 z&K7|OUIY+AWG-I7$#L3S2INoxJ5{cFx%O#%T4?X-e~hz3k=C5fR7BiQ$yGi5rczE@0&gBFX=AVPs7w`87gF{n0|v&*$^wpqF$ zb4hXAGR1Z6$Op)E?e(=EIN+aj$VYVSPS(AkeYt*-I^76FkJgzijO_uRj~u?lU8Jl{ zNx9GLylv{z`S({)C$7?|ViZmLs=9`{eK>VvQ+Ix*jJ#U~3^Ost$re-R^y%HZ2$E)2Hy8 z^}Bi5^T)Iby%{M=VVbjyVOE9FIphsu|Hy6vSKtOdI*$K&p$xI$# z(c%!&v6_U18)T5vi+x~QJcRNfDmpYM$C@<~8k6thC>Im0d@kIe1I|ZG9JR=?@|r6= zVXSQsN|O;o*g*1n>5npZ3B4(}#?Wsi4y+84;+XbdcTDr&aJ`)baj*_?AO0Hrf!fOn zk=k56Tp%6J+T2dlJ}=GgM{+cBrY7|M7CS2k8OF`&qFumY z>Y|e#KHnlw7WyED6!ZDi6dn%FxEyRecp6DU-lPV(ApmESV$nKbEV$Q(5n+{WRQg5 zMyce1Sq5HrvAUawa}9*C_kYB=#Xxl*Ch$%&Z5s>5M3qCw$ZaQ>rgq=7N@AtG7gC6$ zc07mpUARJc?n}KP2&reHWk*z>Bcf?BQiOHwSqFu^(bVL?8q?i(_q=w{fB$%aByPPL zXn&;CqP7^$Zne?IwN}Ab+~fvXhsRq#Z~)nzCVt6(Yx4TSthmA;yFL^^cHB~qwv#@^ zgoBWT*UbHman8i1>|4Pi}Y?vL(LDwI^ z-PvkYhD(omT}(0pH8d$k8*a?_v&ElW9|5QoLJ8RwsBi+1+~r8ADOGV48LV82tQ8V| zRw=J8zegZT4Fg*MEvV+mUu=6aH^`iz?#)1$)iz8*vbmbi_X*PEn)|!P2GC0nZ|C#H zav% zqDayC6hWr2>|$=S+Y#lIqLI7s?C?LqNJO-0#yQNTh&N~e`mXF$0t1A<8z_EOmm}Zq zA0qq~ojE{ccB`U$L_==VvpPAePR?JRi6FZ(M_%nNg6kCt9TR zSv}6+l?qIyH&Q|R%v@)k65kd65;2bB5^KMcM0#GA_Q3s{t$s{rE}1wRz(uV(?z)0F zvw1f(J^d%XYe;iqi16vfBF!bD(`%!{0pj!^i-qhij2v#Ntq&7Gj1sJ@_3bhsH8I6n zcY)TZfFk3@96hqM)bLm_uD6sNHh{8v%HeK+1b7Va^=P>y!37T|dcWh*n0!za2-K}{ zJ{+JhdnfVm(m~I1G)3xr=vz%wh5QaqDKk?rC$|m;SR(0PmcHI0A^Sr>y}{v?Y-CyET6`cL$SFzP9C*d^89Y8c3nXl z6mfNu`8e>}=E#9)1rcGVQ&?Jig>bT;xi;QVcLC7nS&2f{t_1ZF7#{$>kX$qnHox}0 zUKM|AD9po?4B~2+*_oyo#xlvs-{LKT?P-$DjF>VfpkcCMzKgv_jg{5*sb7%?t_DA@pWvcG%OTYUCJcW!8+ z+aLHL%>Dv<)9>%@qP~$Q?T8=;S`?>R9f1EUXBM!nalbCs z6ZDUF2~nUd%Sq!3uUCc*Aa9S5f5X^N4!LJ6Ne0Vy(%YJ;$l;2l5cXrm2}W*cziyTZ{g&AiYw)i9j)_cKG$w61*bp0}FkxEdli4#ss$0aNenj=e<~`9QJQEYI0;`xmh4#~DJ6FsV|bA&CZ|ejb=%;AfApsmio-D-xzxGvpA?IlQI4gqJDoyLDl) z^X`t76eC(Vd%Z~R>?*j6A)VhGL4pi5Gr8tKB`YXh){U&brU7vgQiC@`Q&U!H-N_>VnI)G^G&As?wTA^+r z1jLUF7fJvTkpAQf#$8N>S|7hwi-)~g(~@38`jakNB9B5W-fsOUD##3{1KCvq$>-H-SK zmbWOVp8y=~PaapC+zOE@D!QW7Kz|O(nnmlP1`b#j(c!RFl&PA;DCI$0zl{ixKFUd1 zO8I^SYC0hepaSHR3x4hbW-v|o7L;JksSr@!Fz>uiLFgPp1Sm-HC3*>S-=50_Lco;v zYupXVKYuM7ObzP|s|`mZZSrzd!^j>O-J@cCa)F6lWR}1-$B(b}1cXljX-bUO%BJk@ z=eX>xm?J1~SsGr%>pYS?^Mw5xq*@HOqA~hf{oe!l1TnV`Q2sYu`jS1W0_X$~Sl}m# z+cCmr#A83lMvyTwAV5)~SH$1`*jw@Urw+>^4xQjeC{f$ekw4+5^^<|85@gaD2X$+P z3SgA*S1w;Osbj7n$kYU710%q1^>yf#_kLSDNZETXlPC`4y9VN)<1RD_!|1NeV3;uI zf_qW)<_~nX7-!kyRlSV6l+;9(NrUo5Ts|RUG>-1o!AnYq_cD!1@1Fz zivY$0xKTb@0s*BJK`6QozN%+p6B5J^GjJO&`P62Ghmjy8;i@Y^k5}>5uZ!$L2|kA? zE^fHc40O(IKBHOeR3D(MR+(`Vswv{y=}C{|OW^M4XS4!L^?$E^#HyJz_$YU4glY6%!}_6 z@Ph(=1gF4{1JS1h%s_B7z98Ti2K@4XUmFFt{1c!HgbCqF>>H_MP)=?I75Bbj)1b$M z6$c330)>l}EK|NxRWfkH=RaG2XIIg-%fSMnYnmUw#XQ-P@^<~t0wl@2^xT>Me*^0V zR@T?7SOD|?zyEw%hZ&c?pK=y~`Cq;YQ$NvdIqi9Nu59cE?B_Q*4Q`J*ufhC(NxeP$ zlM$PdC#%`#{B8FZ`Pm89ov;AwrTG%IRWSJsw`r3uekkx)X|zEnbVpwd#z>6ERLsUg z{D3vsgdNz012~2=xP%+HhbMT2KlqnVUmpY+!N4B>#k?Sd zi#x%@_kK##nBGj%Ypmul#Jm@{@Wn29naf}4s@AZ!zvD~v#YjxWLZ4=9#LiELqs4)0 zKu#Aa#1}Oik)-%59F8c&HIZo1$H7lO7OOXq^~2Uj$>ltg|6V#V=^sgf;2z3nHHCC7 zl2i(oA))3<%@J7dN)2CZsbyq#1~U1l*~1XC`R+8cdn{!miM1fk*daa@_%m>buZJa~ z8v&q9B&Ewe%VC?yYGT%E#O#ZnfY?{Al^94=@GXE{qQ=+WKb=+*1h5>eA_P`YQ1rx+ zOae%T zH&(~E6C_ImL0SkuH?3xx z1L{)&flw16i(|t5195tA3lRe>YDKrpEQ$aJbPx(eg3Ro z^zzxh(ud~q>etNm^>3crTi-VCcl`5w-}C1OfnU$6e`5>&ozVXy3}7}w2SvaHMZ&cx z#NncG<5>(o{EH)~vIJ73mq?K%Q>`q;wzkzd(zwk|JlGZ&9&fvA-3t9MW2JuO-^$$O zf66^(Dm+>FAUG0K{ZQb{QTG&|VJ$ze=Upc+f;xNA>ingLb@682QC+_Oo6O({(H{yE zM3ik)o?v3dHt~5*>?Szd{2;j7&J*somxZ_ObqU|jwJ3ZD+_fKrI=dU{5AzrWJ^m2kA2xsC3DycOvjd)@wdGhI;BAJ}>3^F~B3(4%lE+Xekzxoi@c>R4bgSXSJ z*;DEG6n+mb>o#(Yov734FFi)Ri7bYUV#VMnHkF~G*l`#dii>d1P&|1J6U9fsuuwvE z4Ns)_;Y*Y{QWnbZ5 zODIQ~5>4ZD7?{&zGz zS{thvWYg_u-f?UGaNoEqBziFxTt=a;W$Ieb=4{{zH^~)yk_~%N%wXS!xo39APPdO% z94!((+L*A7Dcp*kMo!}Zu}Rz^Z$amd;H!)i2*)d#sIClZZAf-1ROeq8G*>X(!SouNiz{pH9qOEF2;#E*>#)9s2YeGZ%zF zVazT1{P}8iNdUw`t2O%qi!8SKHLPWA>kt5iDs-7~~q9w~#tXi{f!=^3U8nSoS=k1DkEZ&O` zwIG$CvZ-)tAr(nQQ}xt*&SIdTc0CQ~xld4f2u$FU2gnl!uY$uD6M`=z46h>s-&hfa z?`yvy(Y9xz)hgXXag;`7G)8yACwdYlagrx(19l1rHEuV6KD(LrM_`*^BoiKMqE$d$7(cuwj+IsS#92(n$YNX8LXr0Q7F z{aWDyLMRt+H;2+#ZX=HHBH=HHSqC)jvZ%J)Qf-P6^xI0HBdx0pLh9jzFRr)CH))2O@m~BV)0NRH3r8Hhhb(%-hoI zM>VZgk5HK!e*y-nuWD#&>3|FdlORw=fr*La>JS=C)4s;oP^MIynOj)djY4B^c!HH@ z01+nCG_P}zrq<&zSYlwA6;@eeWStE**|g+0+9<*659dD%+7&F6FiRr-J;+R+_Eu^zTI6IVcnf-p=$;VuDN-39}an4<4wJqvSxnIU(yz=yegIE<@FI7K|r4 zqI?plT}?F7V6zwuJXL=8&HHkAqqs=JFtU52iWuWaVG?PWlFrbNp5j9UPtqTJuA_O8 zzTEb|aN-Styld!_hz11!SQMrNQ!jxj%tc}9C9o*m2Y*dvdN)>HJwu8}yoRFfgz=RZ z;3&&N4$eQTu`tU#Bs0o&%j`nJVZVojn}g=%Xhai>6z7C@6gbN~aT=Uiw;X4KJKzjc zAZF^!+yc+eDxFWz5pcvgDBkRZkyQ$IEJB0|+50mIoVgF!CRFNPQt$XOtW3S0?w=WBY&@uv{AYUidK-sp_j*L+vPz-GN??LgjPBH_eu;U+%m zN02YcI+7%xHHu`=m{P11AA?e$3|e_;O08T?rL_ljk7#=B8eb#Y-O%&su-=DvgmxEs zoe>w+Y0=WV?NICJu4>)g7ju6Pw85V2{Zua)c8h~cn0>w6JKEoF?+qT5ZioGJrhoI_ z08fLEAz%eVMg{TEy7&>gtH1#R85bvf6V$H}`V}Q!`z=`+gIH$CFp;@V@!?U7gaR~7 zDbpZ=bSzt2d!zAV6d(u+m;udG%=#UQYPjd@+msC~PW5A{$kwVW8%1_d^_)VW#B}T` zXkrA?7|n_HuB}~b||u4>^IJ)8e12>ZO-J5PR#B(;30S=8lB{HI*QP((Jt%;H`4vKmS&Pm zo1#ET$tcBomL{t_S_M!ErLvT&sjQW|%}J0_E~(Tzh^;{2fdJI}aU3_B}v{b7P zjh8sqYP#ImWwK@0+7~*gag)POHQ)K!xw;HFh>zfz&g)p{jn+r1_l16v#ea|~P*A$y zq+1~zH581HzDL1mDT<+umGU^4ApJ}nbSFu24^x7DsI&U3x)uz2WAA|Xm&*M(r{nD#H-GIa& zOZDSPH<3(}UEFDxXU>B2;DW?kOu8i&^Ri~wrPv08o8XoNY$y62g~ja@zMP_+)&_4h zJ(N(%jF%#hLheQBx!}&L_lhc%SGMPlip1W!H^&G%IF&|)lSYcAk2@DVdfGx)IhC&6 zKZ)i^JNTqu*Hocy6|ctzPe2^+r3e|faQ33IK?eOqu3_2$?3Gw_>KptB$H%ON*6Nav z&LyWkl@lOCNlE9P-=4}jklfD;(M(3S6|s9IF>R$*jWh?dwl1aIPzFm>p;$<`6XmW& zvEAgnzvIQ3-?kn10m2I^YWj`Kx=HUXRx}pTS1htrs+y;S6pXwyVK?|`^R_uW@PUvju~-y z2^Z*p6w5*H;Fa4GyAJK#6Spq4yRT*sMC_52J(j#Ddg7VDp6QbpdhZu%zfyTg?iHCo zLPZX;#Vey?nNg;yEZHiQBbZ#tnj~)_l+rGFI%u8NO2*KJ(+`LAN!EcxGz8X3n(?IF zn6M@=*ba6=n`z)`ZLaZh@ujpSY4Qo$W#AlKtxT>hueP*{uy8fF1npXIIX((ETJy)* zt)}I9uAJN3((RUhllBM72a6v+MO9srtnEW$#%2BssOtSqg!x-$hLK%p`^5p_#xxj@nW`=((DMq+%VoG+2ol-45 zxbZGjzHiyRB6l_`@`AQX0|}k?A#)2eDZ^^z-b;P0CaQKu>Dm_%+1`Nz-wSVTo zgarmgcMV4exZv=R@q&vW@G>0U0;~}({2i}hB`OV8Mbii?t*gV8Ekt_H$Q)bk1=&q! zLiKhG%>wDFwRvm438(s&ixgI31uQ6erm9G`@O~64_QvsEd;?P@6k?JXQn{6(!wflO z6;P@Q14u}ct%LwmRmXLh`lU-xJJA?=xz*3gHQ2nB;*>cd>dfHkOIdTX zZ~;PP{H;_uwHh4EP+!XYgbL-SAyba`6M|kjXI6ej)yy>W^9vC-v+}p^9^~)%e`Nf_ zlvF5* zl}c-LH9iN<(FY>eJ{qSooC|%shnM-YEk}o0V!QVt)qqMn@JOy%Z!aZyd*$9m-Hzna zNc_D!HRA4?HD!Wk97|@|s&muowdYS{*vZ7zASOF1l!Ny+h~F(x5N9V6gs8q7t_dQjH z+$(=;L z+ZD^_-3h!_)L97x`Of7dXDznQ7cSqnP_2M8fk5ET6}R?GTzn(lFgpG$~ctqpFjxz0tZEY?Zd>tp!$0!olmq#QuwOsyeA~7dZJk~>64z< zVmxxCF)y_uNy)d$ct8LQ_Zk}>koQaw5f$h?-Y&*~=XF4b*HI{BzJU+*4v2sl(j5Sx zYqz_itE=}I?V;Ft%;$PO)InBx{gst&?i~SL&B1$0LW%)3E|P$ySq5loKneh*eVsm~ z>D^PqhDUGAnutp-zU17CqT0fevA|qWTdE1sG$py5)@<3*Nl2?|R;Ri6y!MmZdTuA{h`6lqi$)p^!r!gKKPKUPlqa z7(fPmgzBGDqoo1H%91$rFkpybnOqi17|Pv>8C=%D5TCLAuwy$9TTGWeHKxUG8B#-uSY%d& zR#vQ(tJEqZk=TqHHiL?qm}xibQWe8k5vDpylOolWdg6o(kxB_8B}7daB8}$75Cy`6 z?@*zBBg6#d(VCAI#{LZP35Y-t6^M|hF_tHfSsk;FW?l49qZ#~&hICXXFvetmi zegRom$GR@Y*b55QEjmtoVE_Cv2+^sW-Ge4rWC<<1W8qQWz3N{9HcD@qYwCFc_ek&k36JXn?vm4JsHy zRkf;G3LA8g49>!xhkCrAWj@fMLx&DU)lg92ca!`R$lS9Qvqn~l+7hnl#IzgH!camI zm81*Gi%3}n7A!h;1cHkQBTeo?ASw$I8aAP}*0Z5$n|24>1^2*x@Br~avkqeNXJmt0 zs1B4%7)tc<2=eF|$5ebENzjIZB(KiO8xfLt>F#=qQBTt~OO+%lvG3=8wI=!JY~kDA zWQmmYNC76<97q+js_K}@;f@x4j(BwHz&drbsOmJQ?ii)+f;EL6h!-;Mts0!klYMQX zXG&5w2Mi&0Xl$cg#1fyF#yi+q}xW=^8MDMe7z8W!nVJpd%EMtkLGC;$N5Jj`2@ z$w}fMcJ%l?01saE^kk8Gcy4t%t0JA~k8;Y2tq8pGJ4WFisOMAAAg92>jf0>HZ6E~z z00mDLVjUj7Yc8lqv% ztXUCMkytD)7FWj;so5A6B#8ss(N^1UXj4R3(hwPLIwv#lb#~sLyzy24SaP^6B}=1if%TkNL6XAI|ua zuX$x^4mev3w)@~>8VtwWgy^QM$8rmhEmk2#aY{XL?7dsrpnqby;>dIR(FF{V*D44x zECPl{N`~R4+lL%b5Go8}BmjAp!XZE`@QC5j?p?iou=G|i@TPk=T4z`5uFx)q`u^Cj zKHN+5SFB*Xjkd59dSVYA+Y0X5Xo9o@3*e*166SHf(NVRH3MXyveimlpy z8YOPRBJYUY_UV5SN1mJbtTSd85P+j(WUTEtqNX3+z%qc=m zVk&8}romJc)RMx}L`^IFE*AniMI~*9q?nqD-DaT0QY)Nl`E0^%{>uqi&=0QsDJUP@W1RF4v;VQQd5lrbYT08!HX6A2xn!hAjfVDHQv z06LwanY)^a3$e5jk-m+GqZO#Iw-FIhd*4P&lvX!VRhc$g360_GY~wzW)jb1T61LmM zCf{vcld#917mFHG?6##HTMJ}OSTQD-?6#9hm?VfUSPn3UK4?vGp$U4xEzfe$#e=%R z-+JV?&fvG0^d9EckmYWBkHvZ|JtLWgMb-xvjAc#0S}-JZp*gxWL`ITz(ZrKL zNR2*J2>=YyE_t>D5POUuP+^H7m2=-4Oh4<3bJ|@5B7u=J$Q}Z9mMK0l|GV_!Iw_SA z?HaNF>@Pa3EUtYmC>=Y}a;;pQY26G`4GrOO`|HnC*vV7Ke?HE4;irw1W(sxF!Vfv4 z#Uk#X>{AW<2y{GOgjlK>)71>|`QS$FETS{=JcsnSDrLq2`pyR5tjOWRVr+qOr`Lvk~iE zIx=;ngBVd(G3qE6RXK4p7J2tW{u)Q5Rj8w3I%*aIx6LncNzWDG$X3;>F4 z2w^s0q5FVnJpi-s02!d@py-|GmpD-_DY#V87tsp`z5ubTd4MS$Gb)@6nDN4-1Daeg z`+u$hPh-{o@_2mCzoGLJv*yPK&0ACE7mt{6rYpzFIJ@$U%A=98YdUz=bH_8rfi^w+ zMEkd=PdBLEIUPE$SDOs~D{%WH_TP-ve}dAl_O=Dtfgb_VxEq}T^A=` z`BQ^|wR$sAoeq{SY^DQsVL$cqZH;XA<~OZoGyN96rQ}aawY-t zdKquDPH*3&cIoA}uQ2=9X;Y-V_p^Db$|}-p>?Chq+eB9zToG7B2(b195QzXPen>F; zq#mmUfIuk_3r2}gEacgMXe<)dvz_k_R1dats>)vM%~ciUIMVq>z+RRF^=%n8)`RL| zP(9KLun}v3L|{91fHGnMkOAx;a+ySCNPj6IL-9)haq<nV%Z{4+Y_DfE1Q`azEaP zVD?a%u+d2nF%$Dr!wf5nh^Rhi+SW&%^?D_P}TEAb0yAg-w<0W@2LUaJMB=c+EtlbKzPgmzl^-;;Fmqq3l2O#p0vNe|Bg5 z`*Y{KFLoZV(Y(}h0|oBxq=W_TcA1HLcEP&EOuCzinV5)D*_V51I3W)1GGUV$Qg~Vv z4(zT5Yak+;ApJP14!uTS3Q_;P;L%hmF{W*TSx1=kfeJ-hLHq1m4-`z&pR} zg)xq?9J?I$$Bbjl;ux@avBfdqIn9mO*07Hud7ll96|Q&Z-R`^pc4D76wldo5C6Xv5 z(mDiCbMhn*OA10rG8vkc5+R99BF4rkVQxvD>g#94OM0vX7|Pr?uSQ+Pv1RVtxv{(V zpI%BX;=bJn81~ri=EbHNyLqdLi38)EuZD4D#K7O#@rc2`*zOqPg-S&R-MhK}r80*wU>hEp&JPN-*zwtjtGLL>ezV{fy~6q1k1jB38o`4HO8$mHqLnGIm|dO@@v;u zOPm+^?GH{^{XLT$OtRKV!_Ic#hbuZIcpP)iGKcXc%m1i(i|$!KBnm(rK zGqRzdv$E_y35A0@0(Lkvv-Z{pGn2j>PJf>}3eN(X@K<0C6m(iQ7P@Clv`Cl!zhNMN z#b6)QjOdA`+jD-t`sHf>&+S^c&}PuOBe!CTI_6Hkly#pupZ|q~k*PY`c=Rk?YByxV zu{&`kT?=R5DteYK+sI5)MVqvtu3YIbY}1K*31vM?m#2k$>RY+?|4fB~X(_MG%U|M0 z9n;A7d0z_uQ!_2&|BpxE9Wk1wC9ozKL)u+)A~Y$RCUCE^! zEZK7@B&%U;=P9pgV(%pan7XaqR#T@Q^qI75-<7bOris0mf|jX+w}h$h+HEy+rbWLg zD-K+X$ZMH8cq?j~IrdRqIe8nV9&F=1cdpfdX=@JMh$?8CIr=E+m^<~=fl$XOuMm_M zt+9OA2*@E5gq;iKf;XwC{rBI?m6&TcZr!<8^q~5Cj5Wp+y87PCYT4A9O>(=-{xLv7 z3}9vm8z&ToNHSr`%7+pbm5@Tq%44#W@K#ktT~k|E&%oHkEEo4rh#C;tvQ0(nCzyr( z{8wB-h)KvPsZ#UzKi`ZF z!~kZ7uyI0RZ-R>r2_H&WR6+_ZEB|H6=V?l)sB3EL>KPcDJkQEcBC}{#KxC7Q(jG0y z;m7n}m==MJ6AD8h`B1_ct$(4|gcMp<9;2k9u9@xnckH&SXJBk%R*RNE9b_{&kw^Z& zy3?2z5{1U#00ME9dT&~$P-%1qlf~w&ZtqWfe1T9THk3$ZdAa^NT&_^6jB4EcdK0!m z7kueDcAL|~;vIIvZB8X_I6j)yySU*zyaGZZ;*y#E|H;i~)5;Y zhP`L++Xr^r?%0{Tv$005wKB>or#uTRl0-5oq>3g!(Z^Y?(o72v=;p5o!i4aBC6!Ea zDySp{8Vp!);LcyTc)6-H1p)A-F9b*ce6>G(m1R^D6-qSd(fOZmZ~*Bo{#!Enn*RKZ z`LAh_E-UL{M~w3;d(}&9i7&t8W>D|QV)E`2w%SJ;PuA#>OOECH&%I#l>XGHlo?deEox^jQpp6iS2OEB&C7=QyTU$l#k57;XM z7yxf{J82tTCT|lSrhN+-fVBTZMK|+*TGE;}I{AMg2m&4$P>=*dqH_#+cSoyNiuOdU z#q;_lJEi`Qr%Q>gWE1bEVvgot62_cEK5?f{Rg=s&+2m79Ibf=(r&-gQ_qk9tMgAKf zE%#y;ySN!=TA!I`nRV0GyXR)hCX(pWvGRY#F9CHgxD{D9ci9%xlEDlc~f+RA9N~1HFEH;PB<3j=$wCdE0YtX1k zvz9-d^WTrR-r$RXGvH|k!4Op6TU>1bpEEv&s_?@5Z|hzShNJOhI-4(+t949(1OPH9 zpn?Vn1T3vwCYWG>4NmhGEnBq?kBE$ljtRuZ#U~^NvvctL7iCq?7t7VU6*XaK3>Jqc0N{_#L=u@orO_Eo z7MsK6@dZMW+E%>ufXEd}m0F|K=?zAc+2Yrqe*h6Cl=1(BRH(I3%fmO^rw^Y_dD-4B zw=dszLey5(3IjMXGca|y24ekH1kvnYx7|@hgC;H7bm-Ef&wwE##!Q$pQ!0pOiCH6N zi@Ek3)Q8=@aVC{L0sMZX6imVvA+ z0OYyy$imR@=c6FhK1bA9;&Vk=)oRLv zz0m5UDur66)EkVgH?iG8$}X?Z?}zGRq}F7%z{jfV;|+~X%`L5M9i3ghef)C(|U&ifXo4tx1|uhc>J0LWkM` zc!epK>J5)#nN>#qe(Kd=iw+QBLO&T;VU3A(HaX;_FMIhbUiBvw>~-EDvHQkvU!6$L zvRc7x+rvPK3=uG#pWGRr39EG+Ne!Vl7{dD!eKEIZTxRB2wi7zzb6>za-R04GoIyOZUf=FXybcR_oeRVKI}chjuG}gVJC@m zn(&Y433xvBLh41m{A-;R_X>?qs04*TVNeUG71Rc52X%lt%`jk#5L*JA$C~BXX-Yps z>F3qbwL5}NUG96}p?Z1F6HgV)-Mq<@p8S-j_DLZa|15=<0b#Pk$|(N`Xuw2wAngC# zg%Eb{oZH0y7kiHyN&kmJFo_T%lwe)+|1Ub0xRO?KZf_ymLcT@B?fLx}{CgBEWuLRcsh;%Cd%PJM8ybGmPp^p404D8x*zHmf{5+hE6Bq`El^4_uZ zY8ezmibd}w^uHeGFLw6l`uz|7)lWga zv>jou`2nc=X!e)?53tvKr^>V>$Zm#%kNt-dl4SY1=qv#O^v|TofzJIg{x|Jg#?{BD z#@*k2!8sVB0rpvzWrY3sr7=ar%u3)T2ifuZAg^u`{$>@<7nkMZi_4r7m(zff+u(V^ zpKCfQnu#Z64y(Zz??^rfeI=jdqv;D$ZOwhR%su!0_&Vrq50e}dWw>3;--2HGx5;&9 zyV-4Gvp0&$K;pk9Ad<)wDvi!yvOowNg(WbOs;wnbnags8QswTuh0E#o3*Y|g z-RAVLc!!x-SUI@3;XJ$ox)2nB#u16-=JuGFvbBvhns4&+KTZ$LifBc=;#QU``;}tl zdF6A3D|aw6gpCslLm>H30GKV885leRQ>CSS0EH!xX-rD)?d%;Koep4(;KccS4~I0T zPKZ{l+p^z#01Jge@j^)^o?^Pi%)Eq=OPOw#xfWP-@wKjdgB#s+T@Va~Bhgp_BdIkG zV(=s?gRd<bF(@Ni445%z%lYW|#O`wA9&ezFP=aRE54b7x6O=?DiHBg4f^=O{2Qnq0 zj4qj6VxCe;oGCM12xmsocO^(7IbC?%v!qwn&O-DM@lCbNl$TmQowYGDrtd^6tgA3+ zRrY41m?fv!UgI0&J}9*Kozbhr??YXc=TfD@bdhge*EkV*jvk&Xh=8$~S?&Dr)b4n?6`?QzZ zTvDsZ`Pb`xx3osTfINMhM|u|p(qG+48G`{vf73d`h!C)KMq0WeyC21aa1Wz;6wTwS z?C3UN9Bz8H;$B!cvn|-d4;R~cVmJ0;KMvwBj&2?&nKIU9w5*w(uXEXo)v8vR%Tc0E zjjGTJwb`UC31;9ji)0Qpk6FMi5^j)gQf|?1Gum|>hE7wLrQ6o)=yUaZ27F&`(7#$? z=!xMNiP0E~@tBCon7TQgPQ%@SxYvzvzx3LT%frBfbUGgTx`9XeOg#2%XWl2;NB7LRzLzJHkeu?&5j6Y)i73V*rWhN{7{QIlS;ft|5j^|l5Y-e9ospzckbZQ@#U3MYjOQOMWQ=ox=W=8kRCGVQCezVx9tL# zHji9!;^xfVg~zpPf7jZxbpUNpXp=@;blPT;t@v%uc2qbVHhb5m&+eny*oY5#9OJwq z-#cI#3R512Q4fuejoWws9^T`7dA~K@UaOYtgOg1C2=q6po=|ePE_jqqt!gR<7dkaa7)i4}t z^k*|$+0G6I8afk?L94xcRUGVAlmCmvp=?LLYcO6OJBFGt>u?V_8w_6O0>K>9_1rq2 z!1DN+-S7~H>Vq|V42cii_O#H!Y+o9-1ic>f;u8ns;6%2azV^nqe)H;&c=2@#8^sC5;-r&PF(>~NRQf4Cy_I=YuUd6j zGt5sLd>X|K`+_KK?8ZfT6QWVwqVC^4}b zCn0u|_XTm8CM#}p@&^+zPh*0%X;0N>-Kp!1rlUXVC(y$F(kwZ^O#fiREZbe%J z4B;IYxvmo!;yW!-bS1F$cdn~nGdQeupFM+hZVawnMIjsJP`1#2ji$}*pgsD&2xWXA zexNY)i~51agfIFB1xc3e?rwctUuxUF0uSKu16{5)%gu+Fnsy>=cX|M!KM$7F-&b5> z>eP!f+acS4kI&T6)zg-qkEXSvFl=~e!=zgih>JiC>Ej7ehL1bwy;iHafhzjUL$QSNI;i1 z$E*3A(ELt4QNRbz6|4e&Z;|#$sy@s(DpPiv#*tToTbA1;HAUKD$Is6##C zXn3^QCG)lVk~1y!`x@5WZg$Xg00|gz|I0tU5$vOY0j6Pg3EZ~42Le;XNq>#*M)}%OW^&jtUYu^2P z)VlZNUbdlaw6wY8&|li(esBEQV0j;!UZl-)eTWWt?$57--rsi-9rdDq)R|nt9iLy& zWj=zDaa~oMwuZp!0m6ppjGb{}&I>u+Q__3Sd*W=N`@g!!$Ny3H_=I7vKJE6(VfV^W zzukj8_YPyVqK7T)5I)W&K!|YSUxAAsPxM?hZx#v(_d6s5 z8QzC1U4}gIhH|f>jSjl#V}K#Xn1pGgVm4y_zQ>~5!YNx_g6t50Se+0aU4pA~Sz#wu z8LwRQ^0RVq#ECkxT=0JUaicvb9(tfW{hWsvw}k<=dk~8&j7@|el`W~{LcG~N-=iYetFpcpJD2ca06SToOtVeEaR(lL~r&PNIpxxVLuzGJdSzNJ z?RYzf!;PSvM5>gRL%f@h`8Ka^V$Q5daJoAo)HvKgDDQ?Rj7nM|3ea?P`-jn8;b z%*0OI#LwhPrLP#qi&1)_hO~O4zPckn2@^dBY%-$JHzTqn%d;|TopllL%L14oOlWU6 z_fRC)cDp{0bYpjSf7giv#~@xQ02?!6V~itJYbm6|t1n}j%6yixlk+t7o{fTs;4GXw z?5JaoJJIBmn!!xxHQxm+Xt6^^%v`wsMvHfNtqX*&D4A2?Q**x%A;L|iztQK1U;L?6 zQx#JOlIdt2x);3^{Q>&&KCe&|x`f)AYN3rH23@8sIB>&= z8!vu>gsG>E4!Y=}j{$}lVKz8CIzD4k_A$#Mo9uGPS8n;6!>_BB(;d&e_Q6-{-0lI7 zd9E=JJa+9x+!a?8Bcl_Oc#2SjV@>=R=W&s+Tr+ZHA*-nz=0ip@mgk#v&0+Je-Pk_T zExGa@>hGv!)b;t;zH7Ut#|LHx7Dl4`_woOqBLsK>WU#?cac%p!-i>TxGh5oq_KG|S zAYHbGW6~r{%4AK>)xZh{HG>4zqDP7hzUDu7>+!Ot+p+APFSi%i1y>RsE)Aw}s z$KKmjY;(DNf~jdJ!f$D!xwg7WCyRWFs8RhI7X^E3a6>pLV|vV3azuk=FoqH0w9`p9 zz4SB4FyjCa2p9y!Yvd9w4EJs41C;R>4Jsv#x2;;9p>kDq)jE-cBoOQ7n&IqgjPvI7 zXO`3dL#*u+`MZA#?=07>BRXuSA5(Vnolj1?Xlm6pJF=#jk4o8_@r*4P8iCl4pYK)o z(60>NMo-up-gkeg8$X0v@8(}8;Qvw`-hw<#m5Qc3)zdu_)#yb@8uSLYHtj*rcv^%FMvn_qju1lB$j<<<3j)9s0~kMHEKbJtbaD#7PN(;v>zU5_l4&1Y_1@?Z zvq7;L+B?HzF*rWs5-=f6^+_p6MO9iFl2(|t@hXfq+GL|lG|74sthUS_gn82}2QBt% zRrZ?Ux)uH=!AJe11OpkNfwbTtGYpUu4#kmB)*DKC6)BZbGM-e8F)@;OR&fZool(1) zwv$OGIdzd+=ec&4OFygbM^%0Ab0jq&tOg90>1Flst3p&Q@;cDhi$Sg?@ic?Gsrm9} zzA-bPhgLR&*@$=$MpUMhboxqJrr*l4%fe1ehW$TE@;|H%Ljnaie*AUybQ?ey9(cgX*A#8*w=$lN}Z)qBCY2V)iqmIVV=2o;tG4VDibR-iKi%_7z^YTa|NT&1zT@PVmhn@%o6-G0G^n5@hV zHw`rvQ9!0sRJt{nVtATFs!d8N(omP5;}&{lBK-MkEXnpV>mVB_up9(ccbR*?T8OQY zCR*!z6RS@ZUvu#_PexH+;$|_GI^&@$URfI+I_zy>ZmpR?WS?ckM;R#Q9!AtE>LnPu`AExlR zEC!iL1t8ERGF>v2Q|{Fa@4tIEFBY!XE~t#Bg~d<}86c$&0!Fg)=`-m@3W7*L!=Vk} zV4+G634~}%xoo+_)EHnQRyXPQUm5p*=5PIgz$oj{cdd%B&+vQwQt!9>bARvO-4`De z9vl`C8re62^;z9cBWMiT-kgkXq;2aa+V*ay?dTTT&Tggc>Mq)Mf3s(_@4JT{OOp@Oie8ns4f4-qjM02?nm2Nx$d55EAPYIW+}6pBR>sX{5&nJto) zM^39Ty+Ihwa71jPlK3(gUP4~MenY%Q{fJ zI`7UK2hW5!7XMnzHFAr)r+B1#rg^1znrD87cg*L|_sGw&-xGhQo#$J21~>;^fUh9e zP=6)hNkj^nMrF_eCi_8b4wtV*9f&p+2t{H@Dv`^S3bjhB(d&!`v&m|)+nf%!%j@y` zf=DM+w~C!3H;&#qcIWsLzdzORO!XW7FTM5? z)*rF_knP9q>rdfp5;gQ3(drqUUJ06%kjaUd9>4LK7-+JMK6l!to`LjGL0vup9UZarF%H^dqdy@A6JaRPh7)TbVV}W@JcpIQhLypo9Bvixs`R1{m>R%X zKbD4YG}dA7k*a3Z%`4i!k^@H_ee7F*YP~O9`1t#0|J^&=yd4{J&Lx*#*7Wd4J?8$~ z-kY7_w5Mx=L`FYw|6hG$%~De?c)s(W^PwO2gpcVv^#?xcQ|{q8uI}nD-YW52x8QFR zg-j&bDEdWxGZ+4BpterSo*c^vo3tWLMaTm72pV%7jvCRlHqd@RrJMt2?C-krbQHLFgF6=}M zVQ1n9yNvkHJ&-imjWogT6KR7zU{BHodmUr~_NG|EK8Gc@_Jv{v`yD=s)&)>9;X;ZU zTtq2^iz$_G38e@wrBuOX)bemSwJKaey#-fNE5lV(6maz@;ZY5gb-0$|57$wv!1dI7 za09hE+&EE=;U>75A_lh{)T?kSWdm-bR)X6p@^AxCJiw~(AdLVXVo3lG(-IzG1$dOT;W1jl<8*^3mA4F954ocBy{i-;fJ3I1N=f*;8&sqzY#9@oiYi35C!;?6vJO6 z9sVZS@DIs_e@PGgN6JP=br_`&fv_e*ut%8C1Q4m!=NrOWh|&(T4xtO8A_&obrMrJ( z{RoC420{;f*5R=Vp({Q|Fk%S<5F2s$LKuWNNI+a+4&tF6@r5x+fC3~GrXvxmkXV?D zFVTP`!dN8j!YU47b9{|)d^2oX(Z!6~jm3m5ksOmqA#8z^m_RCF2c+)xst;j1r0vz* zg>X31;|CdpgOL$8$RzB6%s4<6;SglSS7a0R!*|$2c40T<=<6O5!jZ^@-{gMDvB+ab z7(zZC$p@T>`~(IC;8YYOXix}FMqvVnB5)dt5)3E?XQDX4K?%43B?$>i!Nn*|-k=Oz zi0{cOlqIAn2bZHfp+p6E5*3LQD#7)rOqft*<*^R7SR-!cV(>~%LuM=u;9b-tMyLhv zqc$-`9e59Q*O)lGfX`5$IH3W2g@(ipjo?c(Ca!3*@`d3G;(})Ib2KNxXaPS(OAbB> ztcxIijMkC>v;hg)N*d7a5c*~}HFm{!Ff|gAOmxCabe7bk3-HnPmsF{p0z~Mxsr7f4 z06BU{I?xmJ=q2ez?}q_>z=gh&N%R8``b%ao0Nfb3nc}d0nSFX9I~~H(fypLx!mty7 z5&y)9O`M&4jCc;Cgq|3kV;uw04`Y|1+%{o2#-B_ADVRvi$0Xu=Ouo#!V#|hh12QUQ zm_}@c>B1(Madv*{<19E7v-u3y9Pl4tE(ICpQIKH%a<1yI zSV}yOWxq$l4nr?^1Iv+UV+Gz})~hog1y*zP{oQ}#NV-75{o@& z`nr7A`r zbhsrc!0pqS9TQf>-7!=9Cai+{W2rybrOkT<-0?`*29L3fC&FfUiZMJBj=^(0@Pc>- zFDbHkwLGhP*hWszv1MHyCZ2BmhF|>tJAG_$_KFes6J+7-W#j8H!}^Q~yWk&C<6q)3 zyrWRyKcP9^AE)=SrL`^;Ke*X2?ecYlSAhLvh0uIBq_*)Z<2R*Xe!W&Fxr{*y)j5bmy+7+!XxI4V1B znQ%8@1rK2p?ji5MOV~g6$l>4&&oLT(Z7t5*pN$iCr(JLFF)^0#wZV!UL<=JDIp=Rd z$S5L2oJ53$-x87Wv7c99k%$djcjFl6t2Hhi<7Ok&w}X5`q=|(@hWt$44@cP&<3CSA zc$>(#{>mPgkFyzVzwU>~PL$8+T5M6P_bZU?R>C6Bmexs|iP9w|qwTGf;teB#gwfwyxO= z-V<+YMN)7ezP1~`U`_n7B>@|)NFWIs)tq1wVjZlxq_lUwGAY|hc&$=m1Svm}$Xf4m z6eN>q5=jghB$hf7M>C11l_dDfcLgiPNzzs2{&?m|3Z_Xa*+tUG9+G~Yd4iF1B$J#a zS!5*1u9c{BsCB$^slGaSI!&?*Ug94q1b`G3r zk;*5ER85cy>gxV^Q*%BqNbSKLxos1nAF2OBAJRY?M_5gaiheGEijK5UF_1PYX7Z7W zjhxQHCw}bvJS@yH3Ik@=iRpE{KjwM5uvy%_44XM6hGFv>3s~P6RyKj9O=5OaSl$fg zH#A-{J! zg71#uz7u%v6y7_7*UsUw8#o7$7lhzoNC*k-A)_KV!a_wxXh;to1!17RXDAL6u|3D9 zu#p-LBEm&-cu0&8xe*~KVwCn0wUMACQWW(H4ZX(4-XOcTsEG_!k)tq5WJZOws8JUU zlA=Xibf}IVpJPB&j7W(IF)<@PR)oifw%(yUc9g}5j=0boH(KIDWBiCt0L=-aDIqlX z?+e6v*Z?Ws3&Mb#1ygP$T-X!1QU(z0KW-Cc!X5Ac*B|gK0(`u(pDte}{$2P10ibLI z7AFlsYzyH$93p~;M?|t;5H)d95sg^(8{%tXcaVrA_79RPd-S6zC}G2X z8bN?{%EJc10GpJLEkXipQvr4e8L&%**dr9c{wEdT8~pqi!0Hyl0Q{nA{O0-rf9M(D zuSWe+HMoO+2orD*52zN8@Ly~;SO5ls2w;T`kPrYeCO~mpO-HS)RU>NpKngvycfbgJ zFcJ0Kphh%R);z4R@`NA2fj)483nSnLA11)xp2PdtHcDUtlJ7jR1?khf&lm5E=Vr3l zgB;{>1O+&R;>|Ch1Z8}M3RE#RPVx6K2t&~DIpVYNOz7Zp{1bXO8Hth`7iNF$C}#3wyS2$Df!(u<^^_ee$%DGVbO zPo(i5>9?1|3mFnHneaxI1R)zf$dO>kg)j0XB;>;n1rjm};g2E-1;q$Ji3CR}0#Wvc zQ1Jofq=nsTkQ5a{E!A6bujKV*5r(Odi{9qvClWANw0MCu0iOZW|5r1BE!=Xqg-+ z!s$lG@R(w@+eWV<;*`8hV!#zjalJ8eKBi2in8bu{=L5De^E{@U;kL1GJEnrwwz2X# zrjp6Fv2i%2ioLeI<84eeV{K#SYD^7FZR6l)OfA!l;v_KY>?+#O*LoF|>yz|#F2(p0 zi;BM9pQyZ_4cq|K7=PwW(Kp%>1_AV*E|Ui@rTOG5+>d^c}g5@poA+`p*1DnR8B|@5*V6f5>LhcV{rlqH_j)PrhRO zQ>Kc(H%Br4IXgw)mzNm7myx1JE@FH<3q>D}e~hn-e9@1@JjT~VyXc4F9OJ7aT=avn zjq$ZnE$}cn?RVR0v;CXCnK3x@p7e4IDfF@oy|j-Rh8S&O7-qDFVT93khEYb77{(Z_ zWEf{O5nyOB01`0_kkJwV6k<4FpydG1i1~ntRscLF762An1%OQ~0bH~c0FRghZ_sXd zOI!jfv>&U{pQpdUb= z>}fHd+x6rw+%7|jJJA-X^*S^!W+bcYXU3X~JAp#n_< zs3h7#6`BrEO|*j=Gy|ZPXb*L0D%2BgpaC^#B$_}I8bLGB3|df!R-!qyp^4B=w15sY z0X`B<;S<^npNXCD1#N{+VmEZ5ZP1-@u6jVLp_kYQeP}K86PsZGt%E^g3k;$4FidQP z5wr$IiA^wu4#GI`HB6xWFiCs~Q|JavlOEp-pmVOA<1 zX2U$xr_I)lp4f}}7JFkK>TmYNezYL=#{snDIGCftU%^~6owtWMw-sMENWI)&S)Gq~frj^krp#m}C6R;ls%7o934j>vjt zbwf{zduZ8kFFgtFqi4eX^z?XudW8q6mw1SJjYp^#cywodg2(VUl?PAYDXJKrE_!l2 zLr;lkCwgi;hZm_Ic%`WC@#?XD;a8okQ?>ECoo`Y#@pjWw;2nA@yt}if!F%|S77HKY zW9nZ%DOwnx?rib!89t}l;0t_7wZd05QBCnRzM-eZUyGgr|DdJDKWVA(ZMJu*-_TOx zKRa7i{Fm=kWBiXFR5$!s^o;na>B*>XcJ!VAq^DrWN=(R4Aq{ycv}XZwQW*FW7kr2Z z-V_dA#BWe0;d3#>C222%m?UqAPKsWMI-~*x(t!$@8V-`9cS2dZ)JPYF9&$y_`_^8EHsK#_Kn#DD0u%dXKJik19J_tnE-~({5HOPmpMGmYE zxv+JOJlOhv0}5apkssTHoY-dM#fZ0&PSE*U-;O5 zGPNfye+68VZzlMEETFxM`wVb;x&!v|#UH`7zptprKI0jbDai^x-6%n1`Az3L`W+%b zVjwmoLs=d=1Y!ZKezvHgnB-0Gbm!#ru-B@j+B`@68Pk+#n>jZ-!GVMr0VQD=rRP5p z`uv>q`X=yvhyE=csVxb}nqNf*QtjJbQI`o3Voq#9t6k-gHmG zE_t{=+;|&GD~q;VMOLqkEa=0H%n76|nu^{Q(|m^&yiom6#@qx2%u zB_oX{kq`H=Sdy}-HXv4m4AwV6^sd z)SCdcZ%}yzE0`0}L5CEq5X}9bE~O?*_(_WW{505XjX*~q1Bd=vX=ytQSUxRCKp@Nw zNX9K`*xoOkfPzGT1W1xQI|Z4z6lT^8_}3>d&NakD786-F8R4m+EUb*z zN=FDXYNfJi298irsYo#SY%Dm))kmgp;6gY=-NAxj+e4*6Mi)dH|;p@kpV*+KmxMEoPPR%i-`=~ zwgNlqHDscmR-xuz2^QaW7n5HzNqzfOzSes%{d#deI(W#yz3vD{#$NG51mT2Hh@uil zBZ*ELK^Ef@1yx{Q4g>Z(qWv8$#{^ZbpP(X=r%)ih5>~-#SOaTe9UKYkMH~Vv2415k z%%E81#BQ#>weh|yB+U6F)?Q)aK&%rR9Y#oFc|l^$Z7X5M4}vhtcbHs5#U;j=sUdtA zN)WQR8ht=9&?hxRH+1cT1rx+gLd(!4BP2fXe|O5)A{HLl?Q9No&0&RVjdgux0?Ey7 zhYi)~+F_>?L+;ZCh(|`Uyr@cJppNQAPuu^QTV$h9(!-2UefO;pg`c?QNC3cxV$Wzl zB@M41wx6j~w_;2jeSB4yazRngAQVG#y}WNde9?J&Dy|^Ov_$Vs5LByX?4^wo&NM1c z0`o#oftU^qoXtU>SZsc(A7;YEBV;e=3=Jk#Y(tn4vGgj2YE7+| z0tiEl!A$|sSdO)}!b$VgqQhx^zZ`;^pjlSmcDTAmA=H!mM}Q>olQXx=?rXia*fx?P7cO*v_2NnWr(p0NT5X$TO zcS~_e(fb$gh z5|P^uc^E(zyy}}-WY~t&8_1(?U@ZTm`l$Z($y_QB(Ek5-h(;ejJm}+ZUrpfMF9LXzXJ8-v8y6c@!%Oq0Ko0AicSo>Ss*j=YH&ndk%AiQo^(aOn>-H&a zT|5L~3fDI^rQF&{osRMY<@_^TW`gkwO%&~BAdo^!#%tJ54c{U}v8fxA$B}d#ZQQ2x zrblyxLjdKVB&Z>K{$XaLMu_RE((-y`V?I<DQ#}`c!0r)z;^3M+XArkQJTpzi52K&Yj$8HdA1SSHB47(|!=n!g?-C*WfLRwcOfT`U6P0s2KY!F&5n8yZm^>Op$j zSLk3L@4}qDXhU(w{b=jxiBBiKOI^Jj{R{wb$)u&o&3gk)H+87Q$@SHQ;yJ87iL2Wl zO|s!uzHtpyHTRTZV8;V5!_+~0qKaK0IM7L~zmriMLYn|Z6`!DH<~~5qS7=3wfEj`a zh4WDlqCHLj!A30*!)8QGYYS!qB&KN@@pDfKaTaO8hDkD@8kx*-o);w>P*+rli4YFO zM=ib6%K}%kH>mVfx$%F0xA1cpns8aXm?~tcPyseSTZ&)Y5%s_%$)Qu z21p^0Djmr#G4M%~^GhN2Ga)ta-Y_2j?k!s5^XB75Ea}Z8fj3)*+=2Xx{vV^0)ay!c z1zDOjz<~G~b}N@y^+t4#@U#srWx8k5A> zE~?2MY5)zB*UK#?UEYlBZkvt*@JV4tcNj^{qwfY4vkH@-Zh$9*Exk6-3RXPoa8kKZ ztym7Mf@n%)H(jzUaaT-~VmZtVX(1j97qM-osfqaLJljfPjNdQ>Y@|=X!BF7<=kr0q z0nS1o8q%DTB<+`p#L(mQyc%b4eo`d^ zF*0IgUu+SX*YcstH!f^8TLG})?O$D4L85<_BJk9%fc&3mQBpY z%eX`%y_|Nmbcc5Y)v0iR^C({MXxg^_FWP0Z8(mhVU^<)Sy!=%pn5S$p4{_Pga!bEl z;O=Hb`1OYW{rS^$IP8DSYcc!jWv;8od-*>6yrpb`D5=3@ z(0r_rliF*Qf#3hslsDw^Ih~y8bsEDESKdDl_ee3=+$QaD*5}~X+0G=WMpP-o>%Km= zC>+sgl`@&-t=-YLYGeXbb)|Ipxd`vj8X%Zs80q*@>QY(3hW=sI@N-n_NU)2aKSCU2#H8*zVgwrLbPFW;Y-fS^&BxvTlfQ zW09>=XASM(P?Ohmc?gHj3u)&-sz~IC&>?{iL0eqrv1f;SiwB+BzX4IS)D|?>{(AqD zrz!e_-sz287)u63ME>mU=g&tB5q?WL&jbYUtTSG9$ky}hKW1e?}KFNQwrE)`TX8!&*O)x+$gd> z!6{C%L4i>aB4lhUHEvpJ+G<14HDgKK$9Cgmn>miJ5cQk+CNwy{1c@nGzdpUhv8HR& z$7e)$rqazDkM%mmmO3rJvJ--{J3$2}5(22?`p_1+{h-EGyHco%HBX)h`jQ)i(+*L* z%1^fpqB2uYBJG&@l0uymZ}S^8Ub2YvEMgP9dK-jV5n}|g2@2k;@!?8#s%MULD^O0w z5>SA4S|tDlOhB0i6V91hv18xxpbD2nkrcz@$&C;B{zkOfgD;n_pXUFcAl9_wX)2{Z zpw-u375m$pUx7=kLbSGv;^setw|6OdJgS6WhCK48UnR&E9!}j^4D03*Rf)Q=t*W}w zv;Yx@z!vGq0$zVL3zzOwk#;QK3sfe5XVgaNz80E!dhL^F8T`kAyIdwF;3mwpL@PYt zhpy1Th+2NANjz^VjTkzdr(oU-YM)m7wnmU8tlEhg+G-u>yHU^@Ksb-EO z#NZ7U+nFH*j#C5UoW@oZXGs>zs~y_FmLZ{Fl#LyFvZ@r7ARvdKEndiT>1!%h8ZCt- z0&3h6rYaCB($gsqiLkLeB(fqU3MiprvzQ6OA496pQAp8|S&gD&@OhjVh&qg+Uh0Fa ztZSPh~f{YoZ98Mx#DIp5Htkxb$3ew+V-Ae0%870O5uzA=bE6@zEqsCO1Tp9j~4fL!9r$fA{{= z8a4x{^eYhoF@aaL5sD7BgMeD~%s?bU$PgApG$CEO!Dc~(*_=^4gKb^R!Kl8Q z+(v_lClEt%w^+q-yv7Ca=mYL;kl4ZV9Gf-2L7^zGu?6dZM&#?ygmgWtzVt)i#sFy9 zJODChHVYwjFf>bf;09PKax3jvUaYs04Mx`lW@PbN3V`V2Wm()h!gFZ1K`@>KAaz;a zKW4DNq|Zy;XH6w^#KbEifEvd!JEdpSgz``sslG7}1VgZgzzJ=+cPQ-p$&lSUdjaru zB?^RLC;d2qnAEgq4(;~^V8HRG*dJIK>j7+vOFV=t@)UkHug0{&^z!1=F4rIoC%P$x zQQ8~QTNMFQNe_wzBB_Eu0nUj5DrcDPdjtu277q!Vom`__)NZaJgSGWxuyX8dpHpNi z)f~S&=2fm5E~olFiV??AeH$}yk6=DFZ0`-=0^q)q%ap~M1xX{ob@Oba$pwPlX3cZN50748eck=^0 z52XbUE6 z5uF5t7wPqbhu37Av(x4edqDx+!8O8%2Qlw(7KU$1z4*j&$n)U=wZ9a9_3Z#TVUUU97f()90 zXwhJojDq3tjyR94r&fAC4Dvpvb>GYdZsKKAEZ4=nsKbio?qx=4zW&@Sca{`)C~TpI z?51EYYa6a;#M5w7!F3VuSstuTO$#-QlISSRF~?ZSzt*OC=% z5^7~Yq|Yp8Z@UybM|}HpqInn6D>%u=yvAHGaN*~{vsoVdp~xLcuepCAo_SuH6u~iw zX+DeF#V4$`k#GriY)Yk!Gl@MdU(W#&;eMP$m=SOqES0M|su1eTZh_Q8ET^fcnWlMJ z=3uh-7E-CKxWzKCE9Vfwqhrl6^MQ$<%GcpQ}n2ZWT4 z$%^FOGLCeb#0{7*Q9T)mHIIs^Mr;ExOPjczlQq8}g51-$`#15*tn#o8?CAqav9^;x zTv)3Vq?tbSn-b%i&DSo=x^a$sbnVT8;Y!z62dS4h4f^^zmcrH)11OQL|GMGlm_o_A zgtEbl0;DM*#t$zxD2IDZd>`)EqhDz2$2mVM zrn!1+#by>1KxYQNmtY zyidkqp3MThkfv&iYlDNFdA9<^hSo2vA*s-&DhY^TVGvX+PN|~xsD8|9?MG_ zFELo$*t!XrHUype<#W$+edB#KWy1bEHrny38s+^gD9%F4Q1oC-p6Hc9r#emCgyJ71fK?E}{@!S6;C1O(uW0)sFP-)rTgSW`OfU8=44e{O z-z)*^;m;p*VtiV_>rt7p6TQZ4n?wsh)(q>BofNI--IC>^1x(TA=Rco}{uR7c?WI@V z<3?^T8%?5mh9u}@hB*Hq2wfOVIVFfv95<a^_t0v<}RxkCc9`ncg>?CUvbQpZJC#5q$jRTi@jGBFE zt(gOlEHKn>1L}$cm-g*-jOW8^Pj7OcG-3(kJcm@}2(iIVBsaa?ggg-l`e}7tdk=YZ zj4LDhr`t_|R{{~21McZ)SmY*(YZ?LKnuL!Yga(c}igWW`CP|R|hLdJb)HSHoE`Xu` z>jDERD$@Zy%B^xUVKKq@Tv<2;4Ekn6%$|DtF=x82o7osiz%~WoWHo zoG<6W9|?-SrT8F|z8XP9N8CDt+j~o!r&CKOFmqa&x$$nE^@T(lCtuhIg9I*J>N-e*B$HS;RaIA# zq+7bMrb5K_Y*(#zi7T$O|M9Pm*P31J)C@-e!@<^Y9-6ANKx=u<<9smfDb!}6qFhpTSFBBlV zssL;z4gsX2Ftnjy%|tzC9e8)m2YFVp(QE0{x9O=i{*2u8a^s$-JG*laW}jp)=E5iTu@_(IZFIaOV#r@1wXyWFz_szNV<)2Y8mzpUBuqVyP+N5Oy)Go~2RE zz&73*Z1fv@`f!RU!aAiZs{t|wHyut{2Y|rQ0&C>7d(sbw=d?@%CmQ?3N%|IV6BI%v zvH)9S)HovetpWTOyVg~7|oH0+RVVyvwaE`_~Z zQvkpmPL=p`1d46sgTQrb=3Wpp+9q3zs(vY_Uos&3xrw60O4t}ObR+7uOtJ5eg zrRk|pR9vs}pG>MnYecbX^`U}_={glqt=hXCRJIQG7YXeaBCKswghij?mO7p1EX~yY z)vqtW1wFEsUK{QQq890aW39b$&5>|U_owaLE}U2^?s@y1g!{36C18?8m3Z!{Upjh(d| zRs6DCl!}v@uMq&M9!RM7wnLP|*}P_)OMsvCA=@)XiStIuxGD@tVqDcG6SKqQp)#%rXv$k#}bIv6?vNZF;utcwH?ad89BV@`kW5FikxWVmi?8mxT- zAf^MSw{Zr72XrF9KauQXeC*838@&ssAk{GVXT&G0<^LLlu~Xkhgd`NDN&`$B0n^vs zIoE5QLa+p?&@irXJ*yEt$iJSii+=>7it9NiZ)rtuN<#_~j)4^orsyRjvXVmx_5@>l zDss^OoqVHaX3*4wJXzRzRtJn>G9!(qm0#~9@j{(P1H>z7 zS|jGB%+l(dLZ9O3EL+ND4ZsH5CKVgmzVPrV*?Yi`y|#)-)}xYcUqB#u*vxtOEGUey z&oGun{g?{1ANMaP2^riA!63l^kTa@&g|pm*iuQvE1UNY0Y6-Y>Gt@(j_+hbCi_tnT zX|5;FK5(-SF{Go{Lp*n18QKttTJT|zq0YSeCWH#Tz7^ooYJyp&G)Q#b_0oHM=$FbPpgl_W6Wlw#ZSwW zE0KW=Y`tp5U+4tRLQ(o_+y2~Q4Pz4v z;~}FS{k4$be;LVnfJ_HpvG^FX6Awa^JqPoc4}(NU%MA4gXWq@GPUv|w@(clJqJa%g zwOAbm?S$B!bmLvml1TH9{c~lS5j{l@M`P?&CX_jX{huNz*xRv>D#1f?*o=l-9sL5T zVi-qoOUA8+${1$5Z9u;!RLAI`Kgq)$^lc+KIF*i?Ynwp7vwbz*Sr>=yy5MJu#dEv( zF78p7YQnkJiYSCXcZt84vN5vA9d4nKEYOy3I{!K>I-nWDM(xWb_X&b z!YoS;UIXmD8EwB6Ob{*}kmRd_Cy0RLb(y-DFW=9`U!ZR2AY>a|M_YSTwMSKZRJG`^W6k^ttzJ4y@k5Iwqm~l=krA$A+%~&el4i`4YN~7} zD|UQv!Q`PkbimTKA~ej+&;DxXqbWdA*QdW=gHlO$+@vJy#!nUCZj~?lV4cT9U__L*pf0;ZB1aTc4f1p5oj-sWmLAo}g0Uipr>Q zqB0NvpK6a*T}R}m`h@u2!gahEBTPS=&dfFl3Dl>VOmIHEeIVXiJuH?t@E|nmUgk{vn-M^Zuu;a|59_!JPxR`$ulK;B^{@{an$Y7yS4~bLS(p~(x0x&KLcE+V`f0g9{3;TC#oy_7o&8Zf-=;G{U+q${&?&s>t?y- zz^A2)ZHT|0hcUymTex6C=tGz&8Wvx(YUr=0tKCk+LsyY}-hMQ`SWuIq(~{e&1}V`s zVR?n}= zW&t=*a=M7Jk-`jb!=n=(r#_?=_@}(Mcj~X{ zAQS+@-z;eylTxn3j)b>7+ zg5@Yaogge@(GQZ!Dz)bLe_~w~psk|Zgeh7wBh`j+^_;?3dMlPPss9SGFS;`u>aM3-Akv4^XZ3;-LN}eS081 zu}emubEE7Z#al@SXz>#iaKZ#ApFz7xdbrd|{SZn^aAg$ECrR?{A3~ZDBim+r%^BEA zh?O@?z?DpW-R-<$jAtzdwRn7WRA)S_(UU1_t)F>b-t9=>=VMn2Z57==0+t=ZMra_R z>Co>Z&QGvd|Ch?vIot`Rq>f5N%y=^v19Xt1u&V%hoZRNB+$~j zWCILKkS@s-yhtlFHF!5L_PztnU-|5a%NG#2=G9!T2mJySb{{`221R41*yrC9r647z zRw!MnL@4;u3Rip{T%&;mh)a>HJyrp6eU!1|$wG_FH~%vGk{Y}>hIiRE5=ut$E!Cg} zhtw))v=vnw-heZkwOq;Q zN2$=*Vl8utL=yK3h837|pOSb0mN(H>7}4S%up+^>H^=t9tCZUnn(1(sn~q$RWH%&( z-t$19*Ps3ucT*FJBJCa4D}l>jCdTxovpRM13^E zM7j*PHDz8IW{$5>y6=V54aMnMta+vF-VzmRC3XO)s699|Vfz zLy`W02aEu7Qk-^gzZ$kLcdO_zX75v2`HsUCnKG@_AKT%muYhv0?_`KGmZdIL?V6xsyEtW4nSXhWMvD zkJfB5pxx0%CK0q#!fG7Ts$rs8c5{6(Vb%WIdtP0KJFyP?xW_MihhTe}1tuQnAr67j z>vR{#>ndAL+!(~flPWLHRPBqWghefWl1mLXw||o|aiCG@gCQx%`KRyr&vnP29VFaf z63jW~vl`haQ5c^m#NJ#l#dau!JPc>@;Kc8OY|rZV>8#}$(e)RR|ED12`{q-K zyT?(&58%TWSG^c-f7)=ckn1Osl!P^7Z;K2^9^PKxd65&}sF2edQKG(ZBTS_EqQP?b zi!Hs#!<9_%3*iK4v4jJ_Q#W{1sw$~<4xBCm`kuv@j>oiS z#L5p*LZq>o@wd3z6A)nR znlI{yMzA76T;KOSOd zRZl*vPnWEK1~)kNcBwxfeiVg4d*QCagKdU18t6|?KQHp?PyE<6`2*9OZzup>`Q%R^ zO>C$({7IdzH)pT-n#)`z+<<^fB2WLGSA~&b^k*|pvv&XowKi(#{dF?MOgp%39Zk>| zZ!lf+hf^x0z1hZ+yN&m^Skfd~S2UoWK>Dchv9eNlJH zicyHlLq_zou0nR~V|7IH8d57hmu~y6Rq^AOQh26NP%<22*w78H^$55xwOf{EZAelE z{zg=*(2w%>B@rh$i_be2?mPB@@s;%A>Nw(&B_tpF&BeCz#Wf^%ePTl&4}>3Zq3Kr)p&<*+kZUojqdelmv#>|w!a?}zeQ!pdcTi6;##1!MK9QCxY?o&l0mP#yu9NcvV>r-#TWvi^c70bP8^Lfk~;!$z6gAy zC1Y*Z<|GkI87y@-^=EnH(a({XE<1N1}eys>i-*c>u| zK|qkVf@z0}8Gh*MxID0RlT&vmgEF+X`U4IAlyL>rg3_v1wivaclQL6?g4~}pNoSn! zvw)8`%no?yfYUo5_?h^c_aoE=0GA8UfO4r8duT^G$hca{Kp5AF;q~i)9``U-v2FcX zc_8+JG11yqh$xz;i)uF+c~MmTLrn_TqGf#NeUeTQ3k{>;OlPsx5r(w;B#?2(76Fyk#fv4;mmKDI)C-vr;RaQqd8dyK_zsT zfbJ<#FW1p#-0jLzyZe5T7pv9pJ1O!*u&zs|!DnLX+ zyY&K^@*GcsfHr)YZ@AAC(d(CpjQ<2v&1NTaMe9jng+jjLo2#3kzTSF z_bGpq50?lT&i9FGUV!s)5h^54&G)uf(CLi( zrD6dJD>#IMh6|L#z%0EtI!#{Bg|`}OqelL~z`^aCk9Un`%)wtvQN-wpr&%G!1LQ4M&IZ8)}bGak+bp2T% z8PDoc1K@WuY!6n>?z!=%JMsdmiJap_?IbH%x~TH!y_$QQW=!poi*PQt-|Y;Xpa!dqWlC(T!B<&0?ffT9Asnnq!5rEmC?; zM7U;q_yd*5xo$rcX^0rG2u^GXt z_J@Ov7xJ(x5Cyi)&UL+91EO?$kASw};wwrN~hy4`-mREeJQyH0nyQ`8kWW0i)_B?O(f3w1@>PD8i9TfX$YJ2k{OCwG&_n|kio2P8O!NSPJ!@e zHTfE-j&P7MFYZlDhz(V-R4+N>)d`p(N{w0t!PQ6 zuyAbhRDrujI@*9WAej>)LlwPMVaKsIp3pK1eu3kNLUA<>(=rCR_S~~`8g?~R<*9)~ z>K9|db0{4R#m}Kkj%#vB_KM24*V?WSv>e4|f6^_u*0w+AnYsZUsrnRppB#`#kf$Qb z*Jb+eIzP9T>;WhV6DwxDuEn&lWB%IZCez-$zB~6z&vrjBf)oi~lJj0rAt>4VcG~sJ zx5V;lTBiH3KKM}^PRzVqFvLy*y%1%e3N!)$owVj;>j_kjvzDiFo%t)s*tFJwBKp1a z;vX9iC782Eub8r9H6n$SSwn7o98jkwafFrLsRO2o#X7&9p2Qu|;#O5}UlExW)nKr( z3S5u*0u*>`TqHHhVhodqdY8vJ)ACa}=*WPv^~HRFw|{(Twj;hwVQekNhP1c-Y}_!E zb)}BJ9ul*hGQg}gk`psyE%Sr~8D)HANm7#VmEJ{GaB^O;>b?Eaz;^3Q_TDJ&akMg! zh>w@iiE6w)E?G5Y@Z(=mp-NhFe{>_78O?5UJsops4c&88F!c0C{pFyoIQC{>YKiUAeU|MFt`Z*ZsLan1GFg` zxeH|c4e{v|W@MqT;8GsPi|s1Xy^bvvWIgj;!3Qw3J#XqnkmqOa!--qELL(X;Il0$c zYwT6csZGM&iM-*hH?oVNnFnHpTb@qF!&y_Ga|GRSW+rnhAzW=Lx856h(lCO60u2z0 zUu}0X7`Ln9*#HCceiZ(zcwX}!(MEr*<94)hJGCq>{90<<5Qp{a3%t&^45wF`6g;Gy znDVc;2qaf2KRZ5J!)mQ;PSZQZh{mc(;)CMwRJcUsTS~5JXG-4)BT(S#jL15id<<5l zDt`4{dcN4d?w`JIg}jZ$3fMMKkj;!uLcnBlL9SC9(0jQ@8~FWzE~|}l61d6h>71o3 zI`?N{?~s^_8F{%IQtOQ9P!yoprO*NK23_u5-=OS`VxE54HCW1*Bnm!oBcY30+pL=;Ah?WWZmR#T%-o51@=C>8(beP9TDMzc3oh8A$VRbHLMb zEfd`Vv82%Q<8CC8;TH)R8KSOay>x=g-pTc;4+@H>s(A`^NItohtmm|J69T2Hauvqk zq_@@E^=KI}?~#h9qXZM=OQ2_Xbb->66fKbZgE0%fJ$}BcYMk5;w7+w0c)xPgPX%)o zkuGBUs_Z(YWFG2wE-V#`GJNI)_ET0a@G?zx?n0z*VijjmXdcU;wiSvYZ@N#l?LQYf zRIGwqIDR`$izF?SQv-^vj1%eO^RPK-yZ5y-?COxkdf*^)$uKHFH*Hv#r(h4Y`cRV zG3{vInSj{-p;d~W?0#^!BDmeZC509ES^9S>-%b(uwLtM#4N@*#17QRS&DdJjkRXs~ zwX3|r~r#a?kv2bJ`hK6mKph^=MgZC=ycK zzx$!&lqS1W{I}huYbXU+SxN6Y&MqY+WrC8TjE|M9OV|2cfFZ+~zdwT+$wuP7bgla{_;>!p=X@TV z5*YLU-y&?ZgyYbZjbJjlB$Pa`)8_jrNo6X@NmMD0p?7ht^M7U;0K+gIW`4^=Ql*pR zmRCvZ$&kr|$(whk7K{;$7);+&n)vdjg{DaK-7AXk>b<-4houq<44^oo3dc&x+;x>x zScjEY!1`0R2sye8oE#KA+(aXj+xX|d(Tem$x-3bZ4^xmczgOUAnlY%wC?|5OVXH1s zGVvL}v!4+pEIm=H%|ymDZsU1fdt}PBQY8nQ zt61dkk!A=I+PoQNa`T0pn9pN2`D!KUqli1#*L!XXfWFmI%ubZPV7w#7*S>V4?r({o zEthe$$CJ#s^^NRCycIkX$vrXiW9tcwY8NM|749@8r&cIeA8woUUfQ@DjX%^{?_HPe zmi-{hpd~a4`^@LJ)8?~+a5ThhzF0Pb>GF`0wx4Y($dVN+QY6VZ57xFH40(iy-&hig z$oAQ@UTtRTDOL0TBgyhte75YRi1LrWl72Z={Mx_P&cpvZ;RWgc9M%_`eUs&hpGUAl zl?EOhXd3q2Z`@@;rIEWMv>GIuC#8{9fged!6$2qEl$YYuV+d(k%rd+W2ingX+Md-? z?-$0d8ESS$-w0uZ9K>_Nyk`cH^1Jqnn$Axk%q)Q7(rI(|NrdW|ECxkE@pvE>A+ZgHL|$r zy{67HQ<{RK%ZTTq_+J+!)p~E-zNTCD$ZhwZ33KZDguMvLem8P%#iXC@SwzvlMi%|x zyS;8^FJZIEVNDlc@Fj8E^yWI3>&)J5Z8LbY&h@n7!LU~P4x9OFY^uWV)D9M?gms-3 zN-M&z{iE2(DY68RgDjsF>mJUj>t#(&HD*iijwdbOIL~3~>axKBooh!5pxNnolh-p- zq+Z#RRa(N4tSW29iqryBX*o=9>*=o{SN6!3ld+ooRhV99v<9pD;k`Z-T_g{mUv|n} zCdD>gdVNb)l+)9zd70f3?Ed|idy5nY@$ckdl7<2hJX#T4Hy_{0eEl+n8Gf?1_;|sm zXR*^Gv&~nRJq~pSts`2VlH+10#zl@-uj*O}$a2wCsr5Y77C`UUR+EvEcbWl$afUFmYGv(DwF(&|^@UT^j|8y5L51PPM5ebTe!P zTl-?e=vqW+uqwnAY4Xszs2~G)Yqkw9{V4fS{VAq&!?CR~e?aMaRmA*A*)J6?N*VPu za7u}gDy)YknWSo_>dnURq-Ml_yViz1EA|u%EIYZ7n)fgN%CdB2DfQ#O8jr^;e;t2V z#pmO;BkL%O1hV2!pHNSg99yHtoVEL?I6{F6%E`?*mmx*bKJBBo6I=xv3cF_L)WnjY zqV&6Spf&b`^gZ4xlM{y>wUn7M}kjl_X`7)eJw=Qo{ zT!n{?F9_D9;)$3%y}Uzxr-Dx}WS$}gI-J~5y)!$T(#<-+!b>L@7yt#2Dv1r|q}lny z%~mQfF;i~Jmq8a$f;PMpXXTylouphhkDR7bKl?QNObx3&<5T~-9f4n`>1#Wuzq_z5 zQ>MTai{vT~2v)?yU`p?aoFEVR8Ob5?Geq_45wIQa$5sl{M_ir6T#jpB64tXQiG8Fd zfHEIQ8}oTc2Vu4GWq8r zjpf^t$DJc!#@y@(&7>Sjjb3 zJQNG&K#;60tqUPSzl$M682Kp?O}@&@X?=DUU4yAbRX8AvA|pEraCtCm&5yO`x8R9x z3-@kyf7(+P?x^lyo$pGKVcDCWz44=pssgn&qms$kT1mlRFP?BJGY;ma8FvR>b}oRz zXRQ>NshyyE(eY@2`{Yw@{)I_>ldSpKGXO5x#V#riiKJrW5=&huAj_a$uc)$}Y&Qqt z4oOG8=6i0Cxmh1=RQVf4J}SnU%~XxxfMWwO>~SjoC0=a*b4 z38B$)hsNFXbFZ8!@g!?hjMtEcme)9X1V+n>Vl*cyG=n-AaSPfx{6kAiG|T_G1vJuR zljHOyXgZ*h8`F-i+Z^UeXguw0oT5KPG(eJAkO!oCpR+s(7tKU#h&Vx5HCzh0cIYkHL@BC4MQ$9a% z!7mlkxl9i#W?f|6WP9gGHy5S-k0sl^}cV%t932oj+<{VsQJ3 zi+_08PHEn0>g7KfS?B*4ykbpJekS_ZLm*pnohIVJqx2&V!Wg3dy{VG!qAs-|$A06V zhV?DN-j)TO7HShioH7G7VxglIbvR);Sfoe?V*HA1<=L18Wr+Z>^PzVw2JbqoeOAMR zar2=BZUJGsW@mzwnV8#G^AI>he$%}0c}L5x_#Fi$4ouA)_EY)Y(q1(3mqK)`2$4|G z)K2rfCIOY3rX&|ix8INHtTB0=m_Oj|a$H$H=nYh<5O#AV27Co&AT;onZGA;k*41%$ zgkwgZd~|8af+E&wMl(};S(@_iH0msi?8uo?CRJ6}dd$xTWI(l&R!`YKwoV@qadw54O(#C%uRj+%WOkb zQcm|CDMjP~0bU|yAue`##G0a?Hh_SQ*q3^DDy2S;nAyn=Wc-Zfht_hiFxG~BUq za3WA}7zQ!6AFeIS_xghllUU7F(EqMwV1Bv}*55CFf`sS?B9k4&Uu#bq*fbkXGC#VJ zMI%Q)pD^Y8%8?bM=S$r#xxKS{&YiTox}_qk98huSlUzlfk!cO+ zv>X{v9M&p{&$=m6Sq+WrD|AFd0=BHe+o_?m* z|C1f8kB6V!gy?@k0#^n-k;%*jS1K4e`sNLopQ1&2WAT#=?em(aU)LqXJQfYmG*o5M zR8!Gg4#?3=bI_@)lu zN6-K2WTbrJtA8KlakRFx_W43amr1h~{c*qXCyJWURt$8E&zsq(yRvR(Czq~Zz|?pF zAQiE@NTmcDT`}4e^%MUl{%-wlTHo3{6e35Lhoi$2?(rKWt0-oXBK3F4Nq3IWZxpNH z+8nJz#dfpFmf}s{M{NWCrl{q^&njk>W`&AdcWE*^r&*FJt_)UmLX4erXVJ`_&`KW4 ziz#vt3PJ=-@gfH;?5=;5>HmacI%$+tGL(wUw$Opnv6>%?Z>HsWPLzD6~l@$;ZhgDv2l;*$X@ftxu2)lgzBhN9r*QS%4x7 z&5U7)HcY^W9=-tfNFY5{TN6NKtA5q}7`ACcFa|w{{-Q$zc_|1nhDb~_hC$4RaAYzp zRs7uCezT1#Z9|LOes1ghuI-Cg^}kWE*PVwR^=IYfzcN^8ltVjh1-Z6nZOOt5wnKTU z!Ur+>%8NoLtC|D98v6?se7G6+-`IsnB_kLjl)O{({Wrsydj@&Xn_@^Z~c=7|0No$g}$ws7cl` zH;ySUteV-^Idk!>8s){+bNYP7#tzDM+qu=A#!m8f+u79=`5nFi=oXrIGEM7*%xi3B z^4vU)r_orbD|Nw3;PXT91$>CVO{LWiW>183mVSJ*&Z)1|2=u;shpu-ylYEvg*O^wT zQ(4YgQeF2VoF-tuq~3MT$f50?qA3Ku&f<*9#X;F!vq8$twf&K_tmb^sWbnpJq~7eB zIq|}dQm*mwM)#uq+wnHnboni|6oHr3LJi#bpPZ_JdDW0Pn5sEHIBrA{Tk(=ZbnuQn zN2~J+6N2Xd(j2--{PMap@#Tej48yU;-3y9tsjz0FGUMQ!lx(`@$y11x0|(o8WOl}e zE0`as72;fE{k>59Aw0F{WHARt0b1~T7y8`5vIvGTl}~_5mLLDYR4V+J88e~I{IQoS za8uTO)|FNZBDCY+hS3DRlF45YPHSG!4~un%U)jTYw`;a1?}6ewy^az`$(MyP&JB_^ zWjgx)t)F#rc*(48^M%!YajBcc;vr6uEKPCdR(FFwBq!(!20!>U93f$wdwGNIc6cqj zz#cH<@TW6)1J2CHtMonYupvh<^A54Gld{v!(H#o z#sTE@UgvMRN*c_bvAAO!*SlWT-6}q z4|%GUmzT|LB~!9?{oY#9P2S@SX}wBYtcSAO64m(>Z?FTC!#+Py)iF~Q_m=z{8Zvo+ z9e355s&o|^cX^S8k(;JZbg20;U?Bd1s$qXfUfDfU*W`hXUeAuK9(zT*x85w$GBs{u znT0*uQ@+|gtUwoAuztV*mzG&GB~3%dSM6b{*{AXJxFTfq>D&X6Pu(;rChS$0T;huj z&K++xU0pIg>|OlIK4g+_82;UZ^C{XQsqoO=n56IunE|Nu&%&~2wE)_tXMx#y~yG}`qHf8sA{xQN>p9oN4o85GSk~AG|_q{ z>!eOKd|@YI-)PiGgUXStDE3p1dl00uW?)uc?w#Cs@8qz?bZ*iQ4m-7gB;HY})y*!E zeaz6>>-0Ll;&XD=Tpq9~*?)DKU78Y?a=&y~M$9O})N9Sx8CTM-SU z*En{E7ZkFG&O$8>H^TvIu4NfDYnUf5<)8>+UKV9<)?mg+61UzsRxc{l7HQ{dTUTEQ}I;6a&&;V^<=$G<8F&~mMC&`q=%#v{6_~)br85&>sllW zlbI%_zrC#s`?{k|-Bf!^%1}b^QIXAXqT3N5s z0eshI%UgtfE&JLnb1b(v<4e@YL$%zJSI_ldQA2S->v~m_o>Z3^qlHzxT7zqduij<~q+Evrz_}WpA z`h3YhEJ>~v^8b=H&`Lq!`%vwDxBRc*)RWa-t=j6#Pjj(VPBvGS<&eNLi&Lk9MVx}O z=<6Y7<7DiWp&;#c!2!WbsC#Qh{+a@!NGa#oRsA79HA4AV>5;_KPdcC<65x+e%42*s zV;`Msr_Ru(86IAqh0FuT8ACR!i;g@Q$KNT2)MeZH1ZBR5vC&!TXXWV|^D zW3e=Lw5C*O(-8*%%_yH!m$e|Q{s{XfVu?lK&GiBG+Hs$q|JE~w@6=-60j{Hu)q+Y;H$lzXF|Vnz0-{$FT`0#NUgAs-gMq?jh3>c@h(^f7jmC$$#!795zwk=; zTa!~7vieb19omeaJLMo6>M2J$if&x|yIg3ctp=;!+Dx+vAz)QRP#&r8s?%{-4>UxG zdhg!Pj4sTeV4_*_2Ny@NrwrMu{89?~89w>kh2>m++ZeT~ZbN@U$e_ckUc(Bhlj!dnF25icn&);5nFg^JH6biR( z17(t?vnsu)EM;|x)8f!5vrDkQU{bR0m2Ip&S0gmmt4&%-WPVMjZ6H-ax{}!~vp8gO zoE;CWMU>jU@>8oUE@P$IXJ1~{1W5>c!7EkdSGIU0wPH7Ycxi+r)R^@W(?H#Xwf`Xe zNdVP=0$!8Si344SOlA16?4O_ z*o@kbt8#%a5g!6x{AUvRtn6HixD4w46(cs-iU-8go3J%GW&_s~s%x(^@YeRl{p`eR zw>JeaOvz17xl%q&nz`8vjR!J&Sb*V2%jrOq(N;<#X$Q!<1QowG?N*5mmf``aAe4=h zsjJw9JMGxaEEsZU>fJ(fCtH3TGJ8hXbE~D^6ZaCIfz=H8w(XjM0FW8?x_&B>yGMzq zLQ$rRRiwxb8+i6$bYgr^_QQ;YA@-t^?u{ck@GQI)-$2Obp@0XBYE_lIB3Se1%{P4v za+gXW#r-crO@f$xyZ*K%!FA2dVp?2n)duEoE8p8_O^tj6s_>W2mt+J^;aOm&uEl|2(W~QiRVT&daw zR(rt<$6*UzL(r5nYtQx{r;`-TdTYrIf@*-Qe&koiqm6%%Ua&I^OUb^j6*8|KkRTtH z!obyT3mRQiIBUvy-n*c!Hwnq9@IH&BA0hg1Y`TNRg7pX5}M%r3d`iEBY>j27zmZZ6@;UcVK`s&vH1< z%PXyyRYN&BhBH&9w_Y&%i@u+SJNvcV1UQvFxUs5O)GY8OoHmT|}d7j3kN^F7>FWx84NFx8CJCTKk-q!SQTuk zNvO{IzP#w8;-E_vr=pgUS4Tx18*otkSq%%WRb`#uhMgt*wKz!KpP!i>J-h*X?B<6k zMgQrdwvikufwoZR-Xnn}(6~*`Q=84;V4=B;2zbegYB~3}r4>s#EqMHEia1Bj(Hy1X zd8{&d-kKqgxDf#V9ZPGrg1rb+8CmI}NNbw<>cWy0!wyea>sr`u5@@PeLA=hx$WfPR zE>iL3Eb%&MeS0hDFC_?! zrqV%$rcEsVCy=ZV$%U-+5s5NhVeBzVQ>8R3AP<@%!wS@iEI{#Vj*=Gy^6T!A zr-CRx(}tIJ^W9)gN*ZETVF6Nqs1*?#Nr)$SB}@7pR)GskFZ=p1iEmILu^AhgETf0W zuKvEB9#1AB0Ipd;m6HY4^g?Y2qW2h-PQJ1^*p;e<+Kjh)&Q_q^)fE`{OJ1!_tO{sV z+&FfV?ZE+@Pa9`0-4nBiEmy5f^>WE+3DLwM($3Y4}K`0&!}_g|piYtbs&`$a}& zm`WW@`Zvz(tJd@X(-kCuWKUm5K=gAM=YZa{-nyYs-c)Hpj$&z6ASkV3sg)d|OqJ*Q zN#qp52mWltR|P{BpmTTU$iMjrxBIpK!V8w(<98bPm#QjEm}$S=>`hWuchtL;vbx?% zBjnKEslXgY`3ZpxUm2(jX0DOvl$BukrK>^muMcH|^)pg|+sBwXheT*-w>7}@(0Mu8 zKiu|Vi?_X}1_NVoRgYi~SVME^I8tPxa@mwj7B}$;FQrjPzr($tAhTO0BE&3b=2q^A%^A<~FQA5=>IB@vV?8Y$=4`Vpf~7RljK ztVAZRVps@el)&-ct2O$?A1toLTKg85(ehjblE}l&nubj_Bo!oOzl0d(CC|7WAVQuC zDJl9lELX-hJG?fnth-C56?n-$^P8u1dV8(43Sk5?a@QEafH7;(2hN_^4hGsYnpV;l z`@b+3s3Y3WR-(G-^ma(JTo{Sfy6WPCbwyK&R6oD!37Na{YK8|O!-qIPrrwq1nJp%r zq_Nnq7$wReSg=Bt8!I`4Yuk=7yN$C zTYlVKD%8~q23p}u{M(NTFra|Vtdw(G@3Z`svS%v6fHEkNvm}ptX|C#M2ae^bN9~o< zHg;=p#TjD(g*<^C8(nON7``--UlbO>s(xxjU{ghsrS5wlgk05vOjcs(%NCoh4mkvlywFZuco-_` z6Bx*%k7+($E|Yq^EBx9FKRox2+6YpsL&kh!(`}fwt1}LzsXRQxzc!Z8((xN(ZVwb& z0nV*l9T4oC3d%T<4lB==TX3ZJ7bQ5mOITJ#N`Ck6jG-VPi2B^$L3MOFidOUh{Oadq zDT>?JlvjMRkLbh1X8t?dsC_T`O1=pdp2((r;fV+Y2xDLPca1QnaqaH5m0a3JqT&DSpu*2$RjTD2sWogV?0r}_k`O4gs(7fINNUi4(U9|3 z@v|RN2I|KXPxa@%r18DEH~$Uxn)>sp?>+3x3HP}fxIeyZGAbf>%?u`apqlSyap?=PB7ReYqEZVI}#=`p&DafAAQ zQzn*~7f&&z9LU3M(RX185Tfb*5W1^_nlb*9Bn&-!cH&2`W9bSZtKe)|o*yDSFj+|0 z{blaz95fvNq`41fk9dX#RvBnZV#6*XIxCW{z9AMLtOsOjGM;4u{_LQ-_8qtoFZVY9x z1SKn0p~w5t?(E$S7xJE1pN>a!%WSUbx3guwHPFM&L!DUW{#oydkn3l z0t|Pz_#wWD+u>xua>awcLX469ml0x>Fb~1*ZQMtMgyX+MmT66RDMj>NXvWLv!%j0y zskL=BK9llCbee<1a3L8WMb(zaAUZ2LGwZ((LZ}R>nMEhbxHWgoaw-(3Gkw4Rdu!ivIa*TDylZ=8aMFA`r~ z0)iLa1T(9$xf=FGb_!A>n48}QJC2JZBA4~VTM;72a^P5S_D0Fit~!WmZHEfrHao@^ zaR6ddBjC83@H;TQji~Rc({ff1G)Ai9HVc7F>!#pH+!oF$%R2d z1oRXG_o^4%^rcJ9+I8mT)0VC2hm6@xMqkLIp=Cd7v74+qjkcLHTb@i^;couYcS zOqQ%c+0>9ZIO=^z)5~G@ehjr6^X@FMm@e?q2b7RMboGHv|N9lkVc#Ej{W2d6^B8TX@i4 zcnp#2w6i5ZZNa-U>-ZCMgH_PLXRDYvM}=UB>)~u+7^u1vxw!4J`e|kMUf|%j0S(i% zPWCQ+JV9}Ro#9Ng@;8jcBNfHy&wSAO`=&(skfDSk*Emg0Ds^LlL7B;+SYT934E z+$q&9ez$ObkJXZdy6y)T{p3!Gw)&CtOd#XTI!T30u{G5+vu3Og#OcSMEFuy4S)ieN ziCk{CiSb-5hT`5xno8!Hj#@)BnXgb^2GT%XCy%O~!KFIeiGsJf4=J~jpP0EC?*3+;ivA0MDyODm(n5kqOarpMB8y{S20Ps&Avec+*8LEV^l$hoViE%Mem8 z1gKUfp&%4sKsRP6t+(#NMubNC-Zg+R3tRMJiy>dF5mk!xQG8X8Q4c(@OS-X0$%9k+ zJgJ;pN>+NwvWlL`XcZNJ+e`d)ZftkhjP0BD_`xy>u4!#ipvj~MO|EM>NQW85<2CCJ zc^3To&2066K)*`-6kOezL+MNXoU?jp$-9>>*1todQk`C&6#sd1`X9%V3U~?ex3~vri(<7;Z+HZM^=4aYHJ9dZB zZ%uzQ!Y!h=!|J8n>Y#HyZ{NH^SYZrNyb3LgN`+EURW6cTVbNIRi@tr_H`x_!h`!8y+uOXL zu7Q$=sC)yK4wc6aiJjgNRh6AA=M2Uy&M1bfXtD3pBd}CNFCvE&X!MHqOPtQNw#igX z5wPL`QK8+qcI9`45WN^YYOsH5;I{?q#$i|zqioJa;<{^${nB*yz(o=ho>KfbeidR{ zuM@29rk3C-hykK)_yAb%G$=%W%>L_2lJ4mrU4sv428yI6b3%UK`{8=FjBmuhvK@I< z&eZ|J(wQZN$Jfac%5o~D?X}eo4TlwJ5Sd+$!iv}<6H#W(->twAR91njQ-%Jn4=w5H zYfzXqP+JR3fBNz#48tk3sP9y_U*rwtnh2>>NFY!U5O^IOGg1uz9O#TjRx}?%1Xrd#% z!nti38YTx(s!-kbpE9LHVm2;YELWH%q27nHvm~?RL=n}XaMN9WrAFwa2-C~wZFhhH z2YkMN@h^Wa<2nv}nYS6EEinvS2iXMh?E=@@Q+&r2R=hfX+MI>(*sMpF)q+@Eg{Gv> zP%P^*1>4@)xIwjlDH4OZDA@}Bi!*InqH80xxih$7BxV7tH-prxxg&~o344V{9hC^myyOQ@YLXrxf{<71GY%ppMh}@ zUqUz>28V5QmcNE!#34QiK{U!&dfd2fJvW9@J(x|hfGmxlqJ`Pv9%cy?WN0?!o{Ekrt|JM{Tk!)#>Mf)^wOVC z-pk5y>^^aZ5FKx5fKug%8jYOw@8$s?QtN-5w-uMFTuoULk{2H%#WK}Ip@~Vy{X!ti zbDjKCJ|u^KVLAm2$d}6s8;($@1OrsW{7ByX4@C3BCq7>QQ$Vc0S^hio2Amq5)&j7M zM~VAqTEZGV9D6Q_hM+lyc92py^b5oo!CVrCDkxn6Ddj`II~9Wf`A~CUf?%-12Z7B) z6s156k6W53$T=6Ckqze!t>GeWEDHieL&;#n)gyDyrCA#+P+)v~RaPZ)L|RaMvzjd1s6Rj>6sYM--8r%^T# zsisVmuB5E&LqA}9#gkrF{rLTd0m;S7mPzUwvz-Fu|3+yYM=|rVASw<2ZsdI>B2eE@ z&|!wsN1!4P#AM$L8Z}!=c0*lY&)`&W`ib)jT+(xV zk{@^qEq;m4-zr7Fcd<}p*2-Fo@4eV&Y(;^NGo!&z>f~6d_!DEUQI0qj@IIv64Sq6n z^GZwNW`jK~F|&gWIhg+Kj#E5Ombp!NeXr|AJ@Ucl`OR#9dRSQ>*Q(cdi)2i~OtBhy z#=!r#7NRA8@gv41G_y7#{CYjq-9wlm)*#LpWFJGKTuzNFsGh51hQXTmKx#Ehx!9?f zX~a^eir6ky;OrHtiub^pV3c5$cvZ`$JKgl*Il?&+D66B970jDcRXtp|uM9O~%>UZ% zpHAu)`+UG`%s(&iO56ldBB4uSPuCik+aE zfqHm6oMSOC?~^|kvw$wtuNxB-u!k1$meEEZs~QGY*mO$O{-5T)vrUCa^+J5KU@k&N z;x!sG^_st(F;_PJgy|`jLi2j#doPM-vldkkhh=M)1lr#+v!>Bif*s<{nyno!n@~%c zlQ0nOjSyWa#6F*KZzJmgXH-24=ML#s8&ssdf~51-!2-+dmeI$wDBSFp-pW@(5&JpEvK`X|U>i%>QW zSIsY*vcB!6%*@NFI8zZ>m;%rQafW{pMXeoj@D1$ ztM;%gEFDDktnl!MVAYv|U3Woh`husb-72ukt3P}cth%ycuH9*7{o-7KB*RtFY5BWt zD3zq}aJu%u<4e0!>@JO@O5$y%%LXK-#cPj}g+@8o&}K+zKDqr*h%U~!{P4~4!z0C6 zW|4-^Bk1Wgpiq0xq#Ja%IJLGOUodO^Zm=fKI{x(g&H0Y^_hrd&Yekk~i}FDaUhB6J zr)e!pjZ8?$*;uE(h=)H47V_%*yIZ~DZL{Ms{`Te2<>Bw{C}6cD+a}^QmCJE!;vAr# zmU4O=(?WPm1~e6Kd6W(IuuZ+@5W!LWgCAEuTb>pw!%|gclq@kY5K~KwGg=k$J3@iT z%<}UAW&K#u+hoDpcW8GB_8=Q-^DqA5=j#CS!m1Z(?ibv3K{G1|2jv}VmI8&4npCLG z6o5dFS!Pyw_=#JF&KfQ>QlXqiz;xvK<+9=*6t(?lD$BfA!wgLj*>YZDyGZB}yyx_+hQdA(Cjsxk&z7 ze*St`s)UEXaW{+qRzMdOkeF9`^s8jye&3z;z2BrmlH0-hl9!IYthW8%*QI&Ok#-ET znDh#JmUH3Q+_-8MptqpZrk6^l6%8Bf5Y#!HeMZ#P-)iA+o!(ks*FDf?soFBVrCzJ? z_;54iZ;ff(e%uU&QKAx42wxH$E7YJlm1V=JkTsScn^1I!G*w6GZTBF)q*qqpXSFVI zP-YFM;sEreY&!8~PJwjXNUH24%ncWUFt04OXe*c}L3>59oq4Gi9CK4$%9YGvra9fy8-d2^ zVtDE*%F^0)6YK*S3jLZx`RCYYrd&sO{QbZePP;ZVY6T8w$~6{086eu>A{;~?Y-@j!T#ogSztqL z)k@upM378kthM@k8bXpCeK8ALR&L9XnoX=kqrjVla6m&WC@)SjTgruIPuVP$sY1zU zUe1bZ)nYvY(mtM1*;T+^q&0AKB}mIqAN2aE>*(Nzni{0g;FF`|KJ8xQ-xXY~$XgPP zju|SBlF@gix!PQd4load8>Df9qV}POudASyJ^9F-Z`AVi?JiG6fR(suZDS~qyj1F} z9;A@PsVO`|B!ja=b!Z~6VTl%GS4((2M-5dhsV$c3kX7o{fkQPA%!%PGZ~Z-}Y)+5! z=J_j^LTs%b-{LKz*0yE`f5&)7Z8+Z9Ao_f;2t9YyY-X)`8B+=q6I~5i_A*+|{2uA2 z4>l_w`0=$9qTh?8KY%aj8tX1c_?0~{Hn<=Mx&YVHpJ-c2!^9aqxPG&s|3TeJBFUNN zvAvZw;}-&M4@56HpT(BJ_0zFXEAQ#U&5DP9K9-rlOlnVDzieZDtYU{Pw+Wg z*&2sks}RPTd1{FT|7zI@97$w0%E`wF=*v%J2?VT8+S<@;GkOL(CU)@GHIPc>X4GX} zOUX=MkFn^Mj@K;msDugHpt%-h%KM5|hNtLUyrX_?pQF~Q)W*C4^tAqS7}NqBQJFjE z?U%ubD$Y->B0aGvAt@gu5?>yQ?Ha7F@kPRhd?c&QpH3c|#Ykcf!^YWNex$CKkN7CC ze8IsgFi@4%>o@-5_oet7fENV^!>jq!b?6d;Tm ztT5}?_$3rap0O?47>))~TZ5y{R0+BjnZj^3Tcg8mfG@ zU#=j>zKhJqZ!Fi7p*{J&Jp#(YO)gBcDroC^~!F%V*E&wtwl(N0^sADcw;t%DAT zcJ|zD$q6FrY!Qb?-O+D~QFhpy>AYJh6a^vc`6qA)}La`N@ra;T(vbJf|CC=yr@wdVFR9vAtSv zw0V8zDpE&OZ&WDx?Yf{z!S7Mg1LfeNNUkcsT?KX4EULjJIYU;xs2b}0GpqE72waw1 zvxv3aS^u*W47>cfL!0FUH?-i8#V0VxZ$H7=i@~8bcNIgXB2F0!k4#BT-wB1Eo}OEH z+<*s}fRhzD$5WQL^7F^=sX!NVaX1ZDmk|VC+{OG)XZbk@-VIavwnlY8apOPX^O_`DOeqUZnc6zcb)Mdo!fDu>)%Zy}NF@>bSxmNrdHwqomfIc{V zu`ByS?T0=z$OyV!TO1$-Ikn*=sQHIf+ja%VxM!sZUBkvV$ zR?FX{Yk?-pSlJ2#b_QWkpRUU=4|}-P zmXKDcVyS{<`2lWL2;&Ha!jlTz+(N=+VuAI&7A1SsFZt2P%>!rb3T7Z!NQ+2YEBfQc zJ(%(G^~4-ER8H219Rg=r_bT|%`!%cHibrS4%@nzx5KTAa&40C&oVZOPBD9+t4ZgEB z0>-a{OV_aF&9oloOq>k4)M}Skn|Z`&RLa$LLzQ|-3j=*%leU;9GSab1EL+Z1C~}Nk zsY53uJZ0G(vhU;eJ_$NRw`EVDdaEsCEq^qo+u(~)ZMC+MqTI8I8ma1Ns1c{#zvaAJ zApGx8zuN}h3P;!-J8R6XU8`H5m&i7Q@(XC1$NJc_BXRstKhH_S`~8v_v2M9R`x zt-jwu@qGmW0kCRz%90svGfqc+)l+0&tN?es-Em-7$BvHO=ThIqPHwBXd1cwpEeMu1 z95BO6@W~IOWv@L?nHpx&=O@?$kMEkZ<&Q{xA~-1YBu1) zgKd@4)`8Z-o+%Jxk^mF2nYEWN>WLEnFsxHuSUICZV!?LJkpi-@lf7dTt)fpB&F<<1 z^xF5|r+8`?z#E|uBP2h2?+lu}VBfwkt4oA3$=N`_mRnx@AXNMmm&ug!S*G$_0zTvn zpVsgW%Gtv%CkYgEkgpI zmr+|&tEsxoZQuH~>{;1>2wAw0sX=xTs4Vz)|8OKINm zYApY~^)ff$$Z7rnbB zv&;OMt$_{mtbAiu?Dmv>15QcJAY@P zn#~n{HeN`m{O>;h!xmG6ejgS7JUU&D%HPL@i;q@- z&)>!MG02ba{Ncsd`z6&)P+3iz;R6BwHo1%w1h`-$>Dm*_3fHYOyb@}8dFF*I{OD?i zV$QyZ#O^rPQT3KIqu+Kz*I?g}*kJh51iX}217J>-dT5XED=*c|ww58juTT)aCd(-h zTIWnR9A>Jlb)1Xs0e!|&hFe?32)a3yfY+|swnZ5eN8^Kwt5b2=+N zR6Jux7D0$M_B_q^3>kHT^hipCQxJ?!#r`TNe)^A;s#$wuK)k?G2~c??+VmBX8;#pq zID&9j!e1S8*RcPl!?UA0mx1lW|G2)zG2T5ia}ErX(0P`ENBt=Ou?n6$Z5#^o*cVT8 zI9mH>jUd538(rEmJEy6ZQR>s#6{^G=130>DJ1gp$&fsNMgX?FEqBO>+8Fq$@60v4+b4&oPsi359Nu?i zxTSAmqzLSB9#0Zu{W_~Q7?$sk+h&=D>94~`huptF1TBkx4P|CEJQPdKE3gs<%8lYd zA+WIwYKCFw#tm1D3ea&&CG9&>(JjN+0z-Yf@MB@a1H+;&1(U8GKy&i zSUat^+vQ#R4pXx;|2%e9u2B40hxnOKAH=nTH8pm(5#Y!gNOD-DX3;?`WS^ANa7*9c@dU$TbEKOE!j7fkx?HX`y_Hs-EfiXJ4 zm^U|3oDY3G7Q)Go(k%B2Eboht1~$Z~ZHz`V-Fl2B{js~<_-m(+S~yfWq~b{7nfgVa zVEs7($8bW8>;{q1*(&2;`}eK%#HIr!)umNh!f$zfL&RC+vyAhttP=?J5lSdvtI}{} zuJR&_IKQ^&r+c%Jb5VC`#=PAb%_p(BTyJI9x~_>Ya`n}T{?i*-QUP}u`1xsY2ChF; z-}E|`q=mO_)pk8X^Ovu&kt<@e6VHvo&aii<`1bmfa5^LUD+;r6m7MA{=6P?*KqAvr zX0J5VV|yY}0cl*nA1j+N-49X*(t;VmdBfTKv@jS@x*zEHpWAI9hjw$T%U##7*IU!r z$k#K}#?s%_mmEGlH!A52; zt*`VjSV6gtHn-kq^|0hYY_Oiu49ZnOrLu{roTp$&Jk9B~f-Iuh&eYcB^SkS#yAE)x zo4^sza~1ZvhY%I(p4XqsVUM2t?mMJH_D3prnhJfxqoV*MO{9Q?qe0Xdaawh4OmzY{ z^6em}FJwRQ3x3V5+A9-;BRT)Z`~G^_UZuo43af2=uEaj?w?o8_z9H2&%^l4k9`%dQ zZY^3`I7AtcRs8_f3Cj@vyjJoTPIRRk`VX$k?Atr;sT|1~N>_4z_sq=bW)5x}94KA* z;M?K{sbm-3{1)UdYhY#9WxLLPEj|m3E%`hmEdSX%7?!vi zF(GU>XgB#W_W(seu2hM#u&4`uMuoTi+&`;biH^H z(NL9wZNVxoz&{rY(0AWwJ6$bK{s>)=A0cFbC>P1_ z3ZFl;`O2kJUi8v!2zV(1OR(E$I*fYE_6u0e!*%g6O3v9pfIXwx2hQxBfCIxBEw$2` z?z^9tf-)Q&#y<4x+M)G*q@Zc`P&im+@^$*l6-Zs!p;AMaL!gYNt_(jbtK^vL7#((* zg1oFpo#?+Dh+J;cmL0ku_wUS+|F(?E@X}zIBeo-zaj*QY7IW9*QBY318$C#|h9)hW zEXyWc`$so^PTqZM+N4}a@s$0{#Qf}5Nm|{{p_ZTP29nN+fw}#mb<#3=>vreH_j%GK zya6avIAZcOl|E-|I_b=rth_Ink&!$I3PUZ=HRg>iq5c2-UhZUa425d~fJ6er6&|De zQx_aS=9Hyj$p8aY_+A}63!&;C3l9T2k7Byk+4Zcz9`N}nY(Zm_(A!!;~gp znV&sLIPq|*oHqt=G?Ru@ze8(2w|Uxt%||I=d3o-w+Q{>~tX>-2qCgz|{(gkZnuzPA zC}r!h&pF<1D{Kr_AAhdEJ`b2VCJmH#Po6gE8& zl1qJD_hOm!VrXZ_T0HOCHJ5Z%M39;=V`5UpmeFQ3%gz%Sr{S zM1$={EB<6gmT+Jcelm&lUv#PLcOGdPuB>y4#`4gb8Gy5S%n?+S}BeJ?;@yaYMRF&bDqr?nrNrj9; zddICBn7=n2kmnnE>0GQ;rD)69d})W+ClCMEp`QAo54JyupZv8kMAp{!U7hvH{3{K+ zziZlZIIN}f?#&iD*}vubSPVVBHfH`?J$By9Jwpfhr84>0AoSGer8b_4bCVHm0s}#T zLdYZ*3x>cze|1c)5vr`xT(XBZA3c#L6tC|pLPGWBaB~WwT8+M06y1_aW<_nz#)%8V zs(?ucD=Ev~T6QqMsl5KdflM7auh5(uWyO!*TUW?>E0yPUyeWOl@v z+0WAn`}$2$la!ZEaHZ~~!uPQmWhi}cfL)T%Em=QeuC@Xkrl*mMs3mentr6LV{6jNf zz`(Gg6s}b82_B&eeZCK^$$XXcEzjAf0Rzderi9Stv2%dYOsU4iEznEr~Rij zG)CMBaqd1+KVU(a7qFFr5~$w`4Iqn{YQh=zl=o>%VB@nbLj?!c7HZ{=Yz_CrYnu%2 z-nPP8-%AAcrDIJmjg;Kwroz(toNB_EH|Rw>kpEU8_3fo{dL>GklxfTFfCq~rj^uaz z_WWITd*-BX1T0NBN26Da*~BK9o(N5Q#rPt!!5Joa^zOg%kxRXjOXZsPW%&gA(w#GW z0b;q9XW6KOu^}5GyXRxag2dPj1{UQ4qeRNAY`-_2dGvGkENykO)#&JJpS{D((&pq8 z-OyP84Tru~K>zUmJ;i;$iDfK&Jef6|Z}?FBJ8W3}`8jY=MZLz+|M3b;IaXD674QhN zaWUt!e1EBT=N*=MkI#pH5t}m{ebWh{j;T`oR1d!gksX;DyH3;0fBM(y6iPBzZCoiY zzV9iyFY%7=*u9<4VTug+=2C`OtI1@`WE{TO(MXl`0H#gfT`U*sMBK7@JYl|;!cnp* z=pI6uqJZ%AZ=Xf*<#+t}lr$fz3wyoK%<|)tGusd~rQdcJRct07oOjo4*&Z>Y1Kl5TyAGuA&yEfoHN>G-9s4Clo z{=u(&@HhbIXl_Ol*FKWUtk{dG^2;*fr?Ba3LUlFAYl4`QdA(CdnnDB1cJ54C>;p-A zHmEt&Y0`opFr<-;`U^-hk&K+er4?#|oUG&XEgP-H7t!s^Qji{@7 ztz1Q8%@gU-XvNn8d5sh9z6n;}LZ%y(T0z;3()bfUPMz+BC9K*wE##ExMDd2Um}uv4 z)LZ-Glv-TbT*l%Y*81Z}R7^_>rNYeqw&RN@M5njiOPa z;i>HYk{OB>ee0L{i@t4L^lih|CkJR+nysU0&g8pFk3v~NjL>32YvrdsR)Z=VpV=bY zQx>jx`>$?R`{f}t{0p@Hlh)^|*MugRSJPrOI0xD%c8HG;fTjKY{{ESbaz>g{M?@)A zd{3`oUAI#F`){Qphl4!0f3sW}`@~z&ymU@IuNq5UaonjWzGLDv(i`psb6IU&tN@!_ za?XE_Ua?o+cBdP2C*gYd)P>VhL#S2OSKEFUn>E^3;hNuT&+|<^r-nYCzxR~!?X^c! z$V>P>epvLHQS#bL$%<*Ot7~45k6(WW`mzEgf8FkSV&SflwI|*=;yp6=hziL{TG1}- zZC}uCpKs5rxeUiK3~W<|I!#l>CU;d@Mn9T-304O{z=Z>;R;O06=0k!a{cAWBx8yZV z%BP}5Jbb)vqIo5Q=TPu6*L2rugQ5&Rg># z>UsFo7J76+)K1JDW4?j=vAxCx8og_Qwtz&+K{1^(lond9X#ER&15k~huYha2iHl3tO)XOc`h3SF!N_=~4knOUe)h2@ty72|S=-)q`sDEaC;$>RD7 zOIo16Cf!+5`h%(s@>fSGM!h46@sFPYv=tr+B92JNRmTXAo3&1yuLWMx@ItTS7p4&3 z+rLir6j1WhMu|5qz5#Xfpf(`E=2V9GSEQ_}q~Kj%{{Fcgr4ZJR!3~B0(o;opRhh5t zIbs@;U6I+r{^h0c{NICUTSsFqBvtYk1Al4B41Dn-7>*7rXr8;GD-iFBi#`WU5w1T# zhsU&uxB_}HPnKybSzw^?$s@otL%glwl#Zcw2e(otgyVCTv;{9YD-rPW510+Ozq7r4 zV{pl=x5nq?D|$}-AIhi~QfITqjOHx@kJ++^dKzeEr6~oo=5|Xw=GFwx$CO$n&RKTD zGT9#_t))Sl@_Ir(=BX`I<8Y@2x_O_!-dNA1*~Fq~T*5%tG2&Dhyg)S#!@f?m^R=p@ z=5eidpQ<>JUPw*w&mDi%{SE0%2b$wQhh1MS`>JOD&&?Z;bESi#zct)$vjpcgbeK?= zfv1>T8l4FcR$SpTCiyAL+b6p!K~Rmm8jF^_Y6Cn4`>ZHGh0KbjRgs^D zSuKSlGU{Kn44p{L<0bsnjfP3WHaqrVRL!o z)2&%Lw4lI}TP+6tc2m3%AE9QU!o7cH_GWjrMVXN@v^&&aV$cZbbJ&`qn@ZN# z&#gn}V$Yd1;4RCohpW6-QJ?COmt&+mvDL%+EWqXI(dL!-79Z!c5YG+m9l^N@zm4dX z2n2F~?3eg@)wuzht)>@af<&+oYM)n|*EfOq&~BnvhSj*KCA8e8+E%k6WHzgUc`9ac zx@44a&(~U3_rZaVez#CaOee|{&+Eu3&1~^yc3?9(!7nIVi(l=0Iy+}YkMq`Yq>b(L zzEEtYwbKIODOCIv7wV1U;<~IzV2=~mF!ujaeBS`%3yrk;ei)?DBkGY2gdUROtM^YH zHz2f7Q!p+X1G(}a(U^vA4DkIkJcD;Hlfjj)ed0t_u565Wv!TyIfpWH#sZm~7J<%%y ztXMUt*JE@ao*s26UitWHfb|TbeOCEdCREa@L)DP#i@sigeBfIEb*||c!9pF_wAovr z?kz;V;#IPu9?IWQ=eFO1UAHSC#%*9@V)Ee(@Gx8Eu6NfNx5?_cTYTe*sfqwdIX7j= zv}V0(&Q{-;(D6RZO<6kLIDYS9O68gbpsqQgceZrQ51DElHB)|G8aS6G@hRx*V0hsN zVZ@m(aA&nU2alf;#3z-&MCvNj)T(WM!8Uhl*bFh@{v}O4Ib+du&@OvD#C#oWslix3 z9SE2_I(I`mkBOFdy zhWgZUdY~4{Y(RK&Vxk_3_@El`kteHmewST$dw)jnPT*eFJzB-NP@Z%2%4!|eJ={>^ z-n2hBG>~KMboU#CgGy16*}AKBb^=yxcLdAwe+|iREiJjyRHP6x7m}DEF{IIDeaL*w zZ2Ex&HCZ0?ppQ)G)lNm z@avF5W871*+}m#K@{)Ch-wa!8O1RTzY+coCd~Zclj130YBhi(u6U#(Dql~udqvchtQzFMX*sI;7HfX;Y^R^TecG~Jpz z?4Og@esn05{r}4ZuE3RD)}|k0aj+W1Rqma){HT=G&rGiDeux!Vj-ePk`^vPn{4yNz zJEX}L1*_S0SM+agn;l!Vl|L%RHLZ_4zEr`{J7UC}jcvF>vRjE%IP~p@UheI7vVU2Q zwoZ5wp;GGgRqS#P)S{*&j$$zhkkV`vv7cEV#*+uEsT&ALn6A5?cH&uZ&iE|J?eV!G ze`x;rxOi&J52>`S@%;G;{=HdUVdWgNY07FU%rEK-&ORsUjv9W}cVwM`)-b8}~_*FJ##D-f)B^d*}SO~mACT~m5})~qMS5~0q8 z85U2ku4+T*kpGuE_;w&PXs}H@yzbhzWyQ}+vifXMf)D|^BAL^qW-aQi4watEnVp=X z?MhvZF*B8%W&L&GNJDRpfwN*}tOaOyt#6m{6l{sXf$6QPKmgs|ug)GOZ#`pWRfnkA zC1jDRoIN{}MP}@ummK?S$0-NHD`NhNIvA+jQ)wDXpNG7hO zyYvB0FW%8)LA3hHPCQ7&JeZ3qffC~Qt$SQW@8&0>63&iz7X~4LRV~GL`kQP_0aG@yW>PKJ$Y&@poBBlQdqpXfV znoS2WdUTd!dAh9$RL!^lMA>PI-~`2v1O_irG-T&ZWA5K1Csh$%z;xuR20tPz$*$)q zor{nBeY|_cPsF%2vm{Kr<0(-eF82R9z3{j4p8`#2wWdd2F#F3AA+?#EEa8n{RLK75 zH5|{ycKOoWWE!PtIc5TRD1u?O4he@m1+%VrcG0PDF|)P _HdD`n5<@_AzZ{+=$) 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�e%nZC1!^pP?`Fe-tm;C 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 G0{{TFWc059 literal 0 HcmV?d00001 diff --git a/pr/672/docs/static.files/FiraSans-LICENSE-05ab6dbd.txt b/pr/672/docs/static.files/FiraSans-LICENSE-05ab6dbd.txt new file mode 100644 index 000000000..d7e9c149b --- /dev/null +++ b/pr/672/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/672/docs/static.files/FiraSans-Medium-e1aa3f0a.woff2 b/pr/672/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{! z(kRY3IK1DAx+i!~-GutTX%UU&{*cBJ60OyuDMC-6c|BUX_5xbdibLNw!xz$XgeJ82 zN%Cl?y$c=cSf_${vuqI@GPZbZRsaEZIN*G!$s-1QE(HoDVGrPgUjxwKMEHad!iXSR zi9LE8;*gJ`JLE=B!ginT9`>n66sM%WB2W<|m=Ei@&UYzk)C8`tBD2&vdNkH_nABd6 zp)as+PT5g-cg+r1KKwc>U&kG9{{3BxHQ$A`F|4Z~q{*@!e-k#kCfAIAbb<9l>LLA*WaIQX z(oh0`-~$4jxea3BsX;BG4*2<&4i%+&-=S7EIaD9&J!)LCSYsA7)^WGvdD3y{K77LG z5q^XaK@5pnDu6TVd8;L$!Zm83Rp>;yZ{&E`Bllk&VuxepDdl;7?Go!Su#B3`3M{as z1tYU_g_AWgN87V0!d>|}WSEJAHR{QQed-4VOwa<`^}{T}LU5@xnNSc}oUm?rBTJyB z*bHXh>98XMJ~exxaM5Gfx5^O9?rM@o{a8;K(-d7vDU3Ofak}*21x$U12firwcZpwM zg^hKwn&mB&ct*7n=#4tlBB81iohxYGQz{W-^zMbdcPU^x2omZ(+Q~cosYg_KRL3YL z7N_O3@I>$Ih6Ovm$nIHAnGuRXSZY?i2+x4GH=}oJJ#=BEDB_%?bRjobG)4a}85@2V zPYtJ&-{o3$AZ>Zzchlw!?G5W^vnZ%ttr6A_xIRK*;&I=!UbB{Vf14S9FIt$%2ApJ1 zV_Wh#Tq478`cre1VP{3c8F2q+q{V?7z!6MvFyed)(CvSdx$z?$!BH8e2LBkYIDc?r z#Gg|212qz6SmDT`YHl&!y|`aPSdF+fHj(}d#PIJzk;p=ks%7TTJdrS^gjWvXtgOx^ zy!Mgkk1VGXynz-v(*F_fQy5k`;^T@WbhtdHNAUULaP2h91OHy}^e2u?xwMPpvs-ED zvy_o#ldYVW4`Q;nBgvSLak@>$w#tPOxR3UHr?emz2nAx|>n&MUg-z}k4=&ZDj;XXXGvs@K zVOjqDRswmnfK2lkv!Yvlnp6|Sb^fq))??vW#`|_xTlL%T|$K-wt6Vo(}9S`>f5;xSFiVdrttr8Nhq5$1mtb_TeGG1-jPv*YXp}sjU-v z$6yO=%$0;Z6bEshhGAVgI+1-o-5FH-`)h-vza4GIBj%b{m}{bqG0mFV+d+0TUGBhB zPjJvK^K99fIrUQ7uH>-r^AlF(@+!skuuNdrr?=iM2va#uk{py~yU8BW4ze3Ap9#)NHdvM; zeh_aaXyV?SgoSoxSlK#X?H8T6Nlo0miCdiy`RLAFrn{v$u3h75)2hwTnp$epSIkgZ z3F;U16T*s)vf{_Y2RWZWytUKN_5`uRh90eNh;Hp>mGqLZOkGPjY`y&=V#n<-ciC#t z+_!lZ9|YlB&9Jt2hTXyb5{r#D4yyyDi_z3uG&r$svbr6|=Rmpxez#9p!I=~s;*b}x zc*@?Y_&zI1S;>i|m_Uh9RNS_elbTmR>vFcjImVvg8i8X?q-0R`H>tUQ>($4StVgi7 zK`8sA+$5AxZgJc;R1a0TZCm<$j@63Z`X-Rnz*?rnM3lefE?ubEoFW0Q< zZEUQ4@uS?_n|hxNgWK&)LyBr*yhHd%<%FYkv+% zZ-CG6W>4KYw0zOx+TvoI;Li~ISw6tIR%h!p0kij1a9kE^R{kM>6}!zw%(@Dg|C}F% z40aC0zsMl3)>sfy_=9R31e7|BQbJVtLms11Xy@`qiX)BX?;``a_!uH4`yX6`n**VN z?}8k^P6=BN>aJl&Df#Cs@{OLa`&7*TNO=Q6Wc-bP*9LNdt3T5aL@B!{1L|J!Xvi~V zFL;8>F+U3C{OFDR5Dko7zb}VJ%;Zqs84}(Ay%ovZ0Ea+$zmNhq1{xAkB07wy6dm-K zGX!e7O8Zf1dPWsQm~TZGCrc7=T{qi$gEv%R=m4-+|5_*5{KwhEW-;0Zmw_LJd7s2& zAiQ6<3e$=WXu>AjJ~N76-c7=G5$W&EIQJ=>AG*X?yO*Ts$d2EDDnHV&5!5%WGVtrH ztp{nJAld&&{xY1#4T65IML9^SEyap6y4)Jh@gh>uAEAchfcZikZ=sQsV z;utI#_!A=7De_8TjL-0e|E@#AvYzrG&z@;=YloK zNrii{e{uDgMPFm#I{4qf7<_%F8?O)x=h@0+l&rfvn&e$$R}g?jApZ9`Go* z4mnDA>FbPUCuI3A;GUq0?0@dKi`WcC{!<&yfuvow-8T&bLGC<({mg)-9#@`nP2#!BgE1T9-`NHhM0yxX_k+=;7i^9eVk23MMPl615 zThiN_lXW|CB#qvgmV@inLLqzgMM)-)-Gu?7c=Q*wpKBp zPA)n~a^N#A8+P?91Zsz5fas@X@gx`7xxLI$y?_}Gvaug(5}Ux}`84DKfr9?Uk@IL2 zT>6UFjDhBtMUF!ea~yEU`L(a+6P0f228OT=GkP{8rHS^K1Xbv*tEYl z;Z+i%rhG;Q6)(@F=M`R<;iDxQx4AU(cn1Lci!r>}K>!3dV+w$%rOZrG2=}1?dhe04 ziF|DsF^L5sM&>?s`z15RE+a;UQH1IJU*ErS#BSX&3UYF2C1CUT1O6bx@Fyhf+w(u^H_~)xxSRk0E%hcNx5{wy4Dt4wzl>v z`^~Q?#z`U0+ACZ88y3*>emp{B@Fu)lq*Ql1O+2C2sklEq%({%!1 zcKA_8xR@+Id3wbZzsZiKKjI{pVFX00vU{*+wa#z7Z&xOFS?Ctk^(-i(TGvzCwWd%t zzv3K5X0KpI+uyVQbo|Lqhau+1_Ey{WMmz1coFh|yd5AQpsinDSx8sg!@4GyBlnq9G zeixBVwSfaQXW!jriHPuS+Sj$`zt1(x3>f|E@Pjf$|4gHKbeSFY20zD!7OG zkp&qtR+2F#^Q&f?_VPnU1^u|A+Z|tTg9eJv)nC)xgYxi~Dh_vNJX#$}NwWz79A5>P zbhcsXq)BTU(ezr!Depoovs}|do8|YmdjLky9oDLd5^#JU4geozREvlAd4z#SXnS;Z zI2!zJaEeUGuoTnr=M(!my^%;JKw$ji!u(@3s~M^Y@E!SGn*9)V!z}6OUYiuV1-lf3 zx8MW#EDqB4H}Dhu7yc-T-1Aag>1v1AY5VDpW)@r~_|7 zGcjK9ns09_Ko5aF68`cYB0e1F2b(%U*MaLz;#( zUHc^L3Gx0?A6=m*^n<~2`A1?LreFrWybp*+``b2pn?pgMvds!&9VAn?7UatT}S$&0na<4q_ky zQXm8Jpad$Qh8foVOX<(KUE6XvNHlsaB#ND}XdN~IGV$7yJw(g7f*(&*8aza-y&W;>@0Pv5VFhhV4pE)$W*<^O#8G8U`@$xq4N(gq#nLi$| zF1}&8vHP^`cPr}nKr+~%2B$LBucHF5MM(xzOM$DqLxCgn_29)hn@lDYc=(-4__FY2 z`n6X+J@s`?AMdkQ2OksqTEhhOp!+@An_LnA&%rArc0GU}k@*k;?jQIPVsw8yYI=k1eieybNE|$2YI=!${ zuDIES8YW(1E-7bNyZs<`$zAMK!0=EMJ^p>^gz!Brx8f=s8|gOmVCq`UfJXY!N%#nT z|B`t4Ntzx7`kB3R9LSjR#vZyEiGx8eY8to%NP-klOAIe^{w%f2)YZLV0fMYI4MuQ4 zewad2*)-QuHT`wcMArv#TVILW`J>Nwg~5i|W{5HN8EJ~cCRpWJQ^>`KHu(4uJA6o^ zu7{37BL9|>8S5$_Ue`+&gW+a3M=lOK-B71}1Lo3IkkQAiP{=Tf#}%Kl2_&deZlOwk zsM>qgjfUnHe&h2M@iN2BIS0_fRB*^sqo&S?jyV$^{;Y&CJF(>)q||dVGOd7xV@15F zD_5v|6)IJ)UTx}{G-z3~PTlJe$a0Sj>F$ZeR(l3Uu8S@Un_zL95~-Wh8G~5dAtK2L zKgUWa<{`ZJJA=}}QFBIxnmam6wvA;eY@hgW?3~ny?4C>lpH5|5_D(z4KYf7%o8AI% zx0ofLw!Bq7Z+#myZfiej-tV$Z`~Gg1jvefft{v}$o}KSMeY-lyRkgM7R&VQ1i2Hq< zF!v|nkh{N|cqrUqsppz|z{NxDzKKld?{Vsp{*0gLkiP|A(vkaC$5H$?ag=T|hkZZi zsNAa@)n_tcbmK?XN@^&bEw&8$vJFg|+s&k{gJAZj$I!>?3iM6MH7uAGteDQXSvk)I zTeZ-IJ7DR{aPYD`w8Qpl&CXrl7TmB!SMu#o%G0@-TZ?`IXrIaobhtAlG( zdxyY2OF($X*NDh2rXse_H-PwVZWQU=NkBG8aH!>E2|HvcIHAM94;KkxlvHG~;v$cq z0Cl1)(I!oWE=^i2=`&%=UJb51_qvj|0Kd^4wGs8C=8v-GhegfXL7+qu&0sW{EmoVu z#ls7z>3y!uuw<%BfYE8iR^3Jd0hjIKKvR3n9DE`pw6X*_b6iI9#>8Yn{#aXs0&%F1 zYQ(V`)sC}us23L+&>*gd7LA}~Q)tz)s&%XV(Wbv0=+Rlb_3gqG0j%HCC9I5;TCr=C zRKXr0Sh80_(zJKl**Pu8TwRlUf?ImfL$~&*thqh!eBF_M0q*W`A?_`#Xb%=soX1Nj z)l;P%fqM}8oXB^Uv2Ehs>~Wx{ zNQqv^WZ5e@m43nEdh^nH_mzeI`+5C;!4C61b;AD5U}_z$+AvGnOvY`~bh4bRn@_fr zLo3N~g6-rqIkS_ThrQ%du%BEfw+@o~1jos<`%k@+w@_b_FVxp$o>r-XvCZR*E~Nhzsm>G>{zL}H0l zX3m^=q$3~YsG$%M37K52E}O8=ueoqBEhH1rIN1j6U)^Gk|Oc$(9m z<2;vt1y^(>SEa72&ge5})(X~t)S;zQUy{2B$^YJu&+RNP>h^Mfd>by7E1aZwQTBuI zE7_3DYnF-=BtHjC&#|;_+h)uBO1@`wP5NG&e?-6? zDsbHSdkf=TDl77!2$v5dk21QL)15$~6O*_kC$)jSNSES=`I)d}3Hb(l%P%B)_ty^Q zXWkwjt!n#m9a6=-qltkhPGO}~7Z2hS<2c1mmvF+=b1J35wtcW})!XFoqY0REdoHFa z>!kK>i2<6k(FAd52|e~yGta&B$m;>;`5;zF{vV;^<4BydbmDlg<>!m(nDQ^SpRdMy zzCF<9FVExuX7*10W>ZUc%TCnImd}AYddC~<00e;{(OBY9#2|;Vxjep5B$n8(sk9#{ zY~6Z$oDPB)30rphX4s6LUQ5q|b?DT^uU}J3TUXz}(8$Ep%>4LeIxzHzWfmA5;n-xv zLCHEPPl;>fhwQ<-cH|yDn5aE-SQRS@%}%l8 z$03d)D2kF>Q;c#1D1RMG1uBw-O4MShqplppYssNCy(>f;Z8@~lo}vS743VG{(?W1+ zX@=5l##LH*Xokt$_sEHf{DqE)h#-49avJ1d zE^4^u;IA?WdjSzS<>?V0=@CO!Gdr-goPtrfr96`skx4>6Nhv5RRi%)cJN|fY%68V$ zwkcsx!m$>zII*=W94xvaQL601atSKQiz?NXjJ`={4MC%poX$z2i=Exp8fxEE?>gne zNzV91G@)0QV)yrDR>3M*p>@bgrr)GHw%odt>g}ZZoejlyQQJ?E?v-eNst+V*D$#Tb z9Zi|fQsmfLF9HOBtpWf50001hJLxeJ*<83#<|2!i!DOo}YOrC?mbvDp5OX^mb0@61 z7rwb4A@hJcA6jJ=hsb1hH#lbpSn~oqk5Qfo#{5T`CIrm?!MIGAT;&GH>=G5MYdVqc z&yj%ZSrC^5i$419=$((_zQ3!jZ{WKPcFVQiBAe^8vSLDm0ssJ81sG%{dj!GR4agob zy8)SGi)2&ly3)&(Cf8$d$W2TWM{t(t>J&+^p*?1-Bj~IP2N-S`-Sp6mq^qB;2?# z64bj53w#gwK@A;)TbKgU*4f37dX~|>*VS-vpp)>Dgod;@j4oAZ23BUs(5m0VJ(QMx z5Bj0UL;rp1Ip~34kXJ=MA}T@W?X_YoV&z$|Lxt!Y*f(|w;<1p{dzoS8uEmnHf@uxw z_BpaUJYyxG`LGZsXp`T9N+r50NSi}w{7r8W+q#sI-oJ)TB>Z3R8v-K})sw~ZG>86~PeP7{K~P+`o3PgpS#$viC+8(H$boyX7Mfk|k7n25n7)yv%f_%O9}odzgY z4CuEeO-2wS23n?0lhwpS124yfLB!oDvRvnRIPI9QAz|WyA+FBtLeMHB2LI-6+MOYGT2t)5{DEJ{vQ4 zx8*K9Q+;)FZPDbbGDCNMQBYx9G_creNyZ=h_4%UA7jrkUYAUOh)h&bdXdA@WM%W~- znkOz=Ym>DXXC130(|440BlU~#J(2asg6V_(V$}@51>)z>#KppTZb!oDXhYc|94$+l zIcczDmbS{%6+tdmVL4qLYR#<05&u0v-x61Ta)Yni65E|77R|0y3uaFfN1tZ8IM5)Q z$~8Y!=+|eZvJ+y}oQ<#NB0C=!7Y0k_Qq0aTz$@TYv23nIcAa6lk>(xo`7Vq1!29Ch z%a}G_Q#3kB@*)ewpG{r7Fv~p^7?LuTtP=mQp;&U>{Guok|5ERqCn->E)tst892-cF zRBsmIAl;u@4?NeSEHN$|H_XjDJ1feeM1^~jjCLPhQOxx$GP{rJ;^suH8It zTT9ZOdV$~5)OT#$NjTa`WY5~O%V)#l8Y{=T5tg8Qagp0CC6~&*r||80{r$Ur^QC=j z-+4p-ZU46`#JewI72tjuV6LAYqXvSE%vgN0av%*Cv*sOX8Xh{3R6 zmxM%7$&-$1p3JJZr#`Cc$%#IT_C>uGeJT26v6jlLD(>WAC*Pogr@lmk5|lDzS1e_y z8WuH}H3D_Q@fzX6uuQ5s#Z|QLX3=r0&&(pB{lq8B{Y4c$$JMHW7qqr}ykq@)ENlPz z=9*eQPA6n0^Vk{SU)|M_xo&H z*Y#X-6#+2 zuR4s!nK&z zJW&G-%@)9+tvw)APPxGHeYjJzM@QC8jM9c5@xn#UjREfl^{Kl-L(mgZxcTQsHokN6 z302d%SHZfLcrihG*t&I=bpYMs8)1dGKL(j$v?v zC`q#I>jqXgCexhtOp)aT_{icL-?i6hI4sdxGTE7sN`TBP^aeeh2{*UlPfBrdT8!o#vPhHQ zZqb|G5%DI@K)8_PN5!Dr3g@ui;9>|%2xMB|mw=~wn$>nsnmUGUAwJEUIYsc5yqF)f z57ZZ&53mD3P(65Fz#IbCRX#(I83M~&IEVw`z;7Tocw4|$sAbk*Y5+D?e%idNLt58P zw7Q^6kHoPow$F;4ImOtqpQ4>oMn5s)84LExhxvBAQ;nITZth0sMkE71K^}2nr^%d* zHh~&Y8-g#9r!L2YdJ3mFUN@PwjNMXK{{v zwxiszj^!V>(*@*N+=3tte650Ofvf?mP|egp)4?6*oXFfd!^)A}Sq{ zICG*z5>IZ~%8(P1_;AEzOnBnJmE^U#GW#tv1O&j~y=@R|cpHLFewX!@jw3JZnqPlkqqgwY{6OUyLz z!-*NT%s632K4*g!i4w_qV1_WVoB`$v@2z8cIRnBpaJGAS1+z$P-y)TXd2@cyI|e(hoxPkUSdk(LnMT+#?_YK{Q8%JP1?k$YYj; zEX^Lx=Q@uE1=Xb0UE+Ej%&A3m&9acvtSux|3#oI&AxmpnL02R6>&d%UM-R_-KK;4* z@OGTO^YUxnUyxZegb6HFRjcBw1}6JvVO0>SMz0R3dC-fPJX=etm7^h?FecqcdiqMKErKMz(vVTW5GceM1#({mA%J5} z+bxF-uAKB2ZKuruZpjOPadGiYnz35bFx8wrbWqdrBI_O$A`BLb#p269`1o_tKg?*s zRM>(osfHn*q4m(2+D@BAm>xMZ45J@>;7EK1Awr5^4nibREh?s2j=rRD_2t6R$-(7= zl*Sbm7)wNIa%rP*XX~kjMJ@XSg@?x&lQvp@utZ5js>mSO!rDr@Jmvd?CN?)Y!c1{e z)M5R(vw!M(lz0U~8ReV`tR$BmLl^G+Z>O^kf?bN4j}SRUpiJb0tIdow$ljw&&J9H* zxojOQe3O^d7!^DpVViLIQKT4~WV_yeWR^%N(~uFSmLkm5zC(P2i&0jFsodTV{xBva zX;F;4A~Z@AUCR#^8RgXQlhz&O8RCxFv%*aXaO*Nr3F6&hMuog{i+9n0#zJACCQChjOsO)(W^NKq6 z45hxA)06d01mqepk)ROzUR*}gFn%&{l-*z@Q{xhQdcXOUmMnj2c(YQC)QKgw1Ucy{ zZ|A`qkAO|wW_}0;0>#bd58_$_Gq6Q4hCp$-!G@XDH}D`}K)Z@2V?qKOpS~MlKc}RW zWp_V~BsofYgT~XWQmfQD9m}!|Whg@#Dj6hA&ib${n{`u8Rt71%@@WhW#c?OsieD}N zREsil$#a!J>hQ_)(&WH>jlcQ7Eb+Gn!8m~+1F!+8bxi=Fms=hn1Z)JBH_ja@W^D|exdhGgi*exv0W zDnW`}^by5Dw~Ngff)KHb6gKWZKQk3k0kQpK<74NpuXYhcROsz)nkhn)W`^F;* z?FR2axTkHgwLkJ__PW-v3?OfGD=`=;Vb-ZqaHCZTwB>)u&pdQ=D0`N)$%n< z!~1V~3>wqYqHyyH&ZxA?kF~<4nv-&Uxjv!`*qST2*Y3`(^Mxy{AJ-CoLal!brFHT_ zvhXN^7J{<0qu*%L_~H&JYu+A_E~U3Metllm_z|VY{JB=c$8?kZD!ruft6O?P0d7EB zvc*YE0}%Jw3u^(iwlqWBtyixCfVTJCoMS<-U>9s{+k?xg)1t<9l_`yl#HC%0nVB1f zGd8(X(z?dXEEbQA9nztko!w_Eb!T97lZ;kvX*#P`G#P#H^RwlQbXRR==PI<)>?0x_ z*{VHEMrD+PM<5Px3X4#|w{Ql%o2wV%U>grU>T$Q&D5ib|+=6T|=DnRN4glDD)LxGy zPYttZ-C#FTiW)W&i;K~kq8ql$E0M@z=Y2@2QA*6#m8h&)hFH|FgT_L99Z?B{;pk6G03d`odI1watTK|sr9Fm4=d})V zoLOQnsVc26>qOlp2cS#lh;jRzo5L$p z_oMEKJD-_ju@E>On?1+owl;(m&v%4}t6uK>t|PqbmE7Tm_r+oQ#^^1?ZC=S5fR$NN zKhOaPAlH>EHT!TPrq?n^W+$-wQ+rUsJOeu(2zh{{a+La8@MV;n_&Kehf)%W&K*cNh zWU|UuO6>Acznc>1=9(#XPh$yA*572QO~^MWmxCw=PR$wc2(;F^j)5a&YQkt;XTUT>n<6BR-jFA2RRU;lcaNI z?zJK+6=3Hux;08kly65?6@+B0(ghK+9R{yqFiEglbs z{e&xX_jm}QEYKPfztfmItbDDkc8A^J^)9c(qqCgO4?BmsuXWRA$}!T?cOmGweu3|No;ChnioEgi zd#Nwg4>SzJsGnB<%3}sFoFfZBr~7xp`)dn}_5@cVE7 z;rr2+;ic7g0)J8GblnheoI7`&=@;j8P6%F*0Fe)hzIf`x!J-JF-&c~2_j8WDJ~VrV z8)I}B>!0Xu^bL^u){ef8XitoadD3I9wq&FXM_IHOg0#$E5b+N8Z!~aOI-Ji}A~tfT znUfn0%Eyks!ETgIo`w>LIz~`~KXGqQ;`m z0(_MfB8!AkM9i*CVE{r~A*}E?fL-}y)H~m(dp9CU-ksnEs;d+EL{rr{c3~5%F z`<7^>skFLwIhf7n86&m}D`eEJ9=jcgFDOHPtkYi6Klh#r9;W6y{SkbWkzC7A%W^g= z9IeoWv7t?&1vyW}Cezu9S7HJUIIuQkK6UjLe8W#4mUb_z|1UxK8M}X8nJ;>6rjfs-j@&hp zvDeW`6CL&Uhh0v%<^j)n$5;NyjT}M0O784SWF0ws-K?v%raBqmPrIFT-GiR@uCM)> z7deB4ivwBT&aRw#+GwV;f&Q|`DK|Xi1@HOBUrFRDGko0co(X3N&j~{W437bSF zzVw4l4mjryk9oyMe(+zx$X8C}goxgw>)_VBLk2X8#ygNPaRUiA&|7MK*A)+Kg z^e8P?6Q60Lr$K(Q#UU5mDtb~xZ`6LGxrW;7W3XRs zchqI~dfJ;l_iKU38n`^io@7JxP%T#rjdal0H-59jF<0E@8E^T*Z#j{zjL*W z{I~c&f2R^sM^rR)3`{KSTs(Z=;H`CqAug?`s;O&WlEq|H4B!U6J!A+JPC-pe&&bTm z!Oh$LN1@C@B4QF!GV)3)Y8qNP-Ftr0S>Mpu)I7_{$yTgkV&f9D(CpwM7egI6re|W~ z=5r+`DSMaxq<@}Qp{}W|i!d-UG0S2K5^B@`@<&}T06=^Y7y^Y;P}9=4@gt!MGb;x- zFTaq8SSOBj%OWKsucV@;p{1jzZ)ki^@pr`>Sx!l|;z%n>f>ascQ1GbOA7Rb6CsGbt zW==k(sx@jCFlf|7udJyrtd3l7o4pP@<)Z8E`_?nRdYAuyfRI|P4q{;w?f=9(B zB&TI=XG4BvBfzIrwMOj%2927q(t2C%>irE>1+{?L;9W^7jV{ITg6W+ve#N>7S>FwW zd?hONR;NWLgkT04PTt3yP?%`gXJTRJ;^7k%78RG2-gD>fT2Wb5T~k{ZVPIrpmc?Z% z`VZ3gto@jZjsXDTgTN3doPv7OZkC>rnU#Z^mtROkOhT$Nzh6<0^GYge8d^Gf`i91) z=Dqxbqa|oS9HgY$z`J_(&0qe*fBtLr|1swlddeb>hd)#9;P8ykX~Y$1SIOF;_Y1pl z#GFQ4gEps0o0Xkej-!YescBluV8@iwu2B6>XE#LwKniu|B?RoJ!w2O6t;qXu3qwsr28ZuRbTdR~5 z@AJc1Tnnvm?aSev!p|=f<3-Y4WVhB0|Bia&zNn6eH(m%_j!Ts!>*AioN}nI1e4@~! zFsLx4u(YsQS@^VYRcIHhaC)K4nA5C5PIEU%Wi{3WmIBI^_!ZP(LB$mlPG?DFNH_5l z&}if~9%tuKvB*#ok#8eo@aSdi+ZR)I>wEkA57Jzdg#r8Y5tVU}odp6ISc|Kb{==&* zX?``p9q$6gh6jSAFjRqiG_(RznNQxA-@x+Nd*)ey`d8WTpL}vM_V3&8x%YWDk$>y- zPR}pi1b;#(yR3hOrq$^UMw1y2UyWKfR7J+v|2tlY#pZB%e1XtcDt(J5T)}t$*oY)D zg-QeI3|}bsxg*F$RNbV_$<_jjex>Xm1em!3KWF>|&u9DsPa{9#4c<2a(trRd$h!!@ z${#wK_eIqOxgHmnz=rClnYnyW>c{Ro7y^YM$P}sy21gMoR62vnVskBv)UdjGLVW`W z4qyA!s#d>-;d#F@(P2hrR(4KqUVg1lvMy@l0CuP8m&E>2XbcvIClE>YB-BqPS146# zjaH{O7)@r2pMLr6kH6-p{@I?%lUwR_T<3LN_b>VWSATZUVMqPzxRXvh>%5EpcNxNR z)Hm0+)VJ2R)n8+}y7@t8Fj;JYP$ZT}Wpag*!{zahZ5hYy;pye=4a)W5)0c0*N|l<@ zXmxr6&9H#ujV80j>gwVL!wFI*_fM27lq$7kvF8#dgrSEUvN5s)3wxI{uVE? zRiVu)q#whA`04xG_P@TNv8lPG8-!7uq*-2+Ro%4udp4OZR-22fo4bdn!_vwcjltsZ z1b_+k*b9kFq0&IcLNBw}94?P95Q@YSsZ6d=s?-{-PH(8>FeGh&AaFmuA^YV6jltsZ z1R{w{q0;(Y=2yyOu{m5GUmz5TB{F&c{3%sxjaH{Onq15ltIh6ko>JFqOrrMiWC0jK zF`OXjmj^S)?}DsIs-_#JWjn6t2VoQ^X_gmdRX1(d592g1>vo*i{bc4`WnU-^jzFR@ z)mR*XNFr0HG&+OHVsp4WzM#%plK6m0Wpag5rPgS5dV|qqwzvQyOeo`hE>dY@B~lvA z-u|q%w0CrNb@%l4_4hOWvw^O%K2=P0lgA4CFHJYhFP7s4QIZu^(+$(IYgZ(BC}0$E zVUkvA4pdfk({@N6A#2vH`V(Z+<$9~-Z25fl9IRIX8MWEi?e%LpK}KViPeB+Lt7Xm# zWFvxN*(zl9XAR9%Z-Q(oh?2aMt z8CJ}BNmlgy=lDCOixZ?Dgi)NNSzc6i(+}e`FY9)km+S5Rcmf~VGDuIC3)oEK$PH*Gge%XXaC^H$TghSaS(+W&)8z5J~`PHE{j;`jdf<|QdW1)*2 zUeycpYoQt0JfD#0`j92a5#$L9BqI_!lc!->7200F6Wu9t?@x1r%LtfHB9+P8izt8h z@bt`w_2yAWvU-v=u6JMd$BPkV6%@L!N-&Yt>!7T&*duE&%EMWE|BV z#f+k_QCrg-`m$XYFTb8|S@;a?eKu0YY>_RR^&C61|2UcR&t8g8N`HLj&2%$Lsi#>w zFh9kk|GAXwfM#EuR}-D+LLS}dK`;8ykAY^;W>-r58GBd8wX+d>ACG`qUwt9;rUamG z-Tz(_Y~FYieen0<`qhWv)MKH=zP-;?Wm>F5AGy@Jr)I78$;D1NQd~K@dhrK=7Pm~? zj|nJy^Ucq*#~L$l`BCvH2Jkq2z(?NHp+DxAQ=0+T&BfclJrO%B*@9;*%(CKH+%~ps z8CubNR%>mKCat-Wywud@OUp>|rDgDmBWuapuqu(p1)VX)&rA~;%o_NmJF^eMoYH53 zX`e!V;t+k zF6nZv>>AUX(JW>+=ef>vehXgY;+C?E<*aaJt6AMXta0t@-OwgBx8-f__x5+P%Q>uf zvxqq{5~YL6fks5fC8nfje+w&T!WO;vQt z4gerN2n>P3DX3}b8JSr*xOw@7M8qVdWaO1p)F?r7qM}>;GtYh@%oR7>&19+>!X+zI zuh(ij9X7`b-`Qxpy$(6yoXc+d+kMXh-SvWAhLQu788U3-=&|D`PM&&87i)XoimR-) z&W6JGwA>0StERd>)L47H4K>kR%Wd|%{Z6`+lHNK{--#iDY@u9hhNAIgI-4(+tMz85 zrSZUu_;j|{=n=OyVLH;BlDO%HZPo)%**Fh zKU$xgACr&O=k~|t7uyg&jzrqJ={)bO3Szm%tUtlL|!nVwcM7{e43ZY$xqcr|pwzG+Z_o4SN<>(kk zXOiHFdAZO)p8#?y9=t+(homQtrI*1gxA(}-uH)6-mw0vd5U=jO!K=LQ@M`WUUbVfL z^=j=`Rma!itS%e*q$V^#OX{E{MYBF}ydYZX8?0+V{yseL&?ApM@w8{_=sAEInp)aA zx_So2CZ=ZQ7M4~)Z&@%PP#7G6M4_>`q&Gbo6e^9*V7{==pK?E>dqi3p%*N@@=53uyA)9$euU5+Ec<99Du3HB*Wa%g{&`b;q%f^8O zU?D^V6~iP5DOpC5Qxz&O2hSyZok!GEkYlLOVZw$BA0c9s{+N7q8D+;Rxk1a#c>%e_ z$nh+uw?uNAwnpyIc~|ZVSWoVyi?$;7iIoR@dH4_}+v}T4na@)CN+lh%4M__2!{rGr zyUJ{LS?nvTqeVJVHYba6x@c$0?rb@nE5`Y9x>zpB<#we!URhqRE!G>$=dIP$zV(OQ*Vh+(MtW zNTU|NuW?J3?yv2-8+3T@MED(z!OOtc-z zwWCI2M+?|{-RY$A%ue~oPJ3ZzytK34*tz=8&cpw9q2BchD0{Vxy#}7WJ{3;s zv*jJD2q)g;dhTrzDWcTs`de%MkF*&u`z5*z6lbo+uXLdojqU=&^X@JwT0@FYW{V}`9XPmcR3*x znbhcn=>hZ1{xCZVo}ZL!o1URDI^;)JTC|1^X_^#H^MVR>4tXl@$4dT)5;L1KJc}lc zqZ&T3tEQ0k{j>5+8qd4G_MjRZd`XH4lj*545xtRrWU)8wQPkSjtr&KT_m2=mwSY`w z6Udx{!8$-EuC}!q$2ajyEYb|KpwG+ha7Dl?-IID((4Bn8LSPo%izCR(WiyH!`Ww&A*FL?`=U} z@s_~$qMH;VmLjLC`6)>?zHSW?RG^bDv@*yveatZ17;`N)!P1A?>6RzWu+kbct#f5L z%hx<|REj4qdG3W|FTKMHNLBYRREKEfoJ3CXL-dq*${fFQ*{5eC+CKPsWN-SLavW}x zapY)&kEv=GQ{8^1VV5$Ex}0g;mBKXXx)x5;ZjEW)olJ}FV_J5KY1P9_>z-oT^p@qc z?VV0cyZ+G4dAra1d%7Gs3Dfn+Jw&G4frs~8gAJn(& zYx=DW8qhFH!C?HQlWbR_kJm$e z53g6C!4^80ofD6SDV99m7fT->-?8lR`SsQErmY070RafUnVm!-LNFG%04 zA=3lc^M1Nq8k9F!e)p5kUK>G4H2eQi4mf0tf1ESHf3CdcEJ%qfK4F7&wk1Xra`HM= zhU&NBX9Vb=j{!!QVum#~Ji?piXbYk-pnWG`^kLKEa!aq3Xbz$)U|tcQ4IK0WR0=8s zRf4KnGkiHAea%`n3_neRe&&~U+SziA8{FhJfAi1k&NN;7*UfU5`{l4$PB@F50T&<+ z5=?9(%)D3m)Yd@!8rtBTt)wNR>$~K&igRUcovAo=(f-kwZY-)&TT-RIluBcSN^|{{ z))!+-+rvvorKi1lc+jfOb#p#0*~9t#(^-A^n4QgI{;{~*kLAb4oLi>rxt0<8tGPXX zJ^sx7@$X5&4tRPzA1pd0{+OxXm2Y&d)+Z3Po4sQ%~czU;%Fr z|2*d-aK~E`S=z`-{Oplm?=@X7$GFnG+pfI)?r5+0gVn3=J;n6>shGVliuwENmc`d> zna|R0A=YL?^Lhn*6m|9qOzxowqz7Ofgbd0N7`KwRx0^z$GV=(U{KpLzwy{!S<7avP zUDR3ceI4V>PR5*elHjU2eAC_i9@N<8J+y=*OqEwxwXg1K3P@iu6Z|DxGtjUQEOIr5 zVyivjm(2-!Ibqc*i)J06##$zk~`k+T_N=WD?-OM}V-22SJmA06hkbm@s3( ziXBIDQUDijJ$gsq@_cFM_OkPhWqPJS0&EK=0gBqo?JRplDsm|`th_@qjhC_Ul(JbO z)fh^=;7nvvo9t@7CW-khU?GcG+?FQWVcAk`x!YOEDps>*SUYW5&$hNbA+wWR?AGvZ zIV1_I+Z!o?B+Uw}`b$7>D;+FCnJTH!7D(YpU%?RcIQQ)?`;glZqv1yn}5?};CZ35we;em1MT#jb@MaRQ>)JMS zINUT2$D;$rS6Ef8tn|R4iEiD?Ca1i5Sab!wtfZ<|`_)EUVSWFMHQcDvEoWuRUm?M2 zQdL&wW%VPjbaqx=S(UxjwR{)LxisUskT2Y5S~HVl3nbkQ&fTO>S9XTa)Wi^VzRXpo zrafa}Z13F6xa`Dm!R3T`TGE^aNl0Rn(p{C=Iix}%Q-a;zp7V2|rV(W%aXBT#M+^-$ zk>p$7``H>Bp|?SkD>B_Gc$Jk>^7Das)B+r4S{k#CN0Hz~1(* z##-xbpwT8;>_ri@9*RpBL*6lm@=%e4{)z+53g^kiOOUhTkYPK34!5~lw9D;@zA#R8VvI_DH@*MI4 zIvW}SjfTcS=R;GWAZRXB3iU#Zpch~{uw2;ntbJLRvp402qKZ*fsD|xh+jni>yWQY> zZdmcc&%3-Y27-DbWa(nc$lmp_mb&Y2=q(pdwJZiKwKLV3oZH15=hq2%rG+efG122k zSm|o^YPG9h|8{qH{qdj}G6ExRfD-N3O zt7_V~dDl^JBO|v;I8##R51qp}v(LnhyPAR#!oUX38krq*p70jW6@E@U;Lk#_E;^&Y zf5Y0p%*)(=YD=V^W=z7top12(3Lu@uf_{d z5cI}S!;CNz()Vruz?X}!2aHj??+af}2jBWBvaPQI_bxy4|LE0Evd8}O$iq3V-BdDW zmv>>smzAtb(lW0=zjCksl5g@X0n}fo!hflK@!@djE`Au_`r{DV;mG{ZZx!7)?`}S9 zk3M8;hVhtsZhGYJ9-Gm|H)OVcb8??5B>yr^|oC+ zQc(W7ITUSyM>}G&oSDG_IrMI-R%kVRM z22M*==a>#`_>vb#)Uvx+`WdrD8VhcpkVyw7U@cy)DlN5xg0i26h1Pv|@ejRYk!rd1wb&2(ay zd36*ksEN$b%eW^#(InqWy8Pn!Gqk?rQB#C>EKLJRaJ^3N9pfyFUOM?uAEUig5CEWG z0--bv91;f3Aoq^$(cDlu@(4QZO`QT!X$>JH3?2!*Q=SDXEL(s1ncgc zFX%z{ER{eagut*fFd{IU*9owJo;Vy!%u;scC4r@9>(daxU@1r;<|=TCRi+-orn{Y^ zRx=wyA{gG#oZdJt%*B3jQn9*n%Oo!vFqhi+39=^CdLI*)3YLey7icSjs2E)(8e`-P zi@Wgh!_!o*`bzUZ<0+X)g@b1QkgblK?&>a(D2w7DnkPJbnNABmN7%Sxq%sSy3%6OU ze`-mV<4M@6m>*S24J*Lj>kbvfrQ%$#KX0?unN~w)sy7*vJLoE|EgF|H&>o%LF_f!s zB4C`wJ2H73;jEm5vbgGTGd(nx5DJKoHolbVIjed2S&a&PQ>UBmKJJz=vOV>FA_}z5 zTkkYDb}H~7kbc&A<(WqhbI&!mbL$uu zc0Qc5^Z9)2X&F7QuJ?Vcxxr)9R7~7xFDC7`FJ-&i+x`xBWSlW38f$_{#+xiK z*?b{ay9LyHpwQf%o3#tG_upr`G-p@U61lUlzjNI=zF?j^*9ys8>-xd9%6CKE(5a=h zo&D`5g%1)3HI>oS;Fgi0v5C2bnXRMKRE4IPD#x>mY38W zzu)=u?#_FE-{0-%7KZy*2g^fsr2m;?I?c$IV_eg+G>9?7>M*@|OU|}^QP~}tLoqrWlQVHS8@E$&crz2H(sMFhSF&_9EBCwa z-z9mu!GT0#P-0#1#76(_*Fz}YQWD;Y1ol}%JD%Dml)qC_~s*Bnm0EFrTXqc9M^f}y%I-?iJ>lG& zwEL2AcT%26)uGfJPTi3-9PCC6+Y-}0iDlbEZf9)U6>7U<-HxPpG2AXByU$Y`=2v}d zgYSLk2kUK-(M01-FzKa~k-CW%{$Sp9+)2k=6l=CvapLy_LIEGx=kBPfSb@U*foMT5 z!}p#9_rz_cMWZG)50XI}U1sx3D6#m!s#NPWU*B$1_^?I>pY&uTJ6Xxh(>#v**}5oV ze=08VRx0BBC-WIDCR64F&$-NHs0dAO>`Gy!DpIjZRJt<7RkY%j%s|Ep2GH zF(Vc+Bp;GXe&mEWtTqKE#GHaq2zf|hLewdOoKY0fqZkrIam1Js6Jkk8sD!sjWkPhR zf>=-$nWP#rNOfd{8i)xs4|EVIq*~wrYKwz9ib37Z@(^FECxQ2M<^%os&>%iCgpUp5 z6QlUlR6aA4`c~0EG7a6Jky|vDLX+YRO^HO2(G10l<`aq^EoiC4(27t>XpK102GO7` z;!HcNG3^msIt;tsVfYRMCc^fZ4?AEx?1){k6HddgA$d$1320{aqgupfzp{mC;pfV_hPM?OVk|6rsQ4uK4@ z8wMi|e1p~CTf~v?Zr+II42BRQ6OdcXLUNdmykrjYoViFV^RSxC$Lg~HyT(GSE{kwb zSd2uo1es?cjA{$`09KCj;0e1lTPG_Q(W#<%NA>VZTyvKobtC#~~kaSVNBZ znWK_8*4sIb<9kl13@5GRl)ap`hBG#D)&U5kccP};{hZg z=0MVps3|e?#H7Fzq#))+O2i_SnJY>KqLA7w8mj^+NJ}h^h{peIN%@rx{m*1y;2UJX ztQg?}g~&v#1DRcIa#SKVf~=@TB(V}?LlL5gogo_i$WH7CInaw3VpqtC0pucff!zLP z4plPy)P8|E#1eZ!zMkm=LY#sEKOC4;^^6lxh#&qMob!xBp$J$*QFCIiD6os-=ID$U zSV2kRU?_zh#1W^V^k0rmy88FCP!{Fz60Z|D59LGPsIXjQ_EmDodyas?1*l9&Q3WnW zRYHk3;4)Mrbf^y3q6T3@O}G)Y2nMy`dekB8s7uhO2e+a=;l-Qq78=}T!d8F>(2$6s z5%K2r4knJKnR59wrzyl7n$^|jgg8TsqAdxz3#|g*qqXDWumt{#wjn;yF7P|rhdhS2 z1HYj|NC0#+zZxzf{?Iuv81IC{pi5v7-gTnuLIMNvJ|zlW!HsT|x9C0`zNMQtcFA{? zYD6fx=m{=-M5#wFpwXN13Vnb<-%YJQx(Ec(pYjC*Ac=vL4t#v1Fz5$m!?~Vi7(>7f z7)lw(FsQ=tn^8n(9YtVfj9zAAvYap&GsL8jG>i+pkMYEOn4qZ>2`r3Bfu&&bvJAK7 zJj2wl>P{j2ICvTTH3imim^ciMU>!#T58@a_9LGV06O=rh1U*hs zUf{H8b$~=E!dWA9hD0gGdC=hkr3AmD5f?8L)rC^vL%jMvM*pJp1sIP& z1RW8WnXE5~b#+WW=dOtfRuU4kL0^*KYeHs@=vQ*x9Q>8c@jXgv&rJpKB)JAzLT%p6 zPBGe}!4mN!LG-;2CdAK#z8}2j5t5oPPQmc7M6wfRye2HfAB44o-Vuoz+Utsm8!wjZ z_0Cpwf0vWM-Ux@C?S2xti*R1k29PD`7Wcfk={aM9O`}oXMde(ub4CO#EFZdvwmq zHTl(K9G{ymQ$H(x&TCo`n{YZy;C!NNhE-_caO3*~Gvr2KcSNoHY;^*&5)DhynZ@$e zuUs~ZWU^m+xCBloy1{dzM|?x{+rb|(znD|t5pv5aobttd%WwKSy%A%?L|<)Fu8$)l zeXGw)FoswJvx(*U)NsK>z9-fl(IpB5BDPiUZAb7ev9B8M+bC$RYv1L%nDMlJ_k*rfwbQNy_la9@kGPY1h{w>HAcurJJk-Q=O7 zw~**dC;f!#R2@U`jKp3)JC?2=^C*YtDM^q)5PWKpB*jOPs+r(v#~?`~jga&+ zMF=zYtjXwao%yKF)=vozldRw<$qtT?XTf-qQ)f^=r%v$B7YlzB9uNZ{9A+>wy zI?gGG#YQYx$%p}~82P}OF$&yQU=#)$7)8OxjgR>0WWWc&X1pJ4VJQH%;KLpA6F}Q;7f~&YUxQ+*b8;tzmCZiy@^)iZpckSPS`+~dR0pK1^1o!bk z@BsG*PjHX>TMtkfh=Rc=8=RUEQ7sY*LrR6nr~o-tpr8*ZsTvi|*6?d)U>;2Gk&i@z7v%ZZs0sm;Bgz zQpjLKS>&)ro@}DdboHj9!9tDsRw$q&hNBZYV=TI$J0_wBZMEv@;~(8N!>7NZ8H-eg zUh3Zqy~OW4@o?J&82kD_hrh=p_G`{?~k)x&azA zP0`}a(ti649lGY|(YLrCneS`KW8JNF=EsWiXlHWGuYJliuO+j#uDrg=f|ZRseb`uD zT`}ADE9V>g)9dz3-nGoauH@X|G{0jdhXK8h* zNhgD#MFuaKPCex<%b9FGo;jDp*K-$g`N{KNK7R!+6$((~N-^(5pb`oUUf{9PHv}nD zp05V?2Wr|ZK9qI|qj z+_g;f{ico5AHfq}DJqSK)pVoIzD!&M(ne#~qfNXUITS*N6HQALoh~Hlrs(xdvI*(N z5Ze#Y?f%D&(GYQ}jfrA12)v;Xvtf$X2-1vFEXI&-{ExAkuxZn`R{;VsP2Whw878OA z=CB~s{6_gEerj^sXsZQTmNvVkf@~|B!(Q<-lhel7E|YU5%4rkNZEY@}#kp0GD7T-J zN7_6Ni}USGo7Y=$fw5_0T@@Evnl_)G;vzHC=66zDY-8F29*Rp0Ok2=BnHLtNTgV@o zQghNR?2JsAE$J5VM5f%36p98`VMPJ4un#^IeT~4HAhyGSqH)<5+nM*G@fjD}mFuDj z`4!up*`kR#6|yIwNr-)#&7#SX7u$OkP06F!z6=&k&7Ii(tQAemmy}DBMANe)HkFs6 z8MzRf&O*`5_=_Eke9^3!OPMxcnjL4c!x1i;6I&_k21aw^Db^mzqG!iYtUr211L7uz zjabp*Sc##dRJ1TYVtJ7%Mi7(t4as|7E(k~Dy-u0JQDqK5WC1`p~rx{>Lnh^;! z6D*Bp#w?l*7NglQl@@|+(8Ab3OTfNpNt~yZ;3!%dw`nc7gw`fhv;ka58xk_w2(F-w z2_tO+SJI}0iMD{7XiLIHTfxn=HQ}c1;0D^BaL^9$G3`i9&@S)}?Mn2}ZtynkPIS>8 z@Hy>CEYV)@1?^3&(0=d}?N9EbL*P$3lmw&0;4eCygrMUPpyNp*oq!OXNRsFzgwx3+ zl}>>nbSjCZbC8G5B@J{53e%;ekuF04x}1EbD^Q58Bwy(^R71Cu33>?rrbl2RdK6vI zV=yT_j`z_MFecEGXp5eLvFT~FN6)}G^eo;+&%wC#JUXBkU@V{)(GI-?BLTgPX6O|d z1?W|@K(E25K(C`EdILrSdK0bCTQD-v+h~s70qdf7Vhz2k@p#@1d`0hp&Cz?CkL7)U zRnhzL1$_WkM<2v8`Vg#vK8)q`5m*y_6f5Xsup0U}meMESIQk_1rcc2!^lAJ_pMmG- zvqX_T2QSd)i86fw9-%K1N%|5zMqeg4`U*TwUnSD?HF$!)PGsmC@F;zgNYS_8CHgi| zMc;u}=(|J>eGgux?-OK z5J|r#)$|)=qu-Jm`W>R^_v9u00a@vfq>BE8()4H2O@Bcg{grgl-%ytRPI~Dds7U|( zoeVr(W8jchi40AYXff7`1v-rBL%w?P6Knokak4Fuqmlwun*F82AZELwT2 zHlABMP3WL;oiwSN#`G*~QLnzR`VAy*aN^kv@o2*n&tb%PKTS>a!}P*-%$QBnocYv! zpLmo-p54;I$5^&DBI`CfwmC7lt%W1A?MD`V`jwgA(>odZW5*k(zu-h1U^5WKsq^ri=)rObDRN z@TM#Ueadz@2Z@xc&P_b!Da289{hl+S7-&%(Zc@p@sVdc#iJ=;>qq?9#AHkaXH)}3` zuCGzbB;daWW1hY#4AZyucRZr+iw8?T)PEvIf8kt1Fh@fRL-cn&40~FFd|Hk?+K3w3 zghJYiYUcCKvy7Y}%fcD4815#ELmMkz+%4Alaucp$P3v|bSbI=nBRDA*nT_l&CPx96 zz?IS6rCc6^D`VkuIS$=r<#;q7J13yKq@0NE((*34D_h<3{2&NdkPm^IBAR*yG1L*n zrA{FcR5~OIgOY)|h-9YrBiX4vNDk^9l7~8t6zO$Fet=%PWC!%RB0HnkIoSohPRhNY z4r7bMEwB~9&9D{3EwL5B&9NQBt#EBQu8C`ha1Gp;iL2m_DO??Q+}Z=UD)!Fk2lg(s ze%QOhAK3e#bJz#KI_v{!#jq#BRvho*?l|7VeQ>;w{^IxmKF9GPdX3{7C7^Vl;(I8c zi=@RFslvlC0RPPfiUXM~c-v z=-E;7fQAFf8YHkPjH2}P2O1haB|W?fJUwLom=4r^5|Fh&OPWya+D=(jU_CIyhCK`> z?_i+I4_x#fv?qQK&tCNpWL9UiJ6d}kDyz!w590lmP(iU2S0BY!k5PH7ZTEPvE?r^7 zo>#Fa!hT0`0O$Xsux>-zpgMWV`Vd@T7uhK)VI?eLG_W-6$LNrukx;DN6Dn5%p`N5( zxa!;pbB6-c$x?ecO0P!Az{E3`$Va=FS55g;XDC*i_d^>K-)cxJWxE!G#SVtn!*{A2 zkyL7H4i)IU(nDrJ+S~n6ZvtiCqVfP%FeluhLkgC%x%a7fv9pq(Ve#GG?)30L&vDuP z#2jKYvGucAAU;&3Rq=A=aFL~6JnMGo@IYiC;qVD7JQ>wTHqXFv;95I;h`<~M{ZLXR z^D<+LN!{1NQ*Vv{Pd7sLdKk~*HuljG;^}I5x?ZC!s~^HReT0n0I-BzC?id!H?TBCo zGM_h|z@`E2n5Ljv3E6N4+8Q*=ZRU#wRKs2!%OgH2D||k5gMy5qUush^bu2VeK#i#h zHKlrLpk~yZ!*w*w5@y)!Cw6iT>}?EOLAQ=MUqkI~vjv-A35hb~`mclVij&u-=u z*}m*JgBneAy(fVg?M=ry>JyFQf=mm!OWMIDvXpktsn!IPK|TF(_x}qs(<&_KQAVh~ z|3M`4(aVl2fG#NZK(_PJ@XCJotx8QN#_hUIt@@i$DC#LR;_JCSH?4-xI-5BwJ4`ln z5WP2HP~C*Gg*Hw&ux$#C?vRx>MA^9e@BPA6u7YF5i&wdhZcqpmqG^NTEl^mq z4aZC1;+BQLDw`VB5kh%=VZVGe8-x5l48JNSzysAGwImx`K2%?4s;Hm!iXr&a-%r`A z>&lM3DGbJ&UK@3e%tenm_>?0xXYP2tY4^s}4BO_MA2x4ZbIR48jvmY@85Ybo1|`dz@D}V0Rt^hpAmnkt&!7~r5C%dK2jUE#5*E@xDC0n$ z!Kq-O41_8U)ETrI7TQ3l)|7}1>|t-P za)=|&4iOg!+znnHam3pp;sbH<mv%~bHea4;%@241*T`9*HA+k zg@?G>_+cN#?G0GW-Mz3hq~#rCo?l`@@?|@l(ITA~UYba&0n;UyhI!&h;baTj(oP4{ zp;gGyS{bo?XOm3+-`)adfCOX#3-O+CiNekxt9$lXsb#Xw#0U|HtPu@NTEVe;UQy!X znM5bze8#i2MMAu@cl6?Xdb{J}srsUf1rWtTAytL^JRF_W5hMaCz&bjRQ7Nt_DMR-p zekFc`ULc0M`=$qVX#UzRb#2|iWP`997?T2DO2l$Y8YC2{jAYwl7mcSDGf){1RE9uvDKBB6yTmV2i%@Vkpjv=)N zh$cem75+oAM369NmY-}H%%2$<4?zPW3U%rNu0~05*_QNm$wUsu=^hp`sdShunVRKX zSE_7~7L|%n5sJg*<62(PY_qL#tQgZ^sDk9??hzW(ga!mH-V7*T6{3`{Ml%d_S+*6F zO|bv}r2I{U+CrW39i5|5kWO7nkC>_!1Ul=ru;aJLQmqtWdt$vp z!#3*e{REkws7p1n4LvIzs=#DO`70uVz1VRE);7c7ac;=$MO?v;MJ4z}Ww8ELLhDq{ z{$N5Tj<<^{Z77?EPvw(B**T_n822I|AD1E(oz={$YV2qTKGiVeUog`!D5|;@J!MHx zS(*}}KuiFNd4;Cz!*a+MvaODg3NUpQvRM(1eAhe?S8Ha^c62PbQDhYrRe2b<6dT&*CfP7pyq1rS8gH6Sv_Dag7m%{{=$pB(d&(FL~iPtSu`TUWGYIRK@D zMXZNh-VycZ^{|GAUz<0&|P#gG|90(OSO)aw;G zm%(P6XW@7qcsl7kC0akChGrZ);+4Sxh@hVW2qNej5Sim-XKTez%h|WH61nmRWs*gR zyhxKM{l~*R>#jz-%WkCEW6+2EWM_ne&g|1A%TUG-0%i$B(0k;8uPILHpNZi=x=}gvryT8FSqt zUM(+?aBy(omr7&~1xBC)fl++NG0g&CCHVrLW<4`QUxOUq%8dndfu7TpFqm>DkRS>=;fl66EgY^qwGj+ihybz$(Qfj$ixt<@d8KpAmKA%F{2pAe z-WwVw#316uuw?^WIPkudXdG{WA99MP>C{hn^$JI~_kWBjd4G|w`O4r+3*~#JqxoI&a?T$ znKHjIg`SA zA!!!s#mmJ7`#1-Z*#>10MX-H_tqE2u$#}uOk66cvnEuB4c-`78F6}cM-4H4UVo$Wd z9ONvqzJ^|mA$2fIJUD=efUNFK2@94>A^;A8yIhF;Q~+W`$7{?0>momX26-mbskB|U zoAu_+3yR=1^-?Mvd{VW>#YjUXmWtVc*Iv($me*QD*e+sZEZ7dC8>MNrTb#kENCANY zz~9Uv+IipNlI5=>WiNHdgkuFL(c@I5)QbcR8oZd9y9Y&ijc($Fe;Xx=I0rHURHXt5?f-0yF*m%K(ZzU5(G^0Gg?i8g+j z$Gs+v@vnhk2|u=pNM|~Tt zMG=>|gmGjn(-JI|;@V^CZkeF24}sX_wxJBUOA97nrOa$$KW+xdJKlm_h%iGwvnxU_ z%0jWqp#d-xS@Q=dx!nG)$lqw+U$NH;=~c;)RxRs#O36JT>d;(M6V}5?KNl6K!jr)l z8T&>g90MWGNfMowClQlWBdfQZuQ;FI_KqvYIWsK)`DF^SSN@A!hE_G=E(vgekG8^t z7b7D?lf)WLV&!t={*WTcQP@s6bfNk_ecREG@8u;ZDlrMyAmS6eYB_QQ=%f_Se$}dw z{(=Lx1gJS+Hsa`cngaTtE)n(~M<3RCV3L`jDW!sn4I1+Gb4GyRQ9Gy0JW?gnu^Bds zZrl!5r`UpXy|JC&a`eY@0Tz4GV2i0 z7oIEdKtA40tecieA8Tf52pxOWR5dn{nag|OPsV=j7t?w!!mBHQ84y+7 zQghPcq+_ZF&88ovlsj-!FET-7tU+T05|rkb*@^Khh>-~%7p)-0q9OnX7$++Fye`K@ z8AV5dyH5%<eoKo1h7EWTu;3A2OhqnWLYpLH-1`L*tPU49(E&81r-pF>`;Y5K&j62$RnQu z{KKp8AHUO(A?RcDdV;!8h!KX7O62!*7Rd#cm zQAibz1Z@Kd-5i+*M9)4=9YX<2E#4%?QqCk|EJsa3wE!iiMJ5PLCc9R>1ti(NsyZxS zN3Ln=sU=(5qyHKP?Wj3&`!KJ*(H6l{fIN31t(r}HBWVCel*ydy$e>Rh{00563=@?- z)Q+KnFc~5lz;3y092$}$iX;z%(+K8nf$B<<#xk(RqukV_&@@Gpi$t?MmX|V`mbS%M z2)+I|Pa(@1E!ib*yJwqk3c0t${Bc7Mu*`;E2nP6~E-zL>Sk&rvo{ZpzIm!9hW@F}W z|7WLiKH^DHfW9D?=`iUPV}v5n&+Gz=#>T=6?pbF03!S3E!osdiFht#<{;ovEzD8)o z)TGjpH7TJeS{)TydsN?a@&~ ze}LyKN4X@xOxS;D;J@#af)a=cNAsmqgew{C;m8wJbOA?HJ+3jY^ZcU8i!MvfbUXP- z5AGhNzR$e)JgYx#`jvm0%h0NDvi?FeQpD0Rq67hp9q9l%S{7G2vB61R6ul50S*}~= z@*Xazo{-iyU%2wMpGyKG)o+6$`aZ=2s*#Miib8A)n#W6R0n;vA*4@&nZyQx)?p8;? zK`eHMn$!+0n*&-4*oI{z)ZlMn%5bK?+B{_me1n(@dX-Qy}Axd9x zHAZ>bA(JI&#a@~f42?jh$7>~pKWym~Tgf7Cx09x52wsp$$C=I1mubA#=gXM(jWYBs zs)ph%iGcQu@?c)$qex1|K>Dy3qmZ-uMSapHU@f5lNjKN>l;`T+o8aDSU*x&>Z~;p( zCM9x8aq6L8QhLnf7pbJyKv0N1Jlp!@OPg4L&@oJ?knb`u&Im#$ZLtS1rb=G76>_A+ zjIP?ng`P5Ln)YK0NK7<_NNP!=A(g|)YmD*r4nWS3cj2DP1eE%QIcjznGq5f(JGUzJ zU(xf?b@LopfB}ynGBlO)prkI6rb*?jN@o{$7$z!2N0Do0B9`$KAMJWh*_+Lp0FBU8 z&t0@w+cped3u>Rv5)N9b0GftBZN_mvw(Z?tnptSmh??{lAB@m8*vk~cxJJBb3mge; zH;DmI%0gl8lK+*wW3jeuO{oQhDw=QYMLcwFmve}Qi~<2d0iMh9Ba!E?7({$` z%S&D$7Phkx0(+)UU-dkTUVb=x>t|hmtTll!Sr3`Nf6u zKXAXrF3np5YlQ@RnZ_NJ5q{Yh{;SW9NxIDrQ+Yyk^seSo-4g~&dh~Ru+}nn0Wop~$ zQ@(m_*ot=z(pM6aASPxmK^#5}F$0+SYa29V92CJmZ7w~(Vf1)Gt6We|9~q3Xb$u3W zYSU5+u2D>{fq0{-?BTLWMfben`75LalwnKdE6II5yBQ}~JFjtG!+`~!EE?~k&{}M5 z5Kc_)520kg2L$fzTU(>mo-VV%QE?9K`X%g1qXjXA^&9)(1WnL(6?Fcu#a18Q!gt4K zIK7rMEgI8VO=y0+b5wPif>v*kuAMyvgj3UbH@he`PkVWy=sdhs{P#5uOAo*k7V%Jf>nxu%(VO5ilFxEhx+I(|ZFDH;10n?uvqj*y8qFhlUDHA|gxNC%v4gs{UJ*swpc@*qy@PqZw)^q z5&InHaf`Ra(8^<=@eLx{D_%A)y#0v)ZI2qQT!6K?wP#wj9kzZmZFA;Omf)U7t_`0r z@_!XMp5Mg;g9C~*sq4CepMZ&KwQvi&XzDWwejz)XK9fEGpZK;{ zeWjNipUtx^)kn}XvZ-^Q5u)Gn)#UywSFPscggyR6tKL_0%&UCzzh)_5T1j31UQZvs zU`~;-%F~$TTjZB-okri{wwBf_b>u=r2ao-y+PFTl zoN!Zs{?-HDp>}Gp&h|s~EY*Q;mDam#8%9S+P!b}q7t%x=^601u#5bU|aLo1>JQTq) zXgDvdQ|N=y2?_kd9(gV7JU8;)7B|e%oW5?TAt0@z&%(Ta2s~~bVj?LD7>hgkGB%Dt z!l{%eSe>AQ-ook0l}-o^`K+g`G1Q{wEwoC)ee@>EZdcYy*7UbY))}!AW0eBQ4c9AI zGPC7Q?&@LkMYoI{OF}zFpAg-1SMw+OB%hL%Vf#lk2ZXO9$7n%=9EUr~hRynn(qSV2PV+lG)9F&))8&zIN&t@|3b|jPO`88=n!4|Ip z_%4@mn-QIEV;`UGTbn(;-mLb5wJ$51xa|~V03?zN{HUCG3vmu%7_!O45>{8 zD3b?>9G5)O$`}RN_95w?MKZdll}D8NUONjS zS8fL~53o2SibxtQu1dRvo3~_85N!8oUDq$? zBFqI$5YnAOV3=f|?wJpMMf1~U_glxWcla6e{2U8KYy*1ot#?NLV}KX3wk4MXq+w>( zzd;6JyIyh2m*e~FnOpKBp+6AzM|QZD9Bnh#QiC<&%%rxt_A&$jb3V9pdAdMww_-VT zfvmsUIc*9btHHVW(61?({zAXw?ow+ZUD1z???YKz-?MdXPpil)Q86;2XMg}9iOC%= ztS+LEwSD9O?Avi7XD!b3#Y+7UcSv~7*L35zVWy|p1QMA{)BP3-d5TQ%&vXMsz5bMm zk*}-0W$ z3KJMbv#?}_{Of`YoNePWv)IgPtD8h#z#r5duyp=sVyre((>(G6S_C$z9 zp#emv^lRG>6DESJON&^w-~L$UV}HKE5dp@E2|b_|h)Hs64>d*s9f>#F`FQ7e*hht4 zFUGlH%t$`jQ$y|K!D!yeb@o!Pd>|AElny0d7H@6L1Y$4y z{ll#|wmSql^B-t1xed(3srT~}Ubc}ctyUY*L2ZbDQS@E?x));21ZWV@SM6B=GTNO^ z0-Bi6UJf(M-YX*I{&>#2l=O5@dH0t|t0M4f$r@m73fH*1BCQ6#2N6#8#n6BuqI_E; zjC0|UC#ukKTWoWnC|q`;T!tZAJPy0=s31skWYO^Gl77J|KvZcY@JJ98zC2efcNfZK zTowKw*Kg9ixII?Z#ob0@pd{*cjX;QO0;puB6 zT$HDmpO}r-bc{ATtcR2w_B=S!bN?KGE=_n#i0DQjqfR3g{8tNT2IHY2fPS8U`1JE# z7YK27N5#+Hl8dPMKe?3H^OF)v^iWd6J~#HcT*Q?Wpl%f#KSkOVK=`N$nphGO$=*S5 zZULob@jey(yLPiTiuqsiHI8S>PyOlm49vnOx2te57wh1sQS@Ge+lXhUejHAU@3G?= zXR=2_6NmR;LO+8~)n)5iej6Gei^d)N^P{dY^@UINk0$f;t;%SN3(N(Kvb zGjb+th+ZHM3V0P)B;{3}w84A>qn8$~FG_3-$mLd{*Fi5?QJ$AFc`pzek_fv*8uSC5 z@Y>)a+<-CflW}P-!x%#jr$uJ*9YD+%AtifVF_aPLI`(Oa3O#x67DxG-~HW_jm+SmtBi#y&o_U{4rTq${%!88)Seut^qZZndp^B4u73G z;N9vM=kR^ZgKi&vvg~{KrFEz4l+iVSn$JuWe#2JJ>d)S}uu8=5#iAAb_EPj%Y$mZ}WJ3zOKu`ZmZ! zl0CTr0jYh}h$mN#f^EMB{L3|GMo;GjrPFN4SqpIQ55)`fd^G%!#dA8EEA7`C3)*!= zVfnF>BdzE+?8O*T=ggOB4(|{P3Ky?u8_INynv@RG6N&H$q)@?PHfyI^Y{d|-M^sXl z+O&fR*6{@tfU><9%~sm-GcRv!uiEJ2kzH%p>OJ$TV?3mQzGXhR=r@5)AB-T9K(c^i zz-l+D&KjXe16sJ6-Sbl>ph|$@f1(%!vQ<5&d%^Wxrz76}mToiw?$}aR0?Y?vRGd>~ z!LH_njfPsLV8qG{N`Fjx_FC)Urhc-tZ$-wDl4T=On;&Chkex*zZUC5M*jk5QPe}{j zeN!UeT~ltP;3z2fK$}uK29OvL{==W$q^RJo$OkF)CM2c+OsLUI6#UWy#br**`>?{3 z{E`wSy?DjF0JRr`dXWw6q@NbHMPG@om1_*slEen;inYa9796(tmMy*A+H`@G`P01# z(De3D%GmL^ID;y|U{m!IWmc_f=e#Z^!)us@74|>+vPWL(6B*!Jz3d1O+n$D(Q3~C2 z2hYX>E1vLP8H0kgUvMyw`rA8sVB+B6qx8ukGP3%hv1`GUV|8LOpgU}YLx$ouu`X0S zI?_>7;B>q}x~GMAotvZMXJmW#jgyMqirr1-oO^(O^RX(<+UI>69>B z_bj7NraJbJRU>Kee1qr`#VfH((n}3KvF$ITTS&iwJj)cZ5vVAfK0Tj^)9cDMo0-w5AfoDriISY^A`QN$vhzB z#+LPHR)lOxnc|n>bREB1j7T^K`iou z40&i*Dz$6c=fU8DxY)xfplq?&j97h}K}PeGMAm{YQPn8i+P0M?;xL8tiFMg*) z-mx-WUl5=hIIg4~UBubOas}8><;e;G{!?5GuK=YD(R0NhyWohtj>*`VaP`hXB@(=u zp(AG2v;wZ4wsM#}HCp9oy#JQl&x}Cdh)B||nt1K;St~=@EE97)lJ0sPbTbg0dArxS zlxv;}*Q0mMZ-&uOuf)=r`pwN0b=Ii9bP1K>S%%*@d)iOHItPs6gMqa{@vma20$@K|yyhB+8TQH0LswD-??>R>o+oOVwVnm!|4R zJLEVh6k3b$slYoFL`4=Dn8b8ISWcf~BcqOby`h%m8}D=e9d!HFzJMmf!$|Hfc6s#3)DT0R~=3$`SRI)FJ`V(7@?UD0Vr`htoJAIcb1G?bN;RIEM3o z8x;gpMyRR^7m5$OrI2s8 zG1-+>!Ktk`*yBttuu|319%45qmbcZ3LeLwJA+x?M@gUFbrF5|sr0jPWQPb@4>V2Ry5G?pqB;0W)Uus+w^bosxxBYy9qgRD+ z45~Cai_%joYKX!+HRX%CI*f%hKUqY9#-5R^2dDnTerM=m#dZhmN`LaXH5xBfP8MC$ zU9os43Zx}TPAW0a3+a_CkATHMh>r@Kn2h;=@6wudvOdp=0w2YVG8)6i7d8W|qj#nwE=5j_k=b|RG{z*7 zgkoEEe=#J+ve3VjDZu_-qWV0F1ZZ;6F*>no{-D|eyA!wsJ}BMn8LK(8qef?kctQ4K zuSbh&`CRAlR1$zp%kbw41stO1w7aFGxht#pFQK5V#KjrY9%l5zN9y=UR$DLkPod?N z$O~7wUxRVHBCsizaaW=7Gam{|!i?OoV_)9B97!@StVd(*D-b&JABT_HT-K$<03q zFYH!Yc03g_B~pu#0US0e8i00Rv+=WkY3pwIA1$=nnd}cA`gFV~nk+!0qHFG5K?dfA zJfjVv6`eEbjcI_5lk7@-mXW|Z0P>c_FR>Q`RbEH%==67`-K?~wsnw}9%?mn9Sl7u9 zg^5ev^x6+;5~Ey-CBbj#DM~RcRe^@=mF*HM%CD}v6|>7JF2yDUVGkBU*xYQrCiEXL z;1JWpo(b`0Cf|Ux!mpfy`5kT$F)Jmbn!C*f#&59Xy?@P^a)xf%Nk*57w#nzVp>l;4S`uvA#h4tNh_TvI z>id9uoaoW4EBInTtAo+2xD)5iT8QIS=IwLbS%XI|~K(@qG>PKl#FI;9t4yr$oiqg$xN(B$zXb;p60!sGUewny1V5&^7vqymDig1A&yGJfeK(!A{A2yn~o*zkCjNTZr z-ymH!#Z{U)H|*t4!-$t{mu2aJW+wJFlCTqOlZ#3lXiAAmgh?gRc5Z@TTMUGYW*3J@ zk2;0TLBe%n^L!NZd)m=i5&R{$LK8b>X$O~!-`fm!ABrUP_b<+`@<=^i2K}U%dw-G6Y|E)4VL#c_MN;YCn!y(fs$eIQKBYvSfHDlvD32Ke zPrQGCT-8LISDTbWNpYunM#Bz4aG|VN>bCAPHtc^iFy{KH!`qa7+lg0Ox!dM7(<>m~ zYAoT6hcZsA^k?+ECH?n=c-5cqvj?8guC1mTPuPR7By_+gz8tLx-d`bs?;E#yZC{N08kezb~VIx+P+HRcX; zto|ZCT!yX4Wjs=@`n<-dqncmQgC%uZqM=Z-(@tdik<(=^-F&4C z=4y|%g$ZLnUOV;}x8M|=(-@!%O$x9maij4Ikrt=ySd6BS@4%Rf=v7?bI$%xhj6PJkgYR zud~evNuIkUXREsjt!W$%DJgFsG-pKkbjy_ zo{IH)PRTE2Y@EqtjkgC3W+M&QcD7|3m5fAtZcz9uD=`9+bC3;+K@f*-xP#u~QT{$d zQRV^BHFaPGcok~Wjyh7XDw;rRsV& zo$Ht9y|X}b{qsVPmP{cBD zdv5&|uAX_x!xR(-j--=aY>NA-2O z`W}8SN}`U~6rcweV@Zo;tCm=Z6!yMzaO*@%T$U!k$=F{!W-#_HY!2k=x^kQ)UCuI4 z-3Upkt zf9M(lq#Z9Ssaq4~<7t)m&4>5H{zh&%UXiAonNl%5sW@Iu;rZo!WyulR`6F+ksD;J; z!je!Xe#(_vOqZiqFN!LYFa?w}N@z99yUe0qm!B0t1s2UY)TwI)`SLGUy%?QhhzmvC z0xMD|G#%odVP^{FJkQbIE8CxKf13JpJj$bWDz-#xjiny>pKye|e>p*)-Xke=rA*-A zK??e;-o^2zz*JPHEKst|p4k`x=hU^;Ae(XzoO>k0fR0tJtvM0NdUJrLkdmJ47QVMt ztS$_@9kQC~x!5On)BvT_GBp>w^M#IB=t~L;F}>L*K)i z!|N1l0Tp14xk}H7*13%}>*wO0{OZrA`m(CyJ#RV628;J$kk;U4hKEk6t*=b7Ht9$d zJK9VuM9%^LY^YL|BwAAA*>A9805F)xqmO(~`+mt}=?9b}+1 zdeB1DgW{G*RR;#$3#E-iiwpt5LL?HD@KEH^oXfzMc1vYg8+p*Sr%Wf8SFZeBP}{!z z@V2_B9FqQ_GC`&-fLiLG@-SgXIAt5m<&13G2s1Vn-KEL2F-_HkZdjPsR!d#HI7QXb zhnXnTZ@MTUVWNuZB)VRg$ub$tU8!%B(B5>Yx%GnDD(X~JSlDrrex*LU=ac1Bxc80A zyL_@;vUAm`W8yWezJYGOvo8MsNi{!yHNAbU=u3atz5D+l;SKQ+g#Gt*PiwB_trAK( zl)oT5nX&%Qy@(OL!mj~l<1z%>|F^R$&`cvaqexW3tdXp4U|g~a2HZsq7_#Tsa*A!B zgWAT~Ijex%t@wbgEVa=+xov`mjn3^b?5;{hg~WW!Hkn)*w|#&fS+#iDP5q}lU3S78mdG_Asu zY0X-XYk3$~3G4$KR$Dc3tlI&JV#(6HXs4#DjD>fHkT?<c(O9k#mS=A9z%ra;8l_ve6`i~b3LZ8~Y$cNh(i2(Pf@EvyWu(H{tBT(SdC ztS{%N1Lwz?9HX#Ei1sH-ui_QcKky2oxs-Nw$H&l+x=(B6vMy~M%9dxCdN0bSB(*)8 zeVdwQsuSU*n%4ZCQkk~y*-#Y`_)qJ;xKs{3TVo88k>a(zo*%_|0<^ZJ!~H%_jM6Ke zL!B6sfHIrbawy7%Max{v1by3vHbre;pwK!C&sab~Z8b*^-Q~3`RqwWHyh#By!ftY^gj&IZZfakaMqid)jM~s3QyhR zhLs<1(4lPj8u9ros9r)Hf3>o1UGDMlFz$Ji^+wOU+~@dr;-YvO{ocUWZzC7K*qYv= zwO^W2K(|k_0vz@nTR!dwXNDZ(pO$yLNp2U6T{C{#9DaJ<#qbwL;F}(8U*VB;VX91{ z{4vf7aTjR%!@AMSbF1iS6Zm+ngqLsKk;!2+dnARF+%0*<_n>(ffs}Wy#9|f097n!n zy95Mnqt!`7FWsq|%pC|CXLcbFb#jIl|DIwGnVlRr_Qu(+Mu%nqvU%0L`alxqvLO(d z!nDDJYb_F`dXVZHgjN-}16z)|0{B{Iq(VDo;rg;v9khNfm=eSehZw} z^|Iw}5Mrxm=tNQm(4V)Z)tU=RFYn3<3+Ku#pizGZidQ!d0Gdmsmd;Y{<48-zZzwoQUBLg7q)#K%zz4G!$Rd@ti4@om=r<9YN zs9wFY=z)~7ZBAxA;p|H`4#>K!$93-gFDAJ@_rz=*K0@-AJEDG#wtKp~{qgqpVDoKA z)%6h8htSaKblVOlnqw@L@KfRFXdu8kr*AB`Ho_;R% z;_HaueTf5OxRv8oBlvNQx}r=Y=xS4z5 zdRt{_pC<36V`WtAHso1CY^IPSiwD&OH!z|YyJP1%6WR7y&UcFIIIuA=Cp1lt(@ofM zpra=cyRs`NKJ05%)JJ|MvRLdkqtkyTQlN4~D|YZsyjR)l5A*krQap^o>;-XYUiy%B zZVkVZE3ue2=;29tKeOf_cf@(7+mnp$58D#)FT~zx*Gsj5%t{BD&BH0t_jW{m>1J73JHd!m)J1WxgY~tlSr0 zo3KH~Q>BgtHC7ZPe_&S-7eBu-|2;&V{j>H+iLMKELw9+5xwv^CRa7?Um)rOM{2|kZ z9r3)0?w^Ps=5V;5&cm(%$j=HL3bQ`%WUzWUXAHWT18DmRA~e`DHu8#HU)ZcI*@PNn z*Yt@Vt{H(hD%;$heoy{d|`Yna-sT=-u|FO^8UoJyJ}M8+%VO**z(Y3UGe&dnpWGA))Irp$TC zT4nC|7xcBEM!kO*XyArj=#)0z!255v6eMQj4|$$;mKmlQ>M6fY%H}6;t?B4;=V;CN zdw54m-ZeNjaGjydf$?g`B9VbiQ1+@fQ_^dgFO?1W*-`MZjUD~M%Ux})B(!otk$SKc ziXo7`NX(n}Dk9?5JR@I=fK0-G<@IytU5$v}0-Y--M8^=Lqlq!m?>D@F$qi4D8y4Q5 zD!n(AA`G_5EVA6Vf|m5o^-|H-sTQ3}9<_cN0=ctDKgwm*iE4R-w`y6Opk;DEBI;V- zJcD2CMsJPzreg<#dL9Me)10Tb7=ox6Z#*T>*d;Fk-1)rVq7aO_l#>WRJr9kDZn!VV zVBLk!f5rSNC&ILl&lH;EwRW9xeMMS>#bYIQ0z!scwYk}l9$jsKpoksz+Tx)}x-P)& z{WV%_EM@v9pX4Pa(LM9JrWOJEF8Lr&_TJc;*ZVIFOY2(;bHKon3xL4P2DS9m7SM|P z7X|*1gOB9`#f!)l8vX=7-ZlWsyb6I_$%I{joF=$gaKda|8EbMpsmpHh^rh-{wP;mE zi)44FP2=JTm(|A}S9UF8C^sf|>*}O|3u&-)G#U6V+$aXI6bu|eC=;Y+E~J;Dx+LP# zNWm8wyxWcVf;nr&LK;mZB;ds&Dy>c^eqZvM#H2MTDXWtbSEq1xyX0qL+nNpCte+UP zhn(CEf%{V>07ll>QFGtt|8af?R(f1B@L||6ALlJ7Fl21_jk;kpX6pB-{EXb0zkQEU zUu-Jo$(!Vo7hbbMUAe-(@aFV;(0-OoX}|vXxH08-ES{e&vHYscWig}#j^is8)8#eG zHAFAMbs&6RY}bGR&N6~I7MVKvprJg$d-@^v7${Z84~<+c z%0Zb@xnjl*J19_Z5~&;=1AUH8P0lwCmIhXi;oBD)6q_6B)SDIdpNI zExC5KOs7#wkiy1R;gVP`BTi(1x%LFahstWalY95ZGl9BbPv9p>#-S8BN5t-W7@*^M z>DX(g&Wv}SIhj9kVtQAW?-!o6pmNNIFV|<$Q9J7ABdg4+>|OPmV_U5A;}+w0cuc_5 zOuJ*VSJ+AV#^x6u3D`PTUQ@oP-mhO4Su#G|?|Y*V3YlD*n(x# z;|MO%WV1(>ZJ8biraj9mmi^V?ZHI!|&J2P1plWPf`6FW7oUN&miY6l8!&cfYicnFMT*oGmwIyh===9X|@`Bha zJbGkio)7cvw>XL7pXzD&e~cL-AfJmrqxV&kF5inm83|{Gh<^DT?2IeI?nU*bPBz=u zMA50Lv5UpRSP>Az5wNKYAwlfcojQ|HxXU+lh6m6ssm1724J4MAWpAo3n>BOBx3e(r z+xM)E#r z6b7|%QuzH%8kbGslhNwKieZm@l5WH)WreE zy^p5&u(*bAqd>J`@A@HKMciktElhB56HyBpVRxq*q9OP;YbwHjs%cBHDhzI`Ilezl zKu>(Y08>aNazEKrU`p*zH9=k!)|A%fgQ4#Im;c7d2IAN-1hyjEVtV#z< zsk7SFy{`S0w-{L3HoJ;q#WR@{LuH+Vt1nq%liuj&W?%8T?^eu@su-T`PkCE`_l;^O zHq7szZz;B1%?qDf0jL=M4Ui{y(_H=Y7Fl0|)b~AzjIh;?%&ywBj`?Njo5i5KhcB7Z~iV zHUmp#e}zyvx_Srn9b&Zr%Tqlmg$ap_I>na8TC|?}2G&}fnjH}3D7G}D%ahTN*;bN< z2)tmW$l1DT$+yr1O-LTCTYwc;7cT`TiNb()o;IZ#Y3$0XW4pOlAagXSZ_^U4*pp6F z#kON=jH%Zk*<4pE-P~g`;i{)iookIPo7a5EcPOG$sG@AH(RE5!%?Vy5({>H+*)%m# zRoyUI-=X`Xzu~ILEul+07EAt@U=yY$s;eR$zf8L#bW5SWC~Y%uyt=k|vJtf|wlB9j zsi;`ORPdz?u_Q!7gdtqub-MD^^%T2pKrh`;-*0oa7?&urvAG2&y03ur?|$`wjdX4D@5`U&BpB!SPhXF_A{JOBX$Mg&Up44C zTr!Kw$4e!-VPT|47jj^}(xc>DkG~=k*d%GwsFg4E-_6z{Fr>Rp^a^tGv^lzeOJ|Oi z2}bGQceH2$(rFCT2Q>{5`KX$P?9Y7c~ZsSJBxZb>y$# zfrzkpU5R2K#ttYw*v;6ablz`nbBeb-Zi=lrQ#_o;dxGKsik4-WXnARVeC?NOk_QuF zS_bS01(|D?X@{Xxr$#HQ8pmrp@Bb}9OR{wJZ*(k&kS68{=_2{RYKm{BB#&yKXbRj_ z5LMDj6oyYExpjPj-0NPWs3oynOlz^b2;6g~DLu!6$y21cxU$q5gp?@n(%jrx<%_T@ zSNemPUfIafclPyG-|Fv|F(n*sm6Kf_U|aT{ofwbxYZKWzwlb~wpiocx5Dm2<=rG_p z6?kzLO{8B^6Fm9*aG*BxLwodA-<=|*Kt!IP*<#+cgI+u7+<>g((}=xzE3P?jQk__w ziSgS})DM$R-O_7X6iKXj8}JvKJwuv$N?_mow*D8jP%EAAlnDE8R2Dq)@^OKRYD0}y zZLF;>!v|pAM+KFa1q0=7`NWXkj!mCtp^J4fwp<@TRjj)`gPLDZB!{y>qJ1BCC~+R?9rHF37=M z5eea+3D>A4yiCDFj{K{Yh}1PY?T#5s9>L5|h)NqoMz9umnbY+s=06Xa*@}%p39++2 z*AXTcrVgc!U8*_4pKScK?boqStc?7*A%k)_zLPdgF#N6g11@SeqmE7a4(*uQv^ahYZYJ7@^Z=BeaV$wLfI1hY;HBx)t)R zqWvRnPo>2U#b`@j02I1YMc8ZnJFNc;~CbU6)1E2hA$hPlYM9MzIX}- zFFKs7d(W^akss8Ld@Q2kQAvC{F*g3{fb+(0=E=EHg4rwj@qa|b;^Xo-(i*TG+QSh) zTgtHqEHOED3Y!8eL8%3Qh0x7TX&!!v=^7(w4uwg^oIGino{I8G< z!mdVv2+L+`3`X_BXH#ZIPqxQkG`O?fh^s}90Y#?RK+5cOhu6loG#+S(U3+9*ZoldwdOEB-1Ro#b|U!=2Qh2gIp?u+)yM#WD^$AWXWw-kN8K6-wvCxJ?%+SI{F z0^7}9h1pr}%P+*#y{^7(%lG9~41PTgx83}{RDW|{4)EOVrX#h_B?B5P(ndHj?=f?Y zz=v0JsOnCo+yQzJzDn^v+{8$2?2gpn1kcAi{R=)!B9mYFOM^QyMzUNf&EI`uh{WVq zEDz|3-T7jHp2Tmcsk^Mg`u=qM118YyP9MRD3!DN!9HXP~d>NU9*r7u7yz4MW4HjGf zN5xC<(D#wE1S0M!E*K+ko+%dvE_>cSd*L=%8!=5=$?)Myb)~abbC4J?Qumza5oexD zOk8cfwLCb(|L1&=HmVY+!+1lKrvLe$pUPnU-!_B=rxv5&$P^^Ef`i&?O5q_ktD_X= zQeC;1f39ym`#e@{p0UaC-@nULhA8PP!0<#5!Td@&+mzkqZMXzQ`$ zk|ljwe3wg9F_SCGb^YjZ{7W>m%SX~BiJ!bNn=yg)Kn+#7B^K0j$jQ>u(tmhO)ykP( zWeuOvVKg!VHDU#EG(;N$_wLN%am192)j&9-aX)cZzY#J?;$nq1qXjG#r9osh^bS4h zSej}w&Y%6^m82g_n(^%WPaM<&=N(~dbO5%5Rewj6{acr z-Q_S(F0}~4S`LZBpFFJ#;}0lu@%bgodh~>Dub8hajg)Vk z-lIW8`hPm%3r-p}Cx;C5o@%K=P(f$cs(82>Cy%Wkyg<;MeWZtT`M26qELs3RO{@?y z_5frIyA3t>b7|>>nySV#?t$|s`t`VOo0x&%A`8Q~zA#?DG&k2?xICc4b=xJ3OfIx1 z!VdruS=0um6v92K12vav3Pu5?I2NR)_wLUYHufOj~{S% zgYEgpTO(>T<^SaNp;MKbq(%8!n_c&|15eO_WBaKpo!e$A>XE_GS!_p|fSLG!u09mz z$s$sI^isc;&>3L+J-6xdgt+gOJvcsWb`QaT-$)NKDFuI*{s%E(7os)Aa$kkd)rNdj z8-M?g+hr?=%~~Z_O?HCDtwRhx@K_U`OU0ielJ7y}EY)mZ@V8CB2bh4M|Fi#(K=993 znETm8sYP7b);k%{Q37RBSxwt$cX#GU35F<~qqs>xTEC2L4PV)PvIUV9OB%4f3wJ6x z%IeOCE@nQd>?v7&-M#XSFUmQ^UYRD)v|6)h3b87tsP0HKzW8~#WR>h~)J@*k4sA$X z6Lv>8=R z3u7A-J^U(_L$ouh+oqw3n8JvfG_#;yW7pJ)7+r>eo{27@n{4)Z>2_A;CJ>B|j0>@3 zR)`T|40}&h&%}UP^2k>w(%9=Y0&`88FhaznX}WDCD7P5_UrfjhwZ!J)VW3T!OorX- zBbx>6-|Rw>NWoKtFr)HH$3ziACQaXKF6*#x+45{&0r8LQHCRCue6Bxf)%YXOk~Lf@ zlgQ9Bn>1S2b5LIMPwm|OqU3t#x+hnmq(JBwZ|UBpzYg* z@2h4v6#JH!l6czqq&R$R_q)~&Ram*Qp2GI|;B2le4?h3cv9Tcfvu`zKser+y^Er$> zQ975B_KBOMqniP;h{suU{g~?0jUk?Omd`%PHo9ek(M###h^O(<4}XQpbN?NO!Kr?L zqwY}PklAUbOGqhVeyRMh`(U+ExvQr}si>%9!xU`7%*}vE2Wta|RwE-go!Bl2!-ZUf z_oHj?4WxK3nu{aiHQqd4=5}{-7PmYHN)|?#t6g9XI7_o~CDev=O$|{t4F%mqX%z|@ zDm{4SFO-x(qnLbXu3=Vj`f2cZ2pXG&LchZodaWE89i%}L`E;HlrW3P38up1xLw=1- zjs0JHzMB2|tNGEtN2Ny$Y!TU|u&%wnxl)SG(VZ3xnpp+`(aJ=-iqM`f;>m{=6l_%Z z?T552CI?Z6Th+eaR_YScODgCRZb|Md9v{F>htnBio`50y?VCwGy}ZpIIasVO_8r9c zy3uFZVm^-{ro;aQa`~@{b9vHo8m&a+vUuewbI}1#;_vi>lDPB+i5qeSJy(%p@w$Y} z(khySQM|yk1f89_DJe4ol&*p(KK|H`2$C|!kNt$6cWh}+UT;}Z7Tv< zv-1pv+4>6u3k7;X|8cVQ>v4*x9}>bttf~!JH{BB+6WV$$&@%W9j$Nm6Td5ZEFMaB@ z-75ldxh!GAp#fo^Pk#PRiOBTdZ&g|}P!1<%bCyn3#NSTzJnDSz@jYOTN#PGvjKCL= zVI8OoJn=_nu+Moi}Z_zSZWtHEbLz-$0kE)_V{8Y8t~@XwSm~E-4YK>DRI$g zlu*oD8CaSH0?bLZ)#V{NabA8amRVi}ep{TDF9LlQ-xahP<rasP(}=vU+Wa9P9W zgpR`*YJavL|CuE9V8%X1F(%LrbVj91Rr&$VS)k}A;ecd?El^|D;7inPNTQ0nFXNMj zEnUEk-|)Wv@34NZQgWju+TsLc2)QSD%#*;54C?GUG!vh5&FZuY`~V$q6imL9`s@c+rzj9c79+&exn?-l*5foPo z1*=xdAE%d(Tzi+qtB>&m5W>rp5)agFE@e)dt@t;?@5kpR&-+W9Nv(B-Bm97x^YfC! zo{34AG_+o4AdqKv@5L**2WEfm%e%{|N6)1ueyD@i>SqN0bP4O(kP`%bs@8J^$*)Lk!1&~;jmLX7Ms!vifC3xa1t&9zUz8iFV zFJo%3z^WK+ii*~{2HKXOWGX)$6RR!97Ka9j$WP;iY<>VG`JoJ}z`aX*4UFy@k;=*a zm00gR?50|2G^)+zqFSk_%t_mRn{yn-xn%k2k6Ue)ec7D+m~RMh%Q1f(d{!w)VIxc? zG{kJNsbODAy7l1#;7Zt61>L}~Yvf}m!W$AiYalyo8rX1)WV`!|A2C8xxf+lJ+6}3N@+DhZwj(e${*vVe7!)})O0RkB(X+@s281q~e6St+euc5EWN$sO=p=n^M zsH@kkONAj`79RfXGXuPL!{R`>-`t+q^oxYTF_m=nF516w%dKl$3i8Wq>k29gj5#U< zl*Vjsi5{e&AvALOY{_9BsSM+YDOy}l8#U#(wi(9t{1 zslJ+>kB7H<(-9p(tiyHL)vDTP!}ax9_m_VL>>cmdW4qlFo-zEr zAMmm^g!`p78_BtP*oQX~z{S{K#mtI=@%DkD(3^h1x5Z(t{Y|Zp^o5~c`Xir|oSc14 zr#F|(vy1UYL&O$>Nxz{q!WKcXvMg)*+V^Cz9O`+2DjjU?;VgbjVeV+CV5JO!%qXL2 z^hU+-27-z|=5Z9>_+w7{0jcNYP`WD79JRY{Xs^Yu1v)yf-f_y6mN(Q?0QVf|W2hte z-D0i@$1QUx8Uv0?I*cq+#aQa98m~O&CFBfprd7{5)zQeY%={ebyB!t5M} z8#pAs+bLmX@w1jzcxxDHlR7Xw5uQm$fdqK5W@LE~(dNuYnR3%f?4O`q^44ll^Zbsc zBn*ikElLs1H<5WIIT9wO;wgBMDoeWcO=MKe5y!rud>H0AlkqbqW9=LJz9Sic$TwTF zq^$L6_)kWNE##JUefW+0PP}-zI~tgp2BZriYJ#Ra(4aOjh854W_P(W&h8?0sHB%EgqFy1? z_^lpZdA5%u5fT)+5Kt0w4qH24p=O})B4;*}NSx}3CfnoB81qf4JyK06u){%wRaIm* zjZ45eY-d-<;t`X9+EpP|h|4(C7DE=;5-ekgC|PgA$O;Xwu9n61;A~}|D4w-Y_`!nunj=dUbFB@Eh@oo0J^Vei&l*vb`! zzBKRz#NLq_NG`3bFQ3>OHANTjlTzgY`nryITH%MK@&N;dM1)d}NWf@7LA}Dh|f`}c~Jv51-@WRQV*t$g7yA;_FxQ!9I z+5jHpn{|F!b32l!h4GoHAg$SyWN;dq{89ZKk&fzw>`g#WF#19BTA59zerQsc+sdmD zidkV7-w4*HjzTGynx{!ceiHO5rIy^vL=ZE0CZE*{3$XPNY4VT;l9#**56eulZTvv` z*f&ybNEYG6(BBvG5mrmI53O#|3bO>-OI3JP-+r| zZ1SOb95Z|wUmTtQM*?yY%b`i3QZvlZJ5*NdwAxx7o-ce-bSqR`y`5MZ$9g)3R>QolDv3oroKNHY_&T0LU#`RDzhAvvKXyi#mvuO zVO4bjT*D zdwwd`-Z1VNds?P3dMrwC9?CeiF998YM zl?J>X1(vOIt+2Q3LDczV3kJm!NyJOs{I+VU$Od~qc%8ArRkSRBn(=&045TFI)0i~& z(z{W!UblW-V@yCc<(TR-OsS_jAoITBaC{NMM|6Y-&n7MwLa`iuY82fS^Ahe9xS-f- z5H!^r?0Evn8QJP>P8eoDQlNfyjRw0UN3zRgh1 z0_XQs)`mqoDX%>5QQKpN>W{Y^@6SMWetn3UE22eL=sB7cw5byN@+4*+{ks3h}4U`oI zNbE1wKcRdSp(ZzX;Sg-$=FEltC%;pi^WzF zo99U!0XYd)$+EK+k=CA*3;YH-)23G+BBky0MtVnVr9(2)LTBsm`CYdBfY`KPw#8C> zwa~QW+;I^!0X^oF3hi^y(mEh;H&{K!B)9yj%7PoEbUZ(s437;Q+O|4!kqk})RaM|D zDRj=>gk^Gse84xDJV-XJ4&W$AdjCmRF8+T;^|ENI)(vZd$ifvzqn1Ptyu0u&@O2a}FJ`P? zoz38a*m}8m^wEV!6y*&>ao68ie@JLC*$-|>Es^V8R@1V;#&#-wv;sV@2vFoXw}Uf< zMjSU1fZY7=3-Y2Qv-AD`UI}r1f_^{6Z(+5z7%UXB_}I$Vi_VjnR5=P^qSFXBAI2%5 z1oPD9A8TtxxqqmV&sYCB?tjSnxP3#U<{_JD5=bkHEtOvr;S)(HXvI*pY(B<202&0$ zo+tBKIJ{Paf>JWa>AvFVLlkIjLNkXgwpdk&qDIGMw*yKn)@TK-tvnUh*?A(PbKodx z^Ga^{I zMb9frM}JU@iaSe5GikU(cvxraQPPtC(leSj{MNqf8u>z!PMfqDSdw1CpLV)%$(F5!v35O%6_YSeqcxaZ#uC1<{ zdAUajl1(gT5rV_XT-|*brwU?bE~4>i#c7X!c>gF51IyYcY3~fiTqDPo@=J+aWhKu5 zs%FcmbdDmwu&7?>cu-0RLl*7IW@uZ4`~il5(kuO8&6?W^NK)Xl{5-`$JJSL zayZs|jR>U*{2ZVO@SeSNt}=^Ig1&l@82xV$qtdG-*Ha@~gI|owjpIqc>XFiyaKM?z zk+*&euw`3YOWMV$fd!{NxS%v5$4kR$CGBY0uEVN zG%fArI?q?({J2H=+Whq-=AA@`ZS1xa?TvaTiFAQPzF6Mva9TQ@HNx`8-XFg@%&2i< z7ZS-k9y}O5={ULkCs#4K2$B6q6qq?oE{b%(taAjqr5ku=E=ysZ?1=a*;2?1&5?_~C z07XE$ztrtvH-a^;kM!i3xd;b>&4wLzxjM+Gqj~hL7!415zqGAvi|KhBF za9zw^?oR?_C+q;vC=iP7H)aZq1%*{Smw-?(V47!tlzgQU$mm_Le8MT~?-r_ft@jE? zanq=~gO(97$6Re9DcP@48y&*`@`k$%kCS7wOTyXwb^cVYu=qqcX-bVrn6ThjZ zf9lFsB*(|p{$%NiSE@(|TKA2jUzH9W*`IP%`8fZ7%wF@uz(cFox-2g|nsaXFDlljj z1hN+wZx!MO$_?x2Dulp51P5+*C=h~AiZ`JacSim&w~CaKRJ^LaY{hILiGhy%t`kk# zlt1Ji+LH5$>9o{c9f==bE@`_eb~zKao}S5W4)d_? zH;^};JsVeA`D+6x-Y<>SL(WWQ@-cB?hTgo#;hDivVX&>|)&|Jyapx;G7AkcX-KBzf z5;^VN&N4Y{3aK?u%6Oib_B*sL6^3v|h*YpPFhW}Ak|{++C7m}GRr`6Pt4Bu0)^&7_ ztr@d(`&-01u1nx4Ef;use4eMGl<(o8vUp1k^Kk0n0XWV=gBqpqp@mJsC-(W=sfO^B zp5nr=2m{}2ZD7>Rr*+yQ3c$a2zJVPlNw!BF9+*Y{D}(1nnXOSQ+}xJpiOT$;-K3ZW z#M_hpJ>On=d8utmUY^j&!zT%sPx|=$?Tj0XnZy9!+EG+i)c)co@NNI~)c74cg+_(e z9Pj`aB!ldpmDFLj%=R}wPx0h6Mssdh+N8(B*T4j6Z?7pxBdEc`!&fHp*UclBqYGkc zR&>6gPU3T`b4?nPzn-3C@RdbfnDW>?3372SB6&2@3D2NQL(CAN&|Xo>b@MU}kLmI* z3y|8ju*ep~e?PJ#Js%U7>;o5gQXr*M+yVE<*y_&M0~tl$6j<3!mcUsdN;EdMG{(*o zP4GQuO#h5+!bOPF`bzCH4dxN&UV{h(&)q`P=+|OUQU#uBtifruwOE>wL?Frd!YoUB zpc^T}VA~Phfp()bJME>@*1#CcbvEMPbVnjp4I=h{%USsi64e%ITcqR77xN4o92>%{ z%n*Tzmrw7!a;1}(3N5X4O6pp&GxmNU{tVNnNYw?_;=aHV@xsv$2d{_fpbXtu34;Y_47i3MfXk|pQ zrW7TS)*%bIQa;U!X@(@C-e2{l>$O}(tJz8X%30vDh9fq;uYao-0mi6`o5v92F5!1l zcLy=1Pxma*WqMv1sYws>Y6v zl?&%{DneHI5&YB;y2K>uqW!^=2b0gjx=8U&lxZxGDn=2!xh>!0j@8X ztj3G2I$PQ=ZyEDx>Z~^u7WaN(CY$*679_?0#iqjtLy63&p}Yi3@pvWFR#Mn!jlFH;3uG>X(N6c-2*_-S$c`PpR8JL?WDC^w<6 zG&=P$r>uA9v8=8S>6!zi1syjUZ}g^JT9Y^M=DYo_c7otvNlXyDS)wmlm zkyA-VHNv=qv>N*LvuvCGfce0tVv?VK*Kv2yd{23=4Wgd&4HF(nGhK*}jU}p&ZAlJo zy+`&IZxpzV3cJ6u7Wr%qtLkZ3tr48Y<`FXaQ1Z=7GrFScudBWu+MTy9bkuby|IpCJ z;C(na``-PYd#Aml%^yo*?T~cx?&n}?`k%jMY=R66q#EZ$$6!CHetN%07KY?in8n3y zX@WU24#jcQe-;~KF9g-#QgUFgk-BJij!y|0KtewW

TIWHPC<_9>em(Cvlhuf-BD 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 literal 0 HcmV?d00001 diff --git a/pr/672/docs/static.files/FiraSans-MediumItalic-ccf7e434.woff2 b/pr/672/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/672/docs/static.files/FiraSans-Regular-0fe48ade.woff2 b/pr/672/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/672/docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt b/pr/672/docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt new file mode 100644 index 000000000..4b3edc29e --- /dev/null +++ b/pr/672/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/672/docs/static.files/SourceCodePro-It-fc8b9304.ttf.woff2 b/pr/672/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/672/docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt b/pr/672/docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt new file mode 100644 index 000000000..0d2941e14 --- /dev/null +++ b/pr/672/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/672/docs/static.files/SourceCodePro-Regular-8badfe75.ttf.woff2 b/pr/672/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/672/docs/static.files/SourceSerif4-Bold-6d4fd4c0.ttf.woff2 b/pr/672/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/672/docs/static.files/SourceSerif4-It-ca3b17ed.ttf.woff2 b/pr/672/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/672/docs/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 b/pr/672/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/672/docs/static.files/SourceSerif4-Semibold-457a13ac.ttf.woff2 b/pr/672/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/672/docs/static.files/favicon-044be391.svg b/pr/672/docs/static.files/favicon-044be391.svg new file mode 100644 index 000000000..8b34b5119 --- /dev/null +++ b/pr/672/docs/static.files/favicon-044be391.svg @@ -0,0 +1,24 @@ + + + + + diff --git a/pr/672/docs/static.files/favicon-32x32-eab170b8.png b/pr/672/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/672/docs/static.files/normalize-9960930a.css b/pr/672/docs/static.files/normalize-9960930a.css new file mode 100644 index 000000000..469959f13 --- /dev/null +++ b/pr/672/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/672/docs/static.files/noscript-263c88ec.css b/pr/672/docs/static.files/noscript-263c88ec.css new file mode 100644 index 000000000..e41db77dc --- /dev/null +++ b/pr/672/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/672/docs/static.files/rust-logo-9a9549ea.svg b/pr/672/docs/static.files/rust-logo-9a9549ea.svg new file mode 100644 index 000000000..62424d8ff --- /dev/null +++ b/pr/672/docs/static.files/rust-logo-9a9549ea.svg @@ -0,0 +1,61 @@ + + + diff --git a/pr/672/docs/static.files/rustdoc-ca0dd0c4.css b/pr/672/docs/static.files/rustdoc-ca0dd0c4.css new file mode 100644 index 000000000..7e6df69c2 --- /dev/null +++ b/pr/672/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/672/docs/static.files/scrape-examples-2bbcccac.js b/pr/672/docs/static.files/scrape-examples-2bbcccac.js new file mode 100644 index 000000000..c90f297f9 --- /dev/null +++ b/pr/672/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/672/docs/static.files/search-8d3311b9.js b/pr/672/docs/static.files/search-8d3311b9.js new file mode 100644 index 000000000..5da8debd8 --- /dev/null +++ b/pr/672/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/672/docs/static.files/settings-c38705f0.js b/pr/672/docs/static.files/settings-c38705f0.js new file mode 100644 index 000000000..7e4939e5c --- /dev/null +++ b/pr/672/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/672/docs/static.files/src-script-813739b1.js b/pr/672/docs/static.files/src-script-813739b1.js new file mode 100644 index 000000000..bf546257c --- /dev/null +++ b/pr/672/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/672/docs/static.files/storage-e2aeef58.js b/pr/672/docs/static.files/storage-e2aeef58.js new file mode 100644 index 000000000..fb76ad5e9 --- /dev/null +++ b/pr/672/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/672/docs/static.files/stringdex-828709d0.js b/pr/672/docs/static.files/stringdex-828709d0.js new file mode 100644 index 000000000..b8268d530 --- /dev/null +++ b/pr/672/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":[646]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/clap_builder/derive/trait.Args.js b/pr/672/docs/trait.impl/clap_builder/derive/trait.Args.js new file mode 100644 index 000000000..7e749e34d --- /dev/null +++ b/pr/672/docs/trait.impl/clap_builder/derive/trait.Args.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Args for Opt"]]],["noq_perf",[["impl Args for Cli"]]],["perf",[["impl Args for Opt"],["impl Args for Opt"],["impl Args for CommonOpt"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[116,126,368]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js b/pr/672/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js new file mode 100644 index 000000000..f69d622e6 --- /dev/null +++ b/pr/672/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl CommandFactory for Opt"]]],["noq_perf",[["impl CommandFactory for Cli"]]],["perf",[["impl CommandFactory for Opt"],["impl CommandFactory for Opt"],["impl CommandFactory for CommonOpt"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[126,136,398]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js b/pr/672/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js new file mode 100644 index 000000000..17dbf22a8 --- /dev/null +++ b/pr/672/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl FromArgMatches for Opt"]]],["noq_perf",[["impl FromArgMatches for Commands"],["impl FromArgMatches for Cli"]]],["perf",[["impl FromArgMatches for Opt"],["impl FromArgMatches for Opt"],["impl FromArgMatches for CommonOpt"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[126,266,398]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/clap_builder/derive/trait.Parser.js b/pr/672/docs/trait.impl/clap_builder/derive/trait.Parser.js new file mode 100644 index 000000000..f44edeb2c --- /dev/null +++ b/pr/672/docs/trait.impl/clap_builder/derive/trait.Parser.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Parser for Opt"]]],["noq_perf",[["impl Parser for Cli"]]],["perf",[["impl Parser for Opt"],["impl Parser for Opt"],["impl Parser for CommonOpt"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[118,128,374]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/clap_builder/derive/trait.Subcommand.js b/pr/672/docs/trait.impl/clap_builder/derive/trait.Subcommand.js new file mode 100644 index 000000000..c0c6a8437 --- /dev/null +++ b/pr/672/docs/trait.impl/clap_builder/derive/trait.Subcommand.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_perf",[["impl Subcommand for Commands"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[140]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/clap_builder/derive/trait.ValueEnum.js b/pr/672/docs/trait.impl/clap_builder/derive/trait.ValueEnum.js new file mode 100644 index 000000000..c39cd5dd7 --- /dev/null +++ b/pr/672/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/672/docs/trait.impl/core/clone/trait.Clone.js b/pr/672/docs/trait.impl/core/clone/trait.Clone.js new file mode 100644 index 000000000..d04b2dbcf --- /dev/null +++ b/pr/672/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 RuntimeType"],["impl Clone for Opt"]]],["noq",[["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 Closed"],["impl Clone for Connection"],["impl Clone for Endpoint"],["impl Clone for EndpointStats"],["impl Clone for Lagged"],["impl Clone for Path"],["impl Clone for WeakConnectionHandle"],["impl Clone for WeakPathHandle"]]],["noq_proto",[["impl Clone for ClosePathError"],["impl Clone for ConfigError"],["impl Clone for ConnectError"],["impl Clone for ConnectionError"],["impl Clone for Dir"],["impl Clone for EcnCodepoint"],["impl Clone for FinishError"],["impl Clone for FrameType"],["impl Clone for LongType"],["impl Clone for MaybeFrame"],["impl Clone for PacketDecodeError"],["impl Clone for PathAbandonReason"],["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 Bbr3"],["impl Clone for Bbr3Config"],["impl Clone for Cubic"],["impl Clone for CubicConfig"],["impl Clone for NewReno"],["impl Clone for NewRenoConfig"],["impl Clone for NoInitialCipherSuite"],["impl Clone for QuicClientConfig"],["impl Clone for QuicServerConfig"],["impl Clone for AckFrequencyConfig"],["impl Clone for ApplicationClose"],["impl Clone for ClientConfig"],["impl Clone for ClosedPath"],["impl Clone for ClosedStream"],["impl Clone for ConnectionClose"],["impl Clone for ConnectionHandle"],["impl Clone for ConnectionId"],["impl Clone for ConnectionStats"],["impl Clone for Datagram"],["impl Clone for DatagramInfo"],["impl Clone for EndpointConfig"],["impl Clone for FourTuple"],["impl Clone for FrameStats"],["impl Clone for IdleTimeout"],["impl Clone for InvalidCid"],["impl Clone for MtuDiscoveryConfig"],["impl Clone for MultipathNotNegotiated"],["impl Clone for PathId"],["impl Clone for PathStats"],["impl Clone for ProtectedInitialHeader"],["impl Clone for RandomConnectionIdGenerator"],["impl Clone for RttEstimator"],["impl Clone for ServerConfig"],["impl Clone for ShouldTransmit"],["impl Clone for StreamId"],["impl Clone for StreamInfo"],["impl Clone for TransportConfig"],["impl Clone for Error"],["impl Clone for Code"],["impl Clone for UdpStats"],["impl Clone for ValidationTokenConfig"],["impl Clone for VarInt"],["impl Clone for VarIntBoundsExceeded"],["impl Clone for TransportParameters"]]],["noq_udp",[["impl Clone for EcnCodepoint"],["impl Clone for RecvMeta"],["impl<'a> Clone for Transmit<'a>"]]],["perf",[["impl Clone for CongestionAlgorithm"],["impl Clone for NoProtectionClientConfig"],["impl Clone for NoProtectionServerConfig"],["impl Clone for OpenStreamStats"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[752,3799,18936,802,1214]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/cmp/trait.Eq.js b/pr/672/docs/trait.impl/core/cmp/trait.Eq.js new file mode 100644 index 000000000..5ba7db1b2 --- /dev/null +++ b/pr/672/docs/trait.impl/core/cmp/trait.Eq.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Eq for CipherSuite"]]],["noq",[["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"],["impl Eq for Lagged"],["impl Eq for WeakPathHandle"]]],["noq_proto",[["impl Eq for ClosePathError"],["impl Eq for ConfigError"],["impl Eq for ConnectError"],["impl Eq for ConnectionError"],["impl Eq for Dir"],["impl Eq for EcnCodepoint"],["impl Eq for FinishError"],["impl Eq for FrameType"],["impl Eq for LongType"],["impl Eq for MaybeFrame"],["impl Eq for PacketDecodeError"],["impl Eq for PathAbandonReason"],["impl Eq for PathError"],["impl Eq for PathEvent"],["impl Eq for PathStatus"],["impl Eq for ReadError"],["impl Eq for ReadableError"],["impl Eq for SendDatagramError"],["impl Eq for SetPathStatusError"],["impl Eq for Side"],["impl Eq for StreamEvent"],["impl Eq for WriteError"],["impl Eq for Error"],["impl Eq for UnexpectedEnd"],["impl Eq for ExportKeyingMaterialError"],["impl Eq for ApplicationClose"],["impl Eq for Chunk"],["impl Eq for ClosedPath"],["impl Eq for ClosedStream"],["impl Eq for ConnectionClose"],["impl Eq for ConnectionHandle"],["impl Eq for ConnectionId"],["impl Eq for DatagramInfo"],["impl Eq for FourTuple"],["impl Eq for FrameStats"],["impl Eq for IdleTimeout"],["impl Eq for InvalidFrameId"],["impl Eq for PathId"],["impl Eq for PathStats"],["impl Eq for ShouldTransmit"],["impl Eq for StreamId"],["impl Eq for StreamInfo"],["impl Eq for Error"],["impl Eq for Code"],["impl Eq for UdpStats"],["impl Eq for VarInt"],["impl Eq for VarIntBoundsExceeded"],["impl Eq for TransportParameters"]]],["noq_udp",[["impl Eq for EcnCodepoint"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[251,2167,12277,261]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/cmp/trait.Ord.js b/pr/672/docs/trait.impl/core/cmp/trait.Ord.js new file mode 100644 index 000000000..bc7d5f330 --- /dev/null +++ b/pr/672/docs/trait.impl/core/cmp/trait.Ord.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_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":[3281]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/cmp/trait.PartialEq.js b/pr/672/docs/trait.impl/core/cmp/trait.PartialEq.js new file mode 100644 index 000000000..76cb60170 --- /dev/null +++ b/pr/672/docs/trait.impl/core/cmp/trait.PartialEq.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl PartialEq for CipherSuite"]]],["noq",[["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"],["impl PartialEq for Lagged"],["impl PartialEq for Path"],["impl PartialEq for WeakPathHandle"]]],["noq_proto",[["impl PartialEq for ClosePathError"],["impl PartialEq for ConfigError"],["impl PartialEq for ConnectError"],["impl PartialEq for ConnectionError"],["impl PartialEq for Dir"],["impl PartialEq for EcnCodepoint"],["impl PartialEq for FinishError"],["impl PartialEq for FrameType"],["impl PartialEq for LongType"],["impl PartialEq for MaybeFrame"],["impl PartialEq for PacketDecodeError"],["impl PartialEq for PathAbandonReason"],["impl PartialEq for PathError"],["impl PartialEq for PathEvent"],["impl PartialEq for PathStatus"],["impl PartialEq for ReadError"],["impl PartialEq for ReadableError"],["impl PartialEq for SendDatagramError"],["impl PartialEq for SetPathStatusError"],["impl PartialEq for Side"],["impl PartialEq for StreamEvent"],["impl PartialEq for WriteError"],["impl PartialEq for Error"],["impl PartialEq for UnexpectedEnd"],["impl PartialEq for ExportKeyingMaterialError"],["impl PartialEq for ApplicationClose"],["impl PartialEq for Chunk"],["impl PartialEq for ClosedPath"],["impl PartialEq for ClosedStream"],["impl PartialEq for ConnectionClose"],["impl PartialEq for ConnectionHandle"],["impl PartialEq for ConnectionId"],["impl PartialEq for DatagramInfo"],["impl PartialEq for FourTuple"],["impl PartialEq for FrameStats"],["impl PartialEq for IdleTimeout"],["impl PartialEq for InvalidFrameId"],["impl PartialEq for PathId"],["impl PartialEq for PathStats"],["impl PartialEq for ShouldTransmit"],["impl PartialEq for StreamId"],["impl PartialEq for StreamInfo"],["impl PartialEq for Error"],["impl PartialEq for Code"],["impl PartialEq for UdpStats"],["impl PartialEq for VarInt"],["impl PartialEq for VarIntBoundsExceeded"],["impl PartialEq for TransportParameters"]]],["noq_udp",[["impl PartialEq for EcnCodepoint"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[272,2598,13285,282]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/cmp/trait.PartialOrd.js b/pr/672/docs/trait.impl/core/cmp/trait.PartialOrd.js new file mode 100644 index 000000000..8638b833c --- /dev/null +++ b/pr/672/docs/trait.impl/core/cmp/trait.PartialOrd.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_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":[3554]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/convert/trait.From.js b/pr/672/docs/trait.impl/core/convert/trait.From.js new file mode 100644 index 000000000..45480683e --- /dev/null +++ b/pr/672/docs/trait.impl/core/convert/trait.From.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[["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"]]],["noq_proto",[["impl From<SocketAddr> for FourTuple"],["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<T: Into<u32>> From<T> for PathId"]]],["noq_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":[5252,10504,599]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/convert/trait.TryFrom.js b/pr/672/docs/trait.impl/core/convert/trait.TryFrom.js new file mode 100644 index 000000000..99f782522 --- /dev/null +++ b/pr/672/docs/trait.impl/core/convert/trait.TryFrom.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_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":[3563]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/default/trait.Default.js b/pr/672/docs/trait.impl/core/default/trait.Default.js new file mode 100644 index 000000000..05bf32959 --- /dev/null +++ b/pr/672/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"]]],["noq",[["impl Default for EndpointStats"]]],["noq_proto",[["impl Default for PathStatus"],["impl Default for Bbr3Config"],["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"]]],["noq_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,281,6951,278,565]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/error/trait.Error.js b/pr/672/docs/trait.impl/core/error/trait.Error.js new file mode 100644 index 000000000..d2884bc31 --- /dev/null +++ b/pr/672/docs/trait.impl/core/error/trait.Error.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[["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 Lagged"],["impl Error for RetryError"]]],["noq_proto",[["impl Error for ClosePathError"],["impl Error for ConfigError"],["impl Error for ConnectError"],["impl Error for ConnectionError"],["impl Error for FinishError"],["impl Error for PacketDecodeError"],["impl Error for PathError"],["impl Error for ReadError"],["impl Error for ReadableError"],["impl Error for SendDatagramError"],["impl Error for SetPathStatusError"],["impl Error for WriteError"],["impl Error for Error"],["impl Error for Error"],["impl Error for UnexpectedEnd"],["impl Error for NoInitialCipherSuite"],["impl Error for ClosedPath"],["impl Error for ClosedStream"],["impl Error for InvalidFrameId"],["impl Error for MultipathNotNegotiated"],["impl Error for RetryError"],["impl Error for Error"],["impl Error for VarIntBoundsExceeded"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[2271,6331]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/fmt/trait.Debug.js b/pr/672/docs/trait.impl/core/fmt/trait.Debug.js new file mode 100644 index 000000000..cd2ac759b --- /dev/null +++ b/pr/672/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 RuntimeType"],["impl Debug for TransferResult"],["impl Debug for Opt"]]],["certificate",[["impl Debug for SkipServerVerification"]]],["noq",[["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 Closed"],["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 Lagged"],["impl Debug for NatTraversalUpdates"],["impl Debug for ObservedExternalAddr"],["impl Debug for Path"],["impl Debug for PathEvents"],["impl Debug for RecvStream"],["impl Debug for RetryError"],["impl Debug for SendStream"],["impl Debug for SmolRuntime"],["impl Debug for Stopped"],["impl Debug for TokioRuntime"],["impl Debug for UnorderedRecvStream"],["impl Debug for WeakConnectionHandle"],["impl Debug for WeakPathHandle"]]],["noq_proto",[["impl Debug for ClosePathError"],["impl Debug for ConfigError"],["impl Debug for ConnectError"],["impl Debug for ConnectionError"],["impl Debug for Dir"],["impl Debug for EcnCodepoint"],["impl Debug for Event"],["impl Debug for FinishError"],["impl Debug for FrameType"],["impl Debug for LongType"],["impl Debug for MaybeFrame"],["impl Debug for PacketDecodeError"],["impl Debug for PathAbandonReason"],["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 Bbr3"],["impl Debug for Bbr3Config"],["impl Debug for Cubic"],["impl Debug for CubicConfig"],["impl Debug for NewReno"],["impl Debug for NewRenoConfig"],["impl Debug for NoInitialCipherSuite"],["impl Debug for CryptoError"],["impl Debug for ExportKeyingMaterialError"],["impl Debug for UnsupportedVersion"],["impl Debug for AcceptError"],["impl Debug for AckFrequencyConfig"],["impl Debug for ApplicationClose"],["impl Debug for Chunk"],["impl Debug for ClientConfig"],["impl Debug for ClosedPath"],["impl Debug for ClosedStream"],["impl Debug for Connection"],["impl Debug for ConnectionClose"],["impl Debug for ConnectionEvent"],["impl Debug for ConnectionHandle"],["impl Debug for ConnectionId"],["impl Debug for ConnectionStats"],["impl Debug for Datagram"],["impl Debug for DatagramInfo"],["impl Debug for Endpoint"],["impl Debug for EndpointConfig"],["impl Debug for EndpointEvent"],["impl Debug for FourTuple"],["impl Debug for FrameStats"],["impl Debug for IdleTimeout"],["impl Debug for Incoming"],["impl Debug for InvalidCid"],["impl Debug for InvalidFrameId"],["impl Debug for MtuDiscoveryConfig"],["impl Debug for MultipathNotNegotiated"],["impl Debug for PartialDecode"],["impl Debug for PathId"],["impl Debug for PathStats"],["impl Debug for ProtectedInitialHeader"],["impl Debug for QlogFileFactory"],["impl Debug for RandomConnectionIdGenerator"],["impl Debug for RetryError"],["impl Debug for RttEstimator"],["impl Debug for ServerConfig"],["impl Debug for ShouldTransmit"],["impl Debug for StreamId"],["impl Debug for StreamInfo"],["impl Debug for TokenMemoryCache"],["impl Debug for Transmit"],["impl Debug for TransportConfig"],["impl Debug for Error"],["impl Debug for Code"],["impl Debug for UdpStats"],["impl Debug for ValidationTokenConfig"],["impl Debug for VarInt"],["impl Debug for VarIntBoundsExceeded"],["impl Debug for TransportParameters"]]],["noq_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":[1017,318,7053,23139,1058]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/fmt/trait.Display.js b/pr/672/docs/trait.impl/core/fmt/trait.Display.js new file mode 100644 index 000000000..98f74d37a --- /dev/null +++ b/pr/672/docs/trait.impl/core/fmt/trait.Display.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[["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 Lagged"],["impl Display for RetryError"]]],["noq_proto",[["impl Display for ClosePathError"],["impl Display for ConfigError"],["impl Display for ConnectError"],["impl Display for ConnectionError"],["impl Display for Dir"],["impl Display for FinishError"],["impl Display for FrameType"],["impl Display for MaybeFrame"],["impl Display for PacketDecodeError"],["impl Display for PathError"],["impl Display for ReadError"],["impl Display for ReadableError"],["impl Display for SendDatagramError"],["impl Display for SetPathStatusError"],["impl Display for WriteError"],["impl Display for Error"],["impl Display for Error"],["impl Display for UnexpectedEnd"],["impl Display for NoInitialCipherSuite"],["impl Display for ApplicationClose"],["impl Display for ClosedPath"],["impl Display for ClosedStream"],["impl Display for ConnectionClose"],["impl Display for ConnectionId"],["impl Display for Datagram"],["impl Display for DatagramInfo"],["impl Display for FourTuple"],["impl Display for InvalidFrameId"],["impl Display for MultipathNotNegotiated"],["impl Display for PathId"],["impl Display for RetryError"],["impl Display for StreamId"],["impl Display for StreamInfo"],["impl Display for Error"],["impl Display for Code"],["impl Display for VarInt"],["impl Display for VarIntBoundsExceeded"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[2289,10075]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/future/future/trait.Future.js b/pr/672/docs/trait.impl/core/future/future/trait.Future.js new file mode 100644 index 000000000..cde2d6911 --- /dev/null +++ b/pr/672/docs/trait.impl/core/future/future/trait.Future.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[["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 Stopped"],["impl Future for ZeroRttAccepted"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[3591]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/future/into_future/trait.IntoFuture.js b/pr/672/docs/trait.impl/core/future/into_future/trait.IntoFuture.js new file mode 100644 index 000000000..4067e9392 --- /dev/null +++ b/pr/672/docs/trait.impl/core/future/into_future/trait.IntoFuture.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[["impl IntoFuture for Incoming"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[297]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/hash/trait.Hash.js b/pr/672/docs/trait.impl/core/hash/trait.Hash.js new file mode 100644 index 000000000..702c41801 --- /dev/null +++ b/pr/672/docs/trait.impl/core/hash/trait.Hash.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_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":[3602]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/iter/traits/iterator/trait.Iterator.js b/pr/672/docs/trait.impl/core/iter/traits/iterator/trait.Iterator.js new file mode 100644 index 000000000..4436cdc9d --- /dev/null +++ b/pr/672/docs/trait.impl/core/iter/traits/iterator/trait.Iterator.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_proto",[["impl Iterator for IncomingAlpns"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[329]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/marker/trait.Copy.js b/pr/672/docs/trait.impl/core/marker/trait.Copy.js new file mode 100644 index 000000000..8b8375e3a --- /dev/null +++ b/pr/672/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 RuntimeType"],["impl Copy for Opt"]]],["noq",[["impl Copy for EndpointStats"]]],["noq_proto",[["impl Copy for Dir"],["impl Copy for EcnCodepoint"],["impl Copy for FrameType"],["impl Copy for LongType"],["impl Copy for MaybeFrame"],["impl Copy for PathError"],["impl Copy for PathStatus"],["impl Copy for Side"],["impl Copy for Error"],["impl Copy for UnexpectedEnd"],["impl Copy for ConnectionHandle"],["impl Copy for ConnectionId"],["impl Copy for DatagramInfo"],["impl Copy for FourTuple"],["impl Copy for FrameStats"],["impl Copy for IdleTimeout"],["impl Copy for InvalidCid"],["impl Copy for MultipathNotNegotiated"],["impl Copy for PathId"],["impl Copy for PathStats"],["impl Copy for RandomConnectionIdGenerator"],["impl Copy for RttEstimator"],["impl Copy for ShouldTransmit"],["impl Copy for StreamId"],["impl Copy for StreamInfo"],["impl Copy for Code"],["impl Copy for UdpStats"],["impl Copy for VarInt"],["impl Copy for VarIntBoundsExceeded"],["impl Copy for TransportParameters"]]],["noq_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":[749,270,8065,526,285]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/marker/trait.Freeze.js b/pr/672/docs/trait.impl/core/marker/trait.Freeze.js new file mode 100644 index 000000000..9e93aef8b --- /dev/null +++ b/pr/672/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 RuntimeType",1,["bench::RuntimeType"]],["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"]]]],["noq",[["impl !Freeze for ReadError",1,["noq::recv_stream::ReadError"]],["impl !Freeze for ReadExactError",1,["noq::recv_stream::ReadExactError"]],["impl !Freeze for ReadToEndError",1,["noq::recv_stream::ReadToEndError"]],["impl !Freeze for ResetError",1,["noq::recv_stream::ResetError"]],["impl !Freeze for SendDatagramError",1,["noq::connection::SendDatagramError"]],["impl !Freeze for StoppedError",1,["noq::send_stream::StoppedError"]],["impl !Freeze for WriteError",1,["noq::send_stream::WriteError"]],["impl !Freeze for Closed",1,["noq::connection::Closed"]],["impl !Freeze for Incoming",1,["noq::incoming::Incoming"]],["impl !Freeze for IncomingFuture",1,["noq::incoming::IncomingFuture"]],["impl !Freeze for Stopped",1,["noq::send_stream::Stopped"]],["impl Freeze for AddressDiscovery",1,["noq::path::AddressDiscovery"]],["impl Freeze for Connecting",1,["noq::connection::Connecting"]],["impl Freeze for Connection",1,["noq::connection::Connection"]],["impl Freeze for Endpoint",1,["noq::endpoint::Endpoint"]],["impl Freeze for EndpointStats",1,["noq::endpoint::EndpointStats"]],["impl Freeze for Lagged",1,["noq::event_stream::Lagged"]],["impl Freeze for NatTraversalUpdates",1,["noq::event_stream::NatTraversalUpdates"]],["impl Freeze for ObservedExternalAddr",1,["noq::event_stream::ObservedExternalAddr"]],["impl Freeze for OnClosed",1,["noq::connection::OnClosed"]],["impl Freeze for OpenPath",1,["noq::path::OpenPath"]],["impl Freeze for Path",1,["noq::path::Path"]],["impl Freeze for PathEvents",1,["noq::event_stream::PathEvents"]],["impl Freeze for RecvStream",1,["noq::recv_stream::RecvStream"]],["impl Freeze for RetryError",1,["noq::incoming::RetryError"]],["impl Freeze for SendStream",1,["noq::send_stream::SendStream"]],["impl Freeze for SmolRuntime",1,["noq::runtime::smol::SmolRuntime"]],["impl Freeze for TokioRuntime",1,["noq::runtime::tokio::TokioRuntime"]],["impl Freeze for UnorderedRecvStream",1,["noq::recv_stream::UnorderedRecvStream"]],["impl Freeze for WeakConnectionHandle",1,["noq::connection::WeakConnectionHandle"]],["impl Freeze for WeakPathHandle",1,["noq::path::WeakPathHandle"]],["impl Freeze for ZeroRttAccepted",1,["noq::connection::ZeroRttAccepted"]],["impl<'a> !Freeze for Accept<'a>",1,["noq::endpoint::Accept"]],["impl<'a> !Freeze for AcceptBi<'a>",1,["noq::connection::AcceptBi"]],["impl<'a> !Freeze for AcceptUni<'a>",1,["noq::connection::AcceptUni"]],["impl<'a> !Freeze for OpenBi<'a>",1,["noq::connection::OpenBi"]],["impl<'a> !Freeze for OpenUni<'a>",1,["noq::connection::OpenUni"]],["impl<'a> !Freeze for ReadDatagram<'a>",1,["noq::connection::ReadDatagram"]],["impl<'a> !Freeze for SendDatagram<'a>",1,["noq::connection::SendDatagram"]]]],["noq_perf",[["impl Freeze for Commands",1,["noq_perf::Commands"]],["impl Freeze for Cli",1,["noq_perf::Cli"]]]],["noq_proto",[["impl !Freeze for ConnectionError",1,["noq_proto::connection::ConnectionError"]],["impl !Freeze for DatagramEvent",1,["noq_proto::endpoint::DatagramEvent"]],["impl !Freeze for Event",1,["noq_proto::connection::Event"]],["impl !Freeze for SendDatagramError",1,["noq_proto::connection::datagrams::SendDatagramError"]],["impl !Freeze for AcceptError",1,["noq_proto::endpoint::AcceptError"]],["impl !Freeze for ApplicationClose",1,["noq_proto::frame::ApplicationClose"]],["impl !Freeze for BloomTokenLog",1,["noq_proto::bloom_token_log::BloomTokenLog"]],["impl !Freeze for Chunk",1,["noq_proto::connection::assembler::Chunk"]],["impl !Freeze for Connection",1,["noq_proto::connection::Connection"]],["impl !Freeze for ConnectionClose",1,["noq_proto::frame::ConnectionClose"]],["impl !Freeze for Datagram",1,["noq_proto::frame::Datagram"]],["impl !Freeze for DecryptedInitial",1,["noq_proto::endpoint::DecryptedInitial"]],["impl !Freeze for Incoming",1,["noq_proto::endpoint::Incoming"]],["impl !Freeze for IncomingAlpns",1,["noq_proto::endpoint::IncomingAlpns"]],["impl !Freeze for TokenMemoryCache",1,["noq_proto::token_memory_cache::TokenMemoryCache"]],["impl Freeze for ClosePathError",1,["noq_proto::connection::ClosePathError"]],["impl Freeze for ConfigError",1,["noq_proto::config::ConfigError"]],["impl Freeze for ConnectError",1,["noq_proto::endpoint::ConnectError"]],["impl Freeze for Dir",1,["noq_proto::Dir"]],["impl Freeze for EcnCodepoint",1,["noq_proto::shared::EcnCodepoint"]],["impl Freeze for FinishError",1,["noq_proto::connection::streams::send::FinishError"]],["impl Freeze for FrameType",1,["noq_proto::frame::FrameType"]],["impl Freeze for LongType",1,["noq_proto::packet::LongType"]],["impl Freeze for MaybeFrame",1,["noq_proto::frame::MaybeFrame"]],["impl Freeze for PacketDecodeError",1,["noq_proto::packet::PacketDecodeError"]],["impl Freeze for PathAbandonReason",1,["noq_proto::connection::paths::PathAbandonReason"]],["impl Freeze for PathError",1,["noq_proto::connection::PathError"]],["impl Freeze for PathEvent",1,["noq_proto::connection::paths::PathEvent"]],["impl Freeze for PathStatus",1,["noq_proto::connection::paths::PathStatus"]],["impl Freeze for ProtectedHeader",1,["noq_proto::packet::ProtectedHeader"]],["impl Freeze for ReadError",1,["noq_proto::connection::streams::recv::ReadError"]],["impl Freeze for ReadableError",1,["noq_proto::connection::streams::recv::ReadableError"]],["impl Freeze for SetPathStatusError",1,["noq_proto::connection::paths::SetPathStatusError"]],["impl Freeze for Side",1,["noq_proto::Side"]],["impl Freeze for StreamEvent",1,["noq_proto::connection::streams::StreamEvent"]],["impl Freeze for WriteError",1,["noq_proto::connection::streams::send::WriteError"]],["impl Freeze for Error",1,["noq_proto::n0_nat_traversal::Error"]],["impl Freeze for Event",1,["noq_proto::n0_nat_traversal::Event"]],["impl Freeze for Error",1,["noq_proto::transport_parameters::Error"]],["impl Freeze for UnexpectedEnd",1,["noq_proto::coding::UnexpectedEnd"]],["impl Freeze for Bbr3",1,["noq_proto::congestion::bbr3::Bbr3"]],["impl Freeze for Bbr3Config",1,["noq_proto::congestion::bbr3::Bbr3Config"]],["impl Freeze for ControllerMetrics",1,["noq_proto::congestion::ControllerMetrics"]],["impl Freeze for Cubic",1,["noq_proto::congestion::cubic::Cubic"]],["impl Freeze for CubicConfig",1,["noq_proto::congestion::cubic::CubicConfig"]],["impl Freeze for NewReno",1,["noq_proto::congestion::new_reno::NewReno"]],["impl Freeze for NewRenoConfig",1,["noq_proto::congestion::new_reno::NewRenoConfig"]],["impl Freeze for HandshakeData",1,["noq_proto::crypto::rustls::HandshakeData"]],["impl Freeze for NoInitialCipherSuite",1,["noq_proto::crypto::rustls::NoInitialCipherSuite"]],["impl Freeze for QuicClientConfig",1,["noq_proto::crypto::rustls::QuicClientConfig"]],["impl Freeze for QuicServerConfig",1,["noq_proto::crypto::rustls::QuicServerConfig"]],["impl Freeze for TlsSession",1,["noq_proto::crypto::rustls::TlsSession"]],["impl Freeze for CryptoError",1,["noq_proto::crypto::CryptoError"]],["impl Freeze for ExportKeyingMaterialError",1,["noq_proto::crypto::ExportKeyingMaterialError"]],["impl Freeze for Keys",1,["noq_proto::crypto::Keys"]],["impl Freeze for UnsupportedVersion",1,["noq_proto::crypto::UnsupportedVersion"]],["impl Freeze for AckFrequencyConfig",1,["noq_proto::config::transport::AckFrequencyConfig"]],["impl Freeze for ClientConfig",1,["noq_proto::config::ClientConfig"]],["impl Freeze for ClosedPath",1,["noq_proto::connection::paths::ClosedPath"]],["impl Freeze for ClosedStream",1,["noq_proto::connection::streams::ClosedStream"]],["impl Freeze for ConnectionEvent",1,["noq_proto::shared::ConnectionEvent"]],["impl Freeze for ConnectionHandle",1,["noq_proto::endpoint::ConnectionHandle"]],["impl Freeze for ConnectionId",1,["noq_proto::shared::ConnectionId"]],["impl Freeze for ConnectionStats",1,["noq_proto::connection::stats::ConnectionStats"]],["impl Freeze for DatagramInfo",1,["noq_proto::frame::DatagramInfo"]],["impl Freeze for Endpoint",1,["noq_proto::endpoint::Endpoint"]],["impl Freeze for EndpointConfig",1,["noq_proto::config::EndpointConfig"]],["impl Freeze for EndpointEvent",1,["noq_proto::shared::EndpointEvent"]],["impl Freeze for FixedLengthConnectionIdParser",1,["noq_proto::packet::FixedLengthConnectionIdParser"]],["impl Freeze for FourTuple",1,["noq_proto::FourTuple"]],["impl Freeze for FrameStats",1,["noq_proto::connection::stats::FrameStats"]],["impl Freeze for HashedConnectionIdGenerator",1,["noq_proto::cid_generator::HashedConnectionIdGenerator"]],["impl Freeze for IdleTimeout",1,["noq_proto::config::transport::IdleTimeout"]],["impl Freeze for InvalidCid",1,["noq_proto::cid_generator::InvalidCid"]],["impl Freeze for InvalidFrameId",1,["noq_proto::frame::InvalidFrameId"]],["impl Freeze for MtuDiscoveryConfig",1,["noq_proto::config::transport::MtuDiscoveryConfig"]],["impl Freeze for MultipathNotNegotiated",1,["noq_proto::connection::MultipathNotNegotiated"]],["impl Freeze for NoneTokenLog",1,["noq_proto::token::NoneTokenLog"]],["impl Freeze for NoneTokenStore",1,["noq_proto::token::NoneTokenStore"]],["impl Freeze for PartialDecode",1,["noq_proto::packet::PartialDecode"]],["impl Freeze for PathId",1,["noq_proto::connection::paths::PathId"]],["impl Freeze for PathStats",1,["noq_proto::connection::stats::PathStats"]],["impl Freeze for ProtectedInitialHeader",1,["noq_proto::packet::ProtectedInitialHeader"]],["impl Freeze for QlogConfig",1,["noq_proto::config::qlog::QlogConfig"]],["impl Freeze for QlogFileFactory",1,["noq_proto::config::qlog::QlogFileFactory"]],["impl Freeze for RandomConnectionIdGenerator",1,["noq_proto::cid_generator::RandomConnectionIdGenerator"]],["impl Freeze for RetryError",1,["noq_proto::endpoint::RetryError"]],["impl Freeze for RttEstimator",1,["noq_proto::connection::paths::RttEstimator"]],["impl Freeze for ServerConfig",1,["noq_proto::config::ServerConfig"]],["impl Freeze for ShouldTransmit",1,["noq_proto::connection::streams::ShouldTransmit"]],["impl Freeze for StdSystemTime",1,["noq_proto::config::StdSystemTime"]],["impl Freeze for StreamId",1,["noq_proto::StreamId"]],["impl Freeze for StreamInfo",1,["noq_proto::frame::StreamInfo"]],["impl Freeze for TokenReuseError",1,["noq_proto::token::TokenReuseError"]],["impl Freeze for Transmit",1,["noq_proto::Transmit"]],["impl Freeze for TransportConfig",1,["noq_proto::config::transport::TransportConfig"]],["impl Freeze for Error",1,["noq_proto::transport_error::Error"]],["impl Freeze for Code",1,["noq_proto::transport_error::Code"]],["impl Freeze for UdpStats",1,["noq_proto::connection::stats::UdpStats"]],["impl Freeze for ValidationTokenConfig",1,["noq_proto::config::ValidationTokenConfig"]],["impl Freeze for VarInt",1,["noq_proto::varint::VarInt"]],["impl Freeze for VarIntBoundsExceeded",1,["noq_proto::varint::VarIntBoundsExceeded"]],["impl Freeze for TransportParameters",1,["noq_proto::transport_parameters::TransportParameters"]],["impl<'a> Freeze for Chunks<'a>",1,["noq_proto::connection::streams::recv::Chunks"]],["impl<'a> Freeze for Datagrams<'a>",1,["noq_proto::connection::datagrams::Datagrams"]],["impl<'a> Freeze for RecvStream<'a>",1,["noq_proto::connection::streams::RecvStream"]],["impl<'a> Freeze for SendStream<'a>",1,["noq_proto::connection::streams::SendStream"]],["impl<'a> Freeze for Streams<'a>",1,["noq_proto::connection::streams::Streams"]],["impl<T> Freeze for KeyPair<T>
where\n T: Freeze,
",1,["noq_proto::crypto::KeyPair"]]]],["noq_udp",[["impl !Freeze for UdpSocketState",1,["noq_udp::imp::UdpSocketState"]],["impl Freeze for EcnCodepoint",1,["noq_udp::EcnCodepoint"]],["impl Freeze for RecvMeta",1,["noq_udp::RecvMeta"]],["impl<'a> Freeze for Transmit<'a>",1,["noq_udp::Transmit"]],["impl<'a> Freeze for UdpSockRef<'a>",1,["noq_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"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1749,297,369,11640,561,35558,1517,2824]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/marker/trait.Send.js b/pr/672/docs/trait.impl/core/marker/trait.Send.js new file mode 100644 index 000000000..d6bd494f2 --- /dev/null +++ b/pr/672/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 RuntimeType",1,["bench::RuntimeType"]],["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"]]]],["noq",[["impl Send for ReadError",1,["noq::recv_stream::ReadError"]],["impl Send for ReadExactError",1,["noq::recv_stream::ReadExactError"]],["impl Send for ReadToEndError",1,["noq::recv_stream::ReadToEndError"]],["impl Send for ResetError",1,["noq::recv_stream::ResetError"]],["impl Send for SendDatagramError",1,["noq::connection::SendDatagramError"]],["impl Send for StoppedError",1,["noq::send_stream::StoppedError"]],["impl Send for WriteError",1,["noq::send_stream::WriteError"]],["impl Send for AddressDiscovery",1,["noq::path::AddressDiscovery"]],["impl Send for Closed",1,["noq::connection::Closed"]],["impl Send for Connecting",1,["noq::connection::Connecting"]],["impl Send for Connection",1,["noq::connection::Connection"]],["impl Send for Endpoint",1,["noq::endpoint::Endpoint"]],["impl Send for EndpointStats",1,["noq::endpoint::EndpointStats"]],["impl Send for Incoming",1,["noq::incoming::Incoming"]],["impl Send for IncomingFuture",1,["noq::incoming::IncomingFuture"]],["impl Send for Lagged",1,["noq::event_stream::Lagged"]],["impl Send for NatTraversalUpdates",1,["noq::event_stream::NatTraversalUpdates"]],["impl Send for ObservedExternalAddr",1,["noq::event_stream::ObservedExternalAddr"]],["impl Send for OnClosed",1,["noq::connection::OnClosed"]],["impl Send for OpenPath",1,["noq::path::OpenPath"]],["impl Send for Path",1,["noq::path::Path"]],["impl Send for PathEvents",1,["noq::event_stream::PathEvents"]],["impl Send for RecvStream",1,["noq::recv_stream::RecvStream"]],["impl Send for RetryError",1,["noq::incoming::RetryError"]],["impl Send for SendStream",1,["noq::send_stream::SendStream"]],["impl Send for SmolRuntime",1,["noq::runtime::smol::SmolRuntime"]],["impl Send for Stopped",1,["noq::send_stream::Stopped"]],["impl Send for TokioRuntime",1,["noq::runtime::tokio::TokioRuntime"]],["impl Send for UnorderedRecvStream",1,["noq::recv_stream::UnorderedRecvStream"]],["impl Send for WeakConnectionHandle",1,["noq::connection::WeakConnectionHandle"]],["impl Send for WeakPathHandle",1,["noq::path::WeakPathHandle"]],["impl Send for ZeroRttAccepted",1,["noq::connection::ZeroRttAccepted"]],["impl<'a> Send for Accept<'a>",1,["noq::endpoint::Accept"]],["impl<'a> Send for AcceptBi<'a>",1,["noq::connection::AcceptBi"]],["impl<'a> Send for AcceptUni<'a>",1,["noq::connection::AcceptUni"]],["impl<'a> Send for OpenBi<'a>",1,["noq::connection::OpenBi"]],["impl<'a> Send for OpenUni<'a>",1,["noq::connection::OpenUni"]],["impl<'a> Send for ReadDatagram<'a>",1,["noq::connection::ReadDatagram"]],["impl<'a> Send for SendDatagram<'a>",1,["noq::connection::SendDatagram"]]]],["noq_perf",[["impl Send for Commands",1,["noq_perf::Commands"]],["impl Send for Cli",1,["noq_perf::Cli"]]]],["noq_proto",[["impl Send for ClosePathError",1,["noq_proto::connection::ClosePathError"]],["impl Send for ConfigError",1,["noq_proto::config::ConfigError"]],["impl Send for ConnectError",1,["noq_proto::endpoint::ConnectError"]],["impl Send for ConnectionError",1,["noq_proto::connection::ConnectionError"]],["impl Send for DatagramEvent",1,["noq_proto::endpoint::DatagramEvent"]],["impl Send for Dir",1,["noq_proto::Dir"]],["impl Send for EcnCodepoint",1,["noq_proto::shared::EcnCodepoint"]],["impl Send for Event",1,["noq_proto::connection::Event"]],["impl Send for FinishError",1,["noq_proto::connection::streams::send::FinishError"]],["impl Send for FrameType",1,["noq_proto::frame::FrameType"]],["impl Send for LongType",1,["noq_proto::packet::LongType"]],["impl Send for MaybeFrame",1,["noq_proto::frame::MaybeFrame"]],["impl Send for PacketDecodeError",1,["noq_proto::packet::PacketDecodeError"]],["impl Send for PathAbandonReason",1,["noq_proto::connection::paths::PathAbandonReason"]],["impl Send for PathError",1,["noq_proto::connection::PathError"]],["impl Send for PathEvent",1,["noq_proto::connection::paths::PathEvent"]],["impl Send for PathStatus",1,["noq_proto::connection::paths::PathStatus"]],["impl Send for ProtectedHeader",1,["noq_proto::packet::ProtectedHeader"]],["impl Send for ReadError",1,["noq_proto::connection::streams::recv::ReadError"]],["impl Send for ReadableError",1,["noq_proto::connection::streams::recv::ReadableError"]],["impl Send for SendDatagramError",1,["noq_proto::connection::datagrams::SendDatagramError"]],["impl Send for SetPathStatusError",1,["noq_proto::connection::paths::SetPathStatusError"]],["impl Send for Side",1,["noq_proto::Side"]],["impl Send for StreamEvent",1,["noq_proto::connection::streams::StreamEvent"]],["impl Send for WriteError",1,["noq_proto::connection::streams::send::WriteError"]],["impl Send for Error",1,["noq_proto::n0_nat_traversal::Error"]],["impl Send for Event",1,["noq_proto::n0_nat_traversal::Event"]],["impl Send for Error",1,["noq_proto::transport_parameters::Error"]],["impl Send for UnexpectedEnd",1,["noq_proto::coding::UnexpectedEnd"]],["impl Send for Bbr3",1,["noq_proto::congestion::bbr3::Bbr3"]],["impl Send for Bbr3Config",1,["noq_proto::congestion::bbr3::Bbr3Config"]],["impl Send for ControllerMetrics",1,["noq_proto::congestion::ControllerMetrics"]],["impl Send for Cubic",1,["noq_proto::congestion::cubic::Cubic"]],["impl Send for CubicConfig",1,["noq_proto::congestion::cubic::CubicConfig"]],["impl Send for NewReno",1,["noq_proto::congestion::new_reno::NewReno"]],["impl Send for NewRenoConfig",1,["noq_proto::congestion::new_reno::NewRenoConfig"]],["impl Send for HandshakeData",1,["noq_proto::crypto::rustls::HandshakeData"]],["impl Send for NoInitialCipherSuite",1,["noq_proto::crypto::rustls::NoInitialCipherSuite"]],["impl Send for QuicClientConfig",1,["noq_proto::crypto::rustls::QuicClientConfig"]],["impl Send for QuicServerConfig",1,["noq_proto::crypto::rustls::QuicServerConfig"]],["impl Send for TlsSession",1,["noq_proto::crypto::rustls::TlsSession"]],["impl Send for CryptoError",1,["noq_proto::crypto::CryptoError"]],["impl Send for ExportKeyingMaterialError",1,["noq_proto::crypto::ExportKeyingMaterialError"]],["impl Send for Keys",1,["noq_proto::crypto::Keys"]],["impl Send for UnsupportedVersion",1,["noq_proto::crypto::UnsupportedVersion"]],["impl Send for AcceptError",1,["noq_proto::endpoint::AcceptError"]],["impl Send for AckFrequencyConfig",1,["noq_proto::config::transport::AckFrequencyConfig"]],["impl Send for ApplicationClose",1,["noq_proto::frame::ApplicationClose"]],["impl Send for BloomTokenLog",1,["noq_proto::bloom_token_log::BloomTokenLog"]],["impl Send for Chunk",1,["noq_proto::connection::assembler::Chunk"]],["impl Send for ClientConfig",1,["noq_proto::config::ClientConfig"]],["impl Send for ClosedPath",1,["noq_proto::connection::paths::ClosedPath"]],["impl Send for ClosedStream",1,["noq_proto::connection::streams::ClosedStream"]],["impl Send for Connection",1,["noq_proto::connection::Connection"]],["impl Send for ConnectionClose",1,["noq_proto::frame::ConnectionClose"]],["impl Send for ConnectionEvent",1,["noq_proto::shared::ConnectionEvent"]],["impl Send for ConnectionHandle",1,["noq_proto::endpoint::ConnectionHandle"]],["impl Send for ConnectionId",1,["noq_proto::shared::ConnectionId"]],["impl Send for ConnectionStats",1,["noq_proto::connection::stats::ConnectionStats"]],["impl Send for Datagram",1,["noq_proto::frame::Datagram"]],["impl Send for DatagramInfo",1,["noq_proto::frame::DatagramInfo"]],["impl Send for DecryptedInitial",1,["noq_proto::endpoint::DecryptedInitial"]],["impl Send for Endpoint",1,["noq_proto::endpoint::Endpoint"]],["impl Send for EndpointConfig",1,["noq_proto::config::EndpointConfig"]],["impl Send for EndpointEvent",1,["noq_proto::shared::EndpointEvent"]],["impl Send for FixedLengthConnectionIdParser",1,["noq_proto::packet::FixedLengthConnectionIdParser"]],["impl Send for FourTuple",1,["noq_proto::FourTuple"]],["impl Send for FrameStats",1,["noq_proto::connection::stats::FrameStats"]],["impl Send for HashedConnectionIdGenerator",1,["noq_proto::cid_generator::HashedConnectionIdGenerator"]],["impl Send for IdleTimeout",1,["noq_proto::config::transport::IdleTimeout"]],["impl Send for Incoming",1,["noq_proto::endpoint::Incoming"]],["impl Send for IncomingAlpns",1,["noq_proto::endpoint::IncomingAlpns"]],["impl Send for InvalidCid",1,["noq_proto::cid_generator::InvalidCid"]],["impl Send for InvalidFrameId",1,["noq_proto::frame::InvalidFrameId"]],["impl Send for MtuDiscoveryConfig",1,["noq_proto::config::transport::MtuDiscoveryConfig"]],["impl Send for MultipathNotNegotiated",1,["noq_proto::connection::MultipathNotNegotiated"]],["impl Send for NoneTokenLog",1,["noq_proto::token::NoneTokenLog"]],["impl Send for NoneTokenStore",1,["noq_proto::token::NoneTokenStore"]],["impl Send for PartialDecode",1,["noq_proto::packet::PartialDecode"]],["impl Send for PathId",1,["noq_proto::connection::paths::PathId"]],["impl Send for PathStats",1,["noq_proto::connection::stats::PathStats"]],["impl Send for ProtectedInitialHeader",1,["noq_proto::packet::ProtectedInitialHeader"]],["impl Send for QlogConfig",1,["noq_proto::config::qlog::QlogConfig"]],["impl Send for QlogFileFactory",1,["noq_proto::config::qlog::QlogFileFactory"]],["impl Send for RandomConnectionIdGenerator",1,["noq_proto::cid_generator::RandomConnectionIdGenerator"]],["impl Send for RetryError",1,["noq_proto::endpoint::RetryError"]],["impl Send for RttEstimator",1,["noq_proto::connection::paths::RttEstimator"]],["impl Send for ServerConfig",1,["noq_proto::config::ServerConfig"]],["impl Send for ShouldTransmit",1,["noq_proto::connection::streams::ShouldTransmit"]],["impl Send for StdSystemTime",1,["noq_proto::config::StdSystemTime"]],["impl Send for StreamId",1,["noq_proto::StreamId"]],["impl Send for StreamInfo",1,["noq_proto::frame::StreamInfo"]],["impl Send for TokenMemoryCache",1,["noq_proto::token_memory_cache::TokenMemoryCache"]],["impl Send for TokenReuseError",1,["noq_proto::token::TokenReuseError"]],["impl Send for Transmit",1,["noq_proto::Transmit"]],["impl Send for TransportConfig",1,["noq_proto::config::transport::TransportConfig"]],["impl Send for Error",1,["noq_proto::transport_error::Error"]],["impl Send for Code",1,["noq_proto::transport_error::Code"]],["impl Send for UdpStats",1,["noq_proto::connection::stats::UdpStats"]],["impl Send for ValidationTokenConfig",1,["noq_proto::config::ValidationTokenConfig"]],["impl Send for VarInt",1,["noq_proto::varint::VarInt"]],["impl Send for VarIntBoundsExceeded",1,["noq_proto::varint::VarIntBoundsExceeded"]],["impl Send for TransportParameters",1,["noq_proto::transport_parameters::TransportParameters"]],["impl<'a> Send for Chunks<'a>",1,["noq_proto::connection::streams::recv::Chunks"]],["impl<'a> Send for Datagrams<'a>",1,["noq_proto::connection::datagrams::Datagrams"]],["impl<'a> Send for RecvStream<'a>",1,["noq_proto::connection::streams::RecvStream"]],["impl<'a> Send for SendStream<'a>",1,["noq_proto::connection::streams::SendStream"]],["impl<'a> Send for Streams<'a>",1,["noq_proto::connection::streams::Streams"]],["impl<T> Send for KeyPair<T>
where\n T: Send,
",1,["noq_proto::crypto::KeyPair"]]]],["noq_udp",[["impl Send for EcnCodepoint",1,["noq_udp::EcnCodepoint"]],["impl Send for RecvMeta",1,["noq_udp::RecvMeta"]],["impl Send for UdpSocketState",1,["noq_udp::imp::UdpSocketState"]],["impl<'a> Send for Transmit<'a>",1,["noq_udp::Transmit"]],["impl<'a> Send for UdpSockRef<'a>",1,["noq_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"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1713,291,363,11388,549,34883,1486,2769]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/marker/trait.StructuralPartialEq.js b/pr/672/docs/trait.impl/core/marker/trait.StructuralPartialEq.js new file mode 100644 index 000000000..0820ce5f1 --- /dev/null +++ b/pr/672/docs/trait.impl/core/marker/trait.StructuralPartialEq.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl StructuralPartialEq for CipherSuite"]]],["noq",[["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"],["impl StructuralPartialEq for Lagged"]]],["noq_proto",[["impl StructuralPartialEq for ClosePathError"],["impl StructuralPartialEq for ConfigError"],["impl StructuralPartialEq for ConnectError"],["impl StructuralPartialEq for ConnectionError"],["impl StructuralPartialEq for Dir"],["impl StructuralPartialEq for EcnCodepoint"],["impl StructuralPartialEq for FinishError"],["impl StructuralPartialEq for FrameType"],["impl StructuralPartialEq for LongType"],["impl StructuralPartialEq for MaybeFrame"],["impl StructuralPartialEq for PacketDecodeError"],["impl StructuralPartialEq for PathAbandonReason"],["impl StructuralPartialEq for PathError"],["impl StructuralPartialEq for PathEvent"],["impl StructuralPartialEq for PathStatus"],["impl StructuralPartialEq for ReadError"],["impl StructuralPartialEq for ReadableError"],["impl StructuralPartialEq for SendDatagramError"],["impl StructuralPartialEq for SetPathStatusError"],["impl StructuralPartialEq for Side"],["impl StructuralPartialEq for StreamEvent"],["impl StructuralPartialEq for WriteError"],["impl StructuralPartialEq for Error"],["impl StructuralPartialEq for UnexpectedEnd"],["impl StructuralPartialEq for ExportKeyingMaterialError"],["impl StructuralPartialEq for ApplicationClose"],["impl StructuralPartialEq for Chunk"],["impl StructuralPartialEq for ClosedPath"],["impl StructuralPartialEq for ClosedStream"],["impl StructuralPartialEq for ConnectionClose"],["impl StructuralPartialEq for ConnectionHandle"],["impl StructuralPartialEq for ConnectionId"],["impl StructuralPartialEq for DatagramInfo"],["impl StructuralPartialEq for FourTuple"],["impl StructuralPartialEq for FrameStats"],["impl StructuralPartialEq for IdleTimeout"],["impl StructuralPartialEq for InvalidFrameId"],["impl StructuralPartialEq for PathId"],["impl StructuralPartialEq for PathStats"],["impl StructuralPartialEq for ShouldTransmit"],["impl StructuralPartialEq for StreamId"],["impl StructuralPartialEq for StreamInfo"],["impl StructuralPartialEq for Code"],["impl StructuralPartialEq for UdpStats"],["impl StructuralPartialEq for VarInt"],["impl StructuralPartialEq for VarIntBoundsExceeded"],["impl StructuralPartialEq for TransportParameters"]]],["noq_udp",[["impl StructuralPartialEq for EcnCodepoint"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[308,2372,14702,318]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/marker/trait.Sync.js b/pr/672/docs/trait.impl/core/marker/trait.Sync.js new file mode 100644 index 000000000..a7ad5d61d --- /dev/null +++ b/pr/672/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 RuntimeType",1,["bench::RuntimeType"]],["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"]]]],["noq",[["impl Sync for ReadError",1,["noq::recv_stream::ReadError"]],["impl Sync for ReadExactError",1,["noq::recv_stream::ReadExactError"]],["impl Sync for ReadToEndError",1,["noq::recv_stream::ReadToEndError"]],["impl Sync for ResetError",1,["noq::recv_stream::ResetError"]],["impl Sync for SendDatagramError",1,["noq::connection::SendDatagramError"]],["impl Sync for StoppedError",1,["noq::send_stream::StoppedError"]],["impl Sync for WriteError",1,["noq::send_stream::WriteError"]],["impl Sync for AddressDiscovery",1,["noq::path::AddressDiscovery"]],["impl Sync for Closed",1,["noq::connection::Closed"]],["impl Sync for Connecting",1,["noq::connection::Connecting"]],["impl Sync for Connection",1,["noq::connection::Connection"]],["impl Sync for Endpoint",1,["noq::endpoint::Endpoint"]],["impl Sync for EndpointStats",1,["noq::endpoint::EndpointStats"]],["impl Sync for Incoming",1,["noq::incoming::Incoming"]],["impl Sync for IncomingFuture",1,["noq::incoming::IncomingFuture"]],["impl Sync for Lagged",1,["noq::event_stream::Lagged"]],["impl Sync for NatTraversalUpdates",1,["noq::event_stream::NatTraversalUpdates"]],["impl Sync for ObservedExternalAddr",1,["noq::event_stream::ObservedExternalAddr"]],["impl Sync for OnClosed",1,["noq::connection::OnClosed"]],["impl Sync for OpenPath",1,["noq::path::OpenPath"]],["impl Sync for Path",1,["noq::path::Path"]],["impl Sync for PathEvents",1,["noq::event_stream::PathEvents"]],["impl Sync for RecvStream",1,["noq::recv_stream::RecvStream"]],["impl Sync for RetryError",1,["noq::incoming::RetryError"]],["impl Sync for SendStream",1,["noq::send_stream::SendStream"]],["impl Sync for SmolRuntime",1,["noq::runtime::smol::SmolRuntime"]],["impl Sync for Stopped",1,["noq::send_stream::Stopped"]],["impl Sync for TokioRuntime",1,["noq::runtime::tokio::TokioRuntime"]],["impl Sync for UnorderedRecvStream",1,["noq::recv_stream::UnorderedRecvStream"]],["impl Sync for WeakConnectionHandle",1,["noq::connection::WeakConnectionHandle"]],["impl Sync for WeakPathHandle",1,["noq::path::WeakPathHandle"]],["impl Sync for ZeroRttAccepted",1,["noq::connection::ZeroRttAccepted"]],["impl<'a> Sync for Accept<'a>",1,["noq::endpoint::Accept"]],["impl<'a> Sync for AcceptBi<'a>",1,["noq::connection::AcceptBi"]],["impl<'a> Sync for AcceptUni<'a>",1,["noq::connection::AcceptUni"]],["impl<'a> Sync for OpenBi<'a>",1,["noq::connection::OpenBi"]],["impl<'a> Sync for OpenUni<'a>",1,["noq::connection::OpenUni"]],["impl<'a> Sync for ReadDatagram<'a>",1,["noq::connection::ReadDatagram"]],["impl<'a> Sync for SendDatagram<'a>",1,["noq::connection::SendDatagram"]]]],["noq_perf",[["impl Sync for Commands",1,["noq_perf::Commands"]],["impl Sync for Cli",1,["noq_perf::Cli"]]]],["noq_proto",[["impl Sync for ClosePathError",1,["noq_proto::connection::ClosePathError"]],["impl Sync for ConfigError",1,["noq_proto::config::ConfigError"]],["impl Sync for ConnectError",1,["noq_proto::endpoint::ConnectError"]],["impl Sync for ConnectionError",1,["noq_proto::connection::ConnectionError"]],["impl Sync for DatagramEvent",1,["noq_proto::endpoint::DatagramEvent"]],["impl Sync for Dir",1,["noq_proto::Dir"]],["impl Sync for EcnCodepoint",1,["noq_proto::shared::EcnCodepoint"]],["impl Sync for Event",1,["noq_proto::connection::Event"]],["impl Sync for FinishError",1,["noq_proto::connection::streams::send::FinishError"]],["impl Sync for FrameType",1,["noq_proto::frame::FrameType"]],["impl Sync for LongType",1,["noq_proto::packet::LongType"]],["impl Sync for MaybeFrame",1,["noq_proto::frame::MaybeFrame"]],["impl Sync for PacketDecodeError",1,["noq_proto::packet::PacketDecodeError"]],["impl Sync for PathAbandonReason",1,["noq_proto::connection::paths::PathAbandonReason"]],["impl Sync for PathError",1,["noq_proto::connection::PathError"]],["impl Sync for PathEvent",1,["noq_proto::connection::paths::PathEvent"]],["impl Sync for PathStatus",1,["noq_proto::connection::paths::PathStatus"]],["impl Sync for ProtectedHeader",1,["noq_proto::packet::ProtectedHeader"]],["impl Sync for ReadError",1,["noq_proto::connection::streams::recv::ReadError"]],["impl Sync for ReadableError",1,["noq_proto::connection::streams::recv::ReadableError"]],["impl Sync for SendDatagramError",1,["noq_proto::connection::datagrams::SendDatagramError"]],["impl Sync for SetPathStatusError",1,["noq_proto::connection::paths::SetPathStatusError"]],["impl Sync for Side",1,["noq_proto::Side"]],["impl Sync for StreamEvent",1,["noq_proto::connection::streams::StreamEvent"]],["impl Sync for WriteError",1,["noq_proto::connection::streams::send::WriteError"]],["impl Sync for Error",1,["noq_proto::n0_nat_traversal::Error"]],["impl Sync for Event",1,["noq_proto::n0_nat_traversal::Event"]],["impl Sync for Error",1,["noq_proto::transport_parameters::Error"]],["impl Sync for UnexpectedEnd",1,["noq_proto::coding::UnexpectedEnd"]],["impl Sync for Bbr3",1,["noq_proto::congestion::bbr3::Bbr3"]],["impl Sync for Bbr3Config",1,["noq_proto::congestion::bbr3::Bbr3Config"]],["impl Sync for ControllerMetrics",1,["noq_proto::congestion::ControllerMetrics"]],["impl Sync for Cubic",1,["noq_proto::congestion::cubic::Cubic"]],["impl Sync for CubicConfig",1,["noq_proto::congestion::cubic::CubicConfig"]],["impl Sync for NewReno",1,["noq_proto::congestion::new_reno::NewReno"]],["impl Sync for NewRenoConfig",1,["noq_proto::congestion::new_reno::NewRenoConfig"]],["impl Sync for HandshakeData",1,["noq_proto::crypto::rustls::HandshakeData"]],["impl Sync for NoInitialCipherSuite",1,["noq_proto::crypto::rustls::NoInitialCipherSuite"]],["impl Sync for QuicClientConfig",1,["noq_proto::crypto::rustls::QuicClientConfig"]],["impl Sync for QuicServerConfig",1,["noq_proto::crypto::rustls::QuicServerConfig"]],["impl Sync for TlsSession",1,["noq_proto::crypto::rustls::TlsSession"]],["impl Sync for CryptoError",1,["noq_proto::crypto::CryptoError"]],["impl Sync for ExportKeyingMaterialError",1,["noq_proto::crypto::ExportKeyingMaterialError"]],["impl Sync for Keys",1,["noq_proto::crypto::Keys"]],["impl Sync for UnsupportedVersion",1,["noq_proto::crypto::UnsupportedVersion"]],["impl Sync for AcceptError",1,["noq_proto::endpoint::AcceptError"]],["impl Sync for AckFrequencyConfig",1,["noq_proto::config::transport::AckFrequencyConfig"]],["impl Sync for ApplicationClose",1,["noq_proto::frame::ApplicationClose"]],["impl Sync for BloomTokenLog",1,["noq_proto::bloom_token_log::BloomTokenLog"]],["impl Sync for Chunk",1,["noq_proto::connection::assembler::Chunk"]],["impl Sync for ClientConfig",1,["noq_proto::config::ClientConfig"]],["impl Sync for ClosedPath",1,["noq_proto::connection::paths::ClosedPath"]],["impl Sync for ClosedStream",1,["noq_proto::connection::streams::ClosedStream"]],["impl Sync for Connection",1,["noq_proto::connection::Connection"]],["impl Sync for ConnectionClose",1,["noq_proto::frame::ConnectionClose"]],["impl Sync for ConnectionEvent",1,["noq_proto::shared::ConnectionEvent"]],["impl Sync for ConnectionHandle",1,["noq_proto::endpoint::ConnectionHandle"]],["impl Sync for ConnectionId",1,["noq_proto::shared::ConnectionId"]],["impl Sync for ConnectionStats",1,["noq_proto::connection::stats::ConnectionStats"]],["impl Sync for Datagram",1,["noq_proto::frame::Datagram"]],["impl Sync for DatagramInfo",1,["noq_proto::frame::DatagramInfo"]],["impl Sync for DecryptedInitial",1,["noq_proto::endpoint::DecryptedInitial"]],["impl Sync for Endpoint",1,["noq_proto::endpoint::Endpoint"]],["impl Sync for EndpointConfig",1,["noq_proto::config::EndpointConfig"]],["impl Sync for EndpointEvent",1,["noq_proto::shared::EndpointEvent"]],["impl Sync for FixedLengthConnectionIdParser",1,["noq_proto::packet::FixedLengthConnectionIdParser"]],["impl Sync for FourTuple",1,["noq_proto::FourTuple"]],["impl Sync for FrameStats",1,["noq_proto::connection::stats::FrameStats"]],["impl Sync for HashedConnectionIdGenerator",1,["noq_proto::cid_generator::HashedConnectionIdGenerator"]],["impl Sync for IdleTimeout",1,["noq_proto::config::transport::IdleTimeout"]],["impl Sync for Incoming",1,["noq_proto::endpoint::Incoming"]],["impl Sync for IncomingAlpns",1,["noq_proto::endpoint::IncomingAlpns"]],["impl Sync for InvalidCid",1,["noq_proto::cid_generator::InvalidCid"]],["impl Sync for InvalidFrameId",1,["noq_proto::frame::InvalidFrameId"]],["impl Sync for MtuDiscoveryConfig",1,["noq_proto::config::transport::MtuDiscoveryConfig"]],["impl Sync for MultipathNotNegotiated",1,["noq_proto::connection::MultipathNotNegotiated"]],["impl Sync for NoneTokenLog",1,["noq_proto::token::NoneTokenLog"]],["impl Sync for NoneTokenStore",1,["noq_proto::token::NoneTokenStore"]],["impl Sync for PartialDecode",1,["noq_proto::packet::PartialDecode"]],["impl Sync for PathId",1,["noq_proto::connection::paths::PathId"]],["impl Sync for PathStats",1,["noq_proto::connection::stats::PathStats"]],["impl Sync for ProtectedInitialHeader",1,["noq_proto::packet::ProtectedInitialHeader"]],["impl Sync for QlogConfig",1,["noq_proto::config::qlog::QlogConfig"]],["impl Sync for QlogFileFactory",1,["noq_proto::config::qlog::QlogFileFactory"]],["impl Sync for RandomConnectionIdGenerator",1,["noq_proto::cid_generator::RandomConnectionIdGenerator"]],["impl Sync for RetryError",1,["noq_proto::endpoint::RetryError"]],["impl Sync for RttEstimator",1,["noq_proto::connection::paths::RttEstimator"]],["impl Sync for ServerConfig",1,["noq_proto::config::ServerConfig"]],["impl Sync for ShouldTransmit",1,["noq_proto::connection::streams::ShouldTransmit"]],["impl Sync for StdSystemTime",1,["noq_proto::config::StdSystemTime"]],["impl Sync for StreamId",1,["noq_proto::StreamId"]],["impl Sync for StreamInfo",1,["noq_proto::frame::StreamInfo"]],["impl Sync for TokenMemoryCache",1,["noq_proto::token_memory_cache::TokenMemoryCache"]],["impl Sync for TokenReuseError",1,["noq_proto::token::TokenReuseError"]],["impl Sync for Transmit",1,["noq_proto::Transmit"]],["impl Sync for TransportConfig",1,["noq_proto::config::transport::TransportConfig"]],["impl Sync for Error",1,["noq_proto::transport_error::Error"]],["impl Sync for Code",1,["noq_proto::transport_error::Code"]],["impl Sync for UdpStats",1,["noq_proto::connection::stats::UdpStats"]],["impl Sync for ValidationTokenConfig",1,["noq_proto::config::ValidationTokenConfig"]],["impl Sync for VarInt",1,["noq_proto::varint::VarInt"]],["impl Sync for VarIntBoundsExceeded",1,["noq_proto::varint::VarIntBoundsExceeded"]],["impl Sync for TransportParameters",1,["noq_proto::transport_parameters::TransportParameters"]],["impl<'a> Sync for Chunks<'a>",1,["noq_proto::connection::streams::recv::Chunks"]],["impl<'a> Sync for Datagrams<'a>",1,["noq_proto::connection::datagrams::Datagrams"]],["impl<'a> Sync for RecvStream<'a>",1,["noq_proto::connection::streams::RecvStream"]],["impl<'a> Sync for SendStream<'a>",1,["noq_proto::connection::streams::SendStream"]],["impl<'a> Sync for Streams<'a>",1,["noq_proto::connection::streams::Streams"]],["impl<T> Sync for KeyPair<T>
where\n T: Sync,
",1,["noq_proto::crypto::KeyPair"]]]],["noq_udp",[["impl Sync for EcnCodepoint",1,["noq_udp::EcnCodepoint"]],["impl Sync for RecvMeta",1,["noq_udp::RecvMeta"]],["impl Sync for UdpSocketState",1,["noq_udp::imp::UdpSocketState"]],["impl<'a> Sync for Transmit<'a>",1,["noq_udp::Transmit"]],["impl<'a> Sync for UdpSockRef<'a>",1,["noq_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"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1713,291,363,11388,549,34883,1486,2769]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/marker/trait.Unpin.js b/pr/672/docs/trait.impl/core/marker/trait.Unpin.js new file mode 100644 index 000000000..9bb8b5955 --- /dev/null +++ b/pr/672/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 RuntimeType",1,["bench::RuntimeType"]],["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"]]]],["noq",[["impl Unpin for ReadError",1,["noq::recv_stream::ReadError"]],["impl Unpin for ReadExactError",1,["noq::recv_stream::ReadExactError"]],["impl Unpin for ReadToEndError",1,["noq::recv_stream::ReadToEndError"]],["impl Unpin for ResetError",1,["noq::recv_stream::ResetError"]],["impl Unpin for SendDatagramError",1,["noq::connection::SendDatagramError"]],["impl Unpin for StoppedError",1,["noq::send_stream::StoppedError"]],["impl Unpin for WriteError",1,["noq::send_stream::WriteError"]],["impl Unpin for AddressDiscovery",1,["noq::path::AddressDiscovery"]],["impl Unpin for Closed",1,["noq::connection::Closed"]],["impl Unpin for Connecting",1,["noq::connection::Connecting"]],["impl Unpin for Connection",1,["noq::connection::Connection"]],["impl Unpin for Endpoint",1,["noq::endpoint::Endpoint"]],["impl Unpin for EndpointStats",1,["noq::endpoint::EndpointStats"]],["impl Unpin for Incoming",1,["noq::incoming::Incoming"]],["impl Unpin for IncomingFuture",1,["noq::incoming::IncomingFuture"]],["impl Unpin for Lagged",1,["noq::event_stream::Lagged"]],["impl Unpin for NatTraversalUpdates",1,["noq::event_stream::NatTraversalUpdates"]],["impl Unpin for ObservedExternalAddr",1,["noq::event_stream::ObservedExternalAddr"]],["impl Unpin for OnClosed",1,["noq::connection::OnClosed"]],["impl Unpin for OpenPath",1,["noq::path::OpenPath"]],["impl Unpin for Path",1,["noq::path::Path"]],["impl Unpin for PathEvents",1,["noq::event_stream::PathEvents"]],["impl Unpin for RecvStream",1,["noq::recv_stream::RecvStream"]],["impl Unpin for RetryError",1,["noq::incoming::RetryError"]],["impl Unpin for SendStream",1,["noq::send_stream::SendStream"]],["impl Unpin for SmolRuntime",1,["noq::runtime::smol::SmolRuntime"]],["impl Unpin for TokioRuntime",1,["noq::runtime::tokio::TokioRuntime"]],["impl Unpin for UnorderedRecvStream",1,["noq::recv_stream::UnorderedRecvStream"]],["impl Unpin for WeakConnectionHandle",1,["noq::connection::WeakConnectionHandle"]],["impl Unpin for WeakPathHandle",1,["noq::path::WeakPathHandle"]],["impl Unpin for ZeroRttAccepted",1,["noq::connection::ZeroRttAccepted"]],["impl<'__pin> Unpin for Stopped
where\n PinnedFieldsOf<__Origin<'__pin>>: Unpin,
"],["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,
"]]],["noq_perf",[["impl Unpin for Commands",1,["noq_perf::Commands"]],["impl Unpin for Cli",1,["noq_perf::Cli"]]]],["noq_proto",[["impl Unpin for ClosePathError",1,["noq_proto::connection::ClosePathError"]],["impl Unpin for ConfigError",1,["noq_proto::config::ConfigError"]],["impl Unpin for ConnectError",1,["noq_proto::endpoint::ConnectError"]],["impl Unpin for ConnectionError",1,["noq_proto::connection::ConnectionError"]],["impl Unpin for DatagramEvent",1,["noq_proto::endpoint::DatagramEvent"]],["impl Unpin for Dir",1,["noq_proto::Dir"]],["impl Unpin for EcnCodepoint",1,["noq_proto::shared::EcnCodepoint"]],["impl Unpin for Event",1,["noq_proto::connection::Event"]],["impl Unpin for FinishError",1,["noq_proto::connection::streams::send::FinishError"]],["impl Unpin for FrameType",1,["noq_proto::frame::FrameType"]],["impl Unpin for LongType",1,["noq_proto::packet::LongType"]],["impl Unpin for MaybeFrame",1,["noq_proto::frame::MaybeFrame"]],["impl Unpin for PacketDecodeError",1,["noq_proto::packet::PacketDecodeError"]],["impl Unpin for PathAbandonReason",1,["noq_proto::connection::paths::PathAbandonReason"]],["impl Unpin for PathError",1,["noq_proto::connection::PathError"]],["impl Unpin for PathEvent",1,["noq_proto::connection::paths::PathEvent"]],["impl Unpin for PathStatus",1,["noq_proto::connection::paths::PathStatus"]],["impl Unpin for ProtectedHeader",1,["noq_proto::packet::ProtectedHeader"]],["impl Unpin for ReadError",1,["noq_proto::connection::streams::recv::ReadError"]],["impl Unpin for ReadableError",1,["noq_proto::connection::streams::recv::ReadableError"]],["impl Unpin for SendDatagramError",1,["noq_proto::connection::datagrams::SendDatagramError"]],["impl Unpin for SetPathStatusError",1,["noq_proto::connection::paths::SetPathStatusError"]],["impl Unpin for Side",1,["noq_proto::Side"]],["impl Unpin for StreamEvent",1,["noq_proto::connection::streams::StreamEvent"]],["impl Unpin for WriteError",1,["noq_proto::connection::streams::send::WriteError"]],["impl Unpin for Error",1,["noq_proto::n0_nat_traversal::Error"]],["impl Unpin for Event",1,["noq_proto::n0_nat_traversal::Event"]],["impl Unpin for Error",1,["noq_proto::transport_parameters::Error"]],["impl Unpin for UnexpectedEnd",1,["noq_proto::coding::UnexpectedEnd"]],["impl Unpin for Bbr3",1,["noq_proto::congestion::bbr3::Bbr3"]],["impl Unpin for Bbr3Config",1,["noq_proto::congestion::bbr3::Bbr3Config"]],["impl Unpin for ControllerMetrics",1,["noq_proto::congestion::ControllerMetrics"]],["impl Unpin for Cubic",1,["noq_proto::congestion::cubic::Cubic"]],["impl Unpin for CubicConfig",1,["noq_proto::congestion::cubic::CubicConfig"]],["impl Unpin for NewReno",1,["noq_proto::congestion::new_reno::NewReno"]],["impl Unpin for NewRenoConfig",1,["noq_proto::congestion::new_reno::NewRenoConfig"]],["impl Unpin for HandshakeData",1,["noq_proto::crypto::rustls::HandshakeData"]],["impl Unpin for NoInitialCipherSuite",1,["noq_proto::crypto::rustls::NoInitialCipherSuite"]],["impl Unpin for QuicClientConfig",1,["noq_proto::crypto::rustls::QuicClientConfig"]],["impl Unpin for QuicServerConfig",1,["noq_proto::crypto::rustls::QuicServerConfig"]],["impl Unpin for TlsSession",1,["noq_proto::crypto::rustls::TlsSession"]],["impl Unpin for CryptoError",1,["noq_proto::crypto::CryptoError"]],["impl Unpin for ExportKeyingMaterialError",1,["noq_proto::crypto::ExportKeyingMaterialError"]],["impl Unpin for Keys",1,["noq_proto::crypto::Keys"]],["impl Unpin for UnsupportedVersion",1,["noq_proto::crypto::UnsupportedVersion"]],["impl Unpin for AcceptError",1,["noq_proto::endpoint::AcceptError"]],["impl Unpin for AckFrequencyConfig",1,["noq_proto::config::transport::AckFrequencyConfig"]],["impl Unpin for ApplicationClose",1,["noq_proto::frame::ApplicationClose"]],["impl Unpin for BloomTokenLog",1,["noq_proto::bloom_token_log::BloomTokenLog"]],["impl Unpin for Chunk",1,["noq_proto::connection::assembler::Chunk"]],["impl Unpin for ClientConfig",1,["noq_proto::config::ClientConfig"]],["impl Unpin for ClosedPath",1,["noq_proto::connection::paths::ClosedPath"]],["impl Unpin for ClosedStream",1,["noq_proto::connection::streams::ClosedStream"]],["impl Unpin for Connection",1,["noq_proto::connection::Connection"]],["impl Unpin for ConnectionClose",1,["noq_proto::frame::ConnectionClose"]],["impl Unpin for ConnectionEvent",1,["noq_proto::shared::ConnectionEvent"]],["impl Unpin for ConnectionHandle",1,["noq_proto::endpoint::ConnectionHandle"]],["impl Unpin for ConnectionId",1,["noq_proto::shared::ConnectionId"]],["impl Unpin for ConnectionStats",1,["noq_proto::connection::stats::ConnectionStats"]],["impl Unpin for Datagram",1,["noq_proto::frame::Datagram"]],["impl Unpin for DatagramInfo",1,["noq_proto::frame::DatagramInfo"]],["impl Unpin for DecryptedInitial",1,["noq_proto::endpoint::DecryptedInitial"]],["impl Unpin for Endpoint",1,["noq_proto::endpoint::Endpoint"]],["impl Unpin for EndpointConfig",1,["noq_proto::config::EndpointConfig"]],["impl Unpin for EndpointEvent",1,["noq_proto::shared::EndpointEvent"]],["impl Unpin for FixedLengthConnectionIdParser",1,["noq_proto::packet::FixedLengthConnectionIdParser"]],["impl Unpin for FourTuple",1,["noq_proto::FourTuple"]],["impl Unpin for FrameStats",1,["noq_proto::connection::stats::FrameStats"]],["impl Unpin for HashedConnectionIdGenerator",1,["noq_proto::cid_generator::HashedConnectionIdGenerator"]],["impl Unpin for IdleTimeout",1,["noq_proto::config::transport::IdleTimeout"]],["impl Unpin for Incoming",1,["noq_proto::endpoint::Incoming"]],["impl Unpin for IncomingAlpns",1,["noq_proto::endpoint::IncomingAlpns"]],["impl Unpin for InvalidCid",1,["noq_proto::cid_generator::InvalidCid"]],["impl Unpin for InvalidFrameId",1,["noq_proto::frame::InvalidFrameId"]],["impl Unpin for MtuDiscoveryConfig",1,["noq_proto::config::transport::MtuDiscoveryConfig"]],["impl Unpin for MultipathNotNegotiated",1,["noq_proto::connection::MultipathNotNegotiated"]],["impl Unpin for NoneTokenLog",1,["noq_proto::token::NoneTokenLog"]],["impl Unpin for NoneTokenStore",1,["noq_proto::token::NoneTokenStore"]],["impl Unpin for PartialDecode",1,["noq_proto::packet::PartialDecode"]],["impl Unpin for PathId",1,["noq_proto::connection::paths::PathId"]],["impl Unpin for PathStats",1,["noq_proto::connection::stats::PathStats"]],["impl Unpin for ProtectedInitialHeader",1,["noq_proto::packet::ProtectedInitialHeader"]],["impl Unpin for QlogConfig",1,["noq_proto::config::qlog::QlogConfig"]],["impl Unpin for QlogFileFactory",1,["noq_proto::config::qlog::QlogFileFactory"]],["impl Unpin for RandomConnectionIdGenerator",1,["noq_proto::cid_generator::RandomConnectionIdGenerator"]],["impl Unpin for RetryError",1,["noq_proto::endpoint::RetryError"]],["impl Unpin for RttEstimator",1,["noq_proto::connection::paths::RttEstimator"]],["impl Unpin for ServerConfig",1,["noq_proto::config::ServerConfig"]],["impl Unpin for ShouldTransmit",1,["noq_proto::connection::streams::ShouldTransmit"]],["impl Unpin for StdSystemTime",1,["noq_proto::config::StdSystemTime"]],["impl Unpin for StreamId",1,["noq_proto::StreamId"]],["impl Unpin for StreamInfo",1,["noq_proto::frame::StreamInfo"]],["impl Unpin for TokenMemoryCache",1,["noq_proto::token_memory_cache::TokenMemoryCache"]],["impl Unpin for TokenReuseError",1,["noq_proto::token::TokenReuseError"]],["impl Unpin for Transmit",1,["noq_proto::Transmit"]],["impl Unpin for TransportConfig",1,["noq_proto::config::transport::TransportConfig"]],["impl Unpin for Error",1,["noq_proto::transport_error::Error"]],["impl Unpin for Code",1,["noq_proto::transport_error::Code"]],["impl Unpin for UdpStats",1,["noq_proto::connection::stats::UdpStats"]],["impl Unpin for ValidationTokenConfig",1,["noq_proto::config::ValidationTokenConfig"]],["impl Unpin for VarInt",1,["noq_proto::varint::VarInt"]],["impl Unpin for VarIntBoundsExceeded",1,["noq_proto::varint::VarIntBoundsExceeded"]],["impl Unpin for TransportParameters",1,["noq_proto::transport_parameters::TransportParameters"]],["impl<'a> Unpin for Chunks<'a>",1,["noq_proto::connection::streams::recv::Chunks"]],["impl<'a> Unpin for Datagrams<'a>",1,["noq_proto::connection::datagrams::Datagrams"]],["impl<'a> Unpin for RecvStream<'a>",1,["noq_proto::connection::streams::RecvStream"]],["impl<'a> Unpin for SendStream<'a>",1,["noq_proto::connection::streams::SendStream"]],["impl<'a> Unpin for Streams<'a>",1,["noq_proto::connection::streams::Streams"]],["impl<T> Unpin for KeyPair<T>
where\n T: Unpin,
",1,["noq_proto::crypto::KeyPair"]]]],["noq_udp",[["impl Unpin for EcnCodepoint",1,["noq_udp::EcnCodepoint"]],["impl Unpin for RecvMeta",1,["noq_udp::RecvMeta"]],["impl Unpin for UdpSocketState",1,["noq_udp::imp::UdpSocketState"]],["impl<'a> Unpin for Transmit<'a>",1,["noq_udp::Transmit"]],["impl<'a> Unpin for UdpSockRef<'a>",1,["noq_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"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1731,294,366,13113,555,35213,1501,2796]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/marker/trait.UnsafeUnpin.js b/pr/672/docs/trait.impl/core/marker/trait.UnsafeUnpin.js new file mode 100644 index 000000000..420fb8943 --- /dev/null +++ b/pr/672/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 RuntimeType",1,["bench::RuntimeType"]],["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"]]]],["noq",[["impl !UnsafeUnpin for Stopped",1,["noq::send_stream::Stopped"]],["impl UnsafeUnpin for ReadError",1,["noq::recv_stream::ReadError"]],["impl UnsafeUnpin for ReadExactError",1,["noq::recv_stream::ReadExactError"]],["impl UnsafeUnpin for ReadToEndError",1,["noq::recv_stream::ReadToEndError"]],["impl UnsafeUnpin for ResetError",1,["noq::recv_stream::ResetError"]],["impl UnsafeUnpin for SendDatagramError",1,["noq::connection::SendDatagramError"]],["impl UnsafeUnpin for StoppedError",1,["noq::send_stream::StoppedError"]],["impl UnsafeUnpin for WriteError",1,["noq::send_stream::WriteError"]],["impl UnsafeUnpin for AddressDiscovery",1,["noq::path::AddressDiscovery"]],["impl UnsafeUnpin for Closed",1,["noq::connection::Closed"]],["impl UnsafeUnpin for Connecting",1,["noq::connection::Connecting"]],["impl UnsafeUnpin for Connection",1,["noq::connection::Connection"]],["impl UnsafeUnpin for Endpoint",1,["noq::endpoint::Endpoint"]],["impl UnsafeUnpin for EndpointStats",1,["noq::endpoint::EndpointStats"]],["impl UnsafeUnpin for Incoming",1,["noq::incoming::Incoming"]],["impl UnsafeUnpin for IncomingFuture",1,["noq::incoming::IncomingFuture"]],["impl UnsafeUnpin for Lagged",1,["noq::event_stream::Lagged"]],["impl UnsafeUnpin for NatTraversalUpdates",1,["noq::event_stream::NatTraversalUpdates"]],["impl UnsafeUnpin for ObservedExternalAddr",1,["noq::event_stream::ObservedExternalAddr"]],["impl UnsafeUnpin for OnClosed",1,["noq::connection::OnClosed"]],["impl UnsafeUnpin for OpenPath",1,["noq::path::OpenPath"]],["impl UnsafeUnpin for Path",1,["noq::path::Path"]],["impl UnsafeUnpin for PathEvents",1,["noq::event_stream::PathEvents"]],["impl UnsafeUnpin for RecvStream",1,["noq::recv_stream::RecvStream"]],["impl UnsafeUnpin for RetryError",1,["noq::incoming::RetryError"]],["impl UnsafeUnpin for SendStream",1,["noq::send_stream::SendStream"]],["impl UnsafeUnpin for SmolRuntime",1,["noq::runtime::smol::SmolRuntime"]],["impl UnsafeUnpin for TokioRuntime",1,["noq::runtime::tokio::TokioRuntime"]],["impl UnsafeUnpin for UnorderedRecvStream",1,["noq::recv_stream::UnorderedRecvStream"]],["impl UnsafeUnpin for WeakConnectionHandle",1,["noq::connection::WeakConnectionHandle"]],["impl UnsafeUnpin for WeakPathHandle",1,["noq::path::WeakPathHandle"]],["impl UnsafeUnpin for ZeroRttAccepted",1,["noq::connection::ZeroRttAccepted"]],["impl<'a> !UnsafeUnpin for Accept<'a>",1,["noq::endpoint::Accept"]],["impl<'a> !UnsafeUnpin for AcceptBi<'a>",1,["noq::connection::AcceptBi"]],["impl<'a> !UnsafeUnpin for AcceptUni<'a>",1,["noq::connection::AcceptUni"]],["impl<'a> !UnsafeUnpin for OpenBi<'a>",1,["noq::connection::OpenBi"]],["impl<'a> !UnsafeUnpin for OpenUni<'a>",1,["noq::connection::OpenUni"]],["impl<'a> !UnsafeUnpin for ReadDatagram<'a>",1,["noq::connection::ReadDatagram"]],["impl<'a> !UnsafeUnpin for SendDatagram<'a>",1,["noq::connection::SendDatagram"]]]],["noq_perf",[["impl UnsafeUnpin for Commands",1,["noq_perf::Commands"]],["impl UnsafeUnpin for Cli",1,["noq_perf::Cli"]]]],["noq_proto",[["impl UnsafeUnpin for ClosePathError",1,["noq_proto::connection::ClosePathError"]],["impl UnsafeUnpin for ConfigError",1,["noq_proto::config::ConfigError"]],["impl UnsafeUnpin for ConnectError",1,["noq_proto::endpoint::ConnectError"]],["impl UnsafeUnpin for ConnectionError",1,["noq_proto::connection::ConnectionError"]],["impl UnsafeUnpin for DatagramEvent",1,["noq_proto::endpoint::DatagramEvent"]],["impl UnsafeUnpin for Dir",1,["noq_proto::Dir"]],["impl UnsafeUnpin for EcnCodepoint",1,["noq_proto::shared::EcnCodepoint"]],["impl UnsafeUnpin for Event",1,["noq_proto::connection::Event"]],["impl UnsafeUnpin for FinishError",1,["noq_proto::connection::streams::send::FinishError"]],["impl UnsafeUnpin for FrameType",1,["noq_proto::frame::FrameType"]],["impl UnsafeUnpin for LongType",1,["noq_proto::packet::LongType"]],["impl UnsafeUnpin for MaybeFrame",1,["noq_proto::frame::MaybeFrame"]],["impl UnsafeUnpin for PacketDecodeError",1,["noq_proto::packet::PacketDecodeError"]],["impl UnsafeUnpin for PathAbandonReason",1,["noq_proto::connection::paths::PathAbandonReason"]],["impl UnsafeUnpin for PathError",1,["noq_proto::connection::PathError"]],["impl UnsafeUnpin for PathEvent",1,["noq_proto::connection::paths::PathEvent"]],["impl UnsafeUnpin for PathStatus",1,["noq_proto::connection::paths::PathStatus"]],["impl UnsafeUnpin for ProtectedHeader",1,["noq_proto::packet::ProtectedHeader"]],["impl UnsafeUnpin for ReadError",1,["noq_proto::connection::streams::recv::ReadError"]],["impl UnsafeUnpin for ReadableError",1,["noq_proto::connection::streams::recv::ReadableError"]],["impl UnsafeUnpin for SendDatagramError",1,["noq_proto::connection::datagrams::SendDatagramError"]],["impl UnsafeUnpin for SetPathStatusError",1,["noq_proto::connection::paths::SetPathStatusError"]],["impl UnsafeUnpin for Side",1,["noq_proto::Side"]],["impl UnsafeUnpin for StreamEvent",1,["noq_proto::connection::streams::StreamEvent"]],["impl UnsafeUnpin for WriteError",1,["noq_proto::connection::streams::send::WriteError"]],["impl UnsafeUnpin for Error",1,["noq_proto::n0_nat_traversal::Error"]],["impl UnsafeUnpin for Event",1,["noq_proto::n0_nat_traversal::Event"]],["impl UnsafeUnpin for Error",1,["noq_proto::transport_parameters::Error"]],["impl UnsafeUnpin for UnexpectedEnd",1,["noq_proto::coding::UnexpectedEnd"]],["impl UnsafeUnpin for Bbr3",1,["noq_proto::congestion::bbr3::Bbr3"]],["impl UnsafeUnpin for Bbr3Config",1,["noq_proto::congestion::bbr3::Bbr3Config"]],["impl UnsafeUnpin for ControllerMetrics",1,["noq_proto::congestion::ControllerMetrics"]],["impl UnsafeUnpin for Cubic",1,["noq_proto::congestion::cubic::Cubic"]],["impl UnsafeUnpin for CubicConfig",1,["noq_proto::congestion::cubic::CubicConfig"]],["impl UnsafeUnpin for NewReno",1,["noq_proto::congestion::new_reno::NewReno"]],["impl UnsafeUnpin for NewRenoConfig",1,["noq_proto::congestion::new_reno::NewRenoConfig"]],["impl UnsafeUnpin for HandshakeData",1,["noq_proto::crypto::rustls::HandshakeData"]],["impl UnsafeUnpin for NoInitialCipherSuite",1,["noq_proto::crypto::rustls::NoInitialCipherSuite"]],["impl UnsafeUnpin for QuicClientConfig",1,["noq_proto::crypto::rustls::QuicClientConfig"]],["impl UnsafeUnpin for QuicServerConfig",1,["noq_proto::crypto::rustls::QuicServerConfig"]],["impl UnsafeUnpin for TlsSession",1,["noq_proto::crypto::rustls::TlsSession"]],["impl UnsafeUnpin for CryptoError",1,["noq_proto::crypto::CryptoError"]],["impl UnsafeUnpin for ExportKeyingMaterialError",1,["noq_proto::crypto::ExportKeyingMaterialError"]],["impl UnsafeUnpin for Keys",1,["noq_proto::crypto::Keys"]],["impl UnsafeUnpin for UnsupportedVersion",1,["noq_proto::crypto::UnsupportedVersion"]],["impl UnsafeUnpin for AcceptError",1,["noq_proto::endpoint::AcceptError"]],["impl UnsafeUnpin for AckFrequencyConfig",1,["noq_proto::config::transport::AckFrequencyConfig"]],["impl UnsafeUnpin for ApplicationClose",1,["noq_proto::frame::ApplicationClose"]],["impl UnsafeUnpin for BloomTokenLog",1,["noq_proto::bloom_token_log::BloomTokenLog"]],["impl UnsafeUnpin for Chunk",1,["noq_proto::connection::assembler::Chunk"]],["impl UnsafeUnpin for ClientConfig",1,["noq_proto::config::ClientConfig"]],["impl UnsafeUnpin for ClosedPath",1,["noq_proto::connection::paths::ClosedPath"]],["impl UnsafeUnpin for ClosedStream",1,["noq_proto::connection::streams::ClosedStream"]],["impl UnsafeUnpin for Connection",1,["noq_proto::connection::Connection"]],["impl UnsafeUnpin for ConnectionClose",1,["noq_proto::frame::ConnectionClose"]],["impl UnsafeUnpin for ConnectionEvent",1,["noq_proto::shared::ConnectionEvent"]],["impl UnsafeUnpin for ConnectionHandle",1,["noq_proto::endpoint::ConnectionHandle"]],["impl UnsafeUnpin for ConnectionId",1,["noq_proto::shared::ConnectionId"]],["impl UnsafeUnpin for ConnectionStats",1,["noq_proto::connection::stats::ConnectionStats"]],["impl UnsafeUnpin for Datagram",1,["noq_proto::frame::Datagram"]],["impl UnsafeUnpin for DatagramInfo",1,["noq_proto::frame::DatagramInfo"]],["impl UnsafeUnpin for DecryptedInitial",1,["noq_proto::endpoint::DecryptedInitial"]],["impl UnsafeUnpin for Endpoint",1,["noq_proto::endpoint::Endpoint"]],["impl UnsafeUnpin for EndpointConfig",1,["noq_proto::config::EndpointConfig"]],["impl UnsafeUnpin for EndpointEvent",1,["noq_proto::shared::EndpointEvent"]],["impl UnsafeUnpin for FixedLengthConnectionIdParser",1,["noq_proto::packet::FixedLengthConnectionIdParser"]],["impl UnsafeUnpin for FourTuple",1,["noq_proto::FourTuple"]],["impl UnsafeUnpin for FrameStats",1,["noq_proto::connection::stats::FrameStats"]],["impl UnsafeUnpin for HashedConnectionIdGenerator",1,["noq_proto::cid_generator::HashedConnectionIdGenerator"]],["impl UnsafeUnpin for IdleTimeout",1,["noq_proto::config::transport::IdleTimeout"]],["impl UnsafeUnpin for Incoming",1,["noq_proto::endpoint::Incoming"]],["impl UnsafeUnpin for IncomingAlpns",1,["noq_proto::endpoint::IncomingAlpns"]],["impl UnsafeUnpin for InvalidCid",1,["noq_proto::cid_generator::InvalidCid"]],["impl UnsafeUnpin for InvalidFrameId",1,["noq_proto::frame::InvalidFrameId"]],["impl UnsafeUnpin for MtuDiscoveryConfig",1,["noq_proto::config::transport::MtuDiscoveryConfig"]],["impl UnsafeUnpin for MultipathNotNegotiated",1,["noq_proto::connection::MultipathNotNegotiated"]],["impl UnsafeUnpin for NoneTokenLog",1,["noq_proto::token::NoneTokenLog"]],["impl UnsafeUnpin for NoneTokenStore",1,["noq_proto::token::NoneTokenStore"]],["impl UnsafeUnpin for PartialDecode",1,["noq_proto::packet::PartialDecode"]],["impl UnsafeUnpin for PathId",1,["noq_proto::connection::paths::PathId"]],["impl UnsafeUnpin for PathStats",1,["noq_proto::connection::stats::PathStats"]],["impl UnsafeUnpin for ProtectedInitialHeader",1,["noq_proto::packet::ProtectedInitialHeader"]],["impl UnsafeUnpin for QlogConfig",1,["noq_proto::config::qlog::QlogConfig"]],["impl UnsafeUnpin for QlogFileFactory",1,["noq_proto::config::qlog::QlogFileFactory"]],["impl UnsafeUnpin for RandomConnectionIdGenerator",1,["noq_proto::cid_generator::RandomConnectionIdGenerator"]],["impl UnsafeUnpin for RetryError",1,["noq_proto::endpoint::RetryError"]],["impl UnsafeUnpin for RttEstimator",1,["noq_proto::connection::paths::RttEstimator"]],["impl UnsafeUnpin for ServerConfig",1,["noq_proto::config::ServerConfig"]],["impl UnsafeUnpin for ShouldTransmit",1,["noq_proto::connection::streams::ShouldTransmit"]],["impl UnsafeUnpin for StdSystemTime",1,["noq_proto::config::StdSystemTime"]],["impl UnsafeUnpin for StreamId",1,["noq_proto::StreamId"]],["impl UnsafeUnpin for StreamInfo",1,["noq_proto::frame::StreamInfo"]],["impl UnsafeUnpin for TokenMemoryCache",1,["noq_proto::token_memory_cache::TokenMemoryCache"]],["impl UnsafeUnpin for TokenReuseError",1,["noq_proto::token::TokenReuseError"]],["impl UnsafeUnpin for Transmit",1,["noq_proto::Transmit"]],["impl UnsafeUnpin for TransportConfig",1,["noq_proto::config::transport::TransportConfig"]],["impl UnsafeUnpin for Error",1,["noq_proto::transport_error::Error"]],["impl UnsafeUnpin for Code",1,["noq_proto::transport_error::Code"]],["impl UnsafeUnpin for UdpStats",1,["noq_proto::connection::stats::UdpStats"]],["impl UnsafeUnpin for ValidationTokenConfig",1,["noq_proto::config::ValidationTokenConfig"]],["impl UnsafeUnpin for VarInt",1,["noq_proto::varint::VarInt"]],["impl UnsafeUnpin for VarIntBoundsExceeded",1,["noq_proto::varint::VarIntBoundsExceeded"]],["impl UnsafeUnpin for TransportParameters",1,["noq_proto::transport_parameters::TransportParameters"]],["impl<'a> UnsafeUnpin for Chunks<'a>",1,["noq_proto::connection::streams::recv::Chunks"]],["impl<'a> UnsafeUnpin for Datagrams<'a>",1,["noq_proto::connection::datagrams::Datagrams"]],["impl<'a> UnsafeUnpin for RecvStream<'a>",1,["noq_proto::connection::streams::RecvStream"]],["impl<'a> UnsafeUnpin for SendStream<'a>",1,["noq_proto::connection::streams::SendStream"]],["impl<'a> UnsafeUnpin for Streams<'a>",1,["noq_proto::connection::streams::Streams"]],["impl<T> UnsafeUnpin for KeyPair<T>
where\n T: UnsafeUnpin,
",1,["noq_proto::crypto::KeyPair"]]]],["noq_udp",[["impl UnsafeUnpin for EcnCodepoint",1,["noq_udp::EcnCodepoint"]],["impl UnsafeUnpin for RecvMeta",1,["noq_udp::RecvMeta"]],["impl UnsafeUnpin for UdpSocketState",1,["noq_udp::imp::UdpSocketState"]],["impl<'a> UnsafeUnpin for Transmit<'a>",1,["noq_udp::Transmit"]],["impl<'a> UnsafeUnpin for UdpSockRef<'a>",1,["noq_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"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[981,312,384,6638,591,21463,876,1671]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/ops/arith/trait.Add.js b/pr/672/docs/trait.impl/core/ops/arith/trait.Add.js new file mode 100644 index 000000000..f30f0983c --- /dev/null +++ b/pr/672/docs/trait.impl/core/ops/arith/trait.Add.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_proto",[["impl Add for FrameStats"],["impl Add for UdpStats"],["impl Add<PathStats> for ConnectionStats"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[945]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/ops/arith/trait.AddAssign.js b/pr/672/docs/trait.impl/core/ops/arith/trait.AddAssign.js new file mode 100644 index 000000000..d1477978a --- /dev/null +++ b/pr/672/docs/trait.impl/core/ops/arith/trait.AddAssign.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_proto",[["impl AddAssign for FrameStats"],["impl AddAssign for UdpStats"],["impl AddAssign<PathStats> for ConnectionStats"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[999]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/ops/bit/trait.Not.js b/pr/672/docs/trait.impl/core/ops/bit/trait.Not.js new file mode 100644 index 000000000..04d216539 --- /dev/null +++ b/pr/672/docs/trait.impl/core/ops/bit/trait.Not.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_proto",[["impl Not for Side"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[254]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/ops/deref/trait.Deref.js b/pr/672/docs/trait.impl/core/ops/deref/trait.Deref.js new file mode 100644 index 000000000..784e90059 --- /dev/null +++ b/pr/672/docs/trait.impl/core/ops/deref/trait.Deref.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_proto",[["impl Deref for ConnectionId"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[294]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/ops/deref/trait.DerefMut.js b/pr/672/docs/trait.impl/core/ops/deref/trait.DerefMut.js new file mode 100644 index 000000000..f4d7e6818 --- /dev/null +++ b/pr/672/docs/trait.impl/core/ops/deref/trait.DerefMut.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_proto",[["impl DerefMut for ConnectionId"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[303]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/ops/drop/trait.Drop.js b/pr/672/docs/trait.impl/core/ops/drop/trait.Drop.js new file mode 100644 index 000000000..b2059f4d1 --- /dev/null +++ b/pr/672/docs/trait.impl/core/ops/drop/trait.Drop.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[["impl Drop for Incoming"],["impl Drop for OnClosed"],["impl Drop for Path"],["impl Drop for RecvStream"],["impl Drop for SendStream"],["impl Drop for WeakPathHandle"]]],["noq_proto",[["impl Drop for Chunks<'_>"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1527,282]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/ops/index/trait.Index.js b/pr/672/docs/trait.impl/core/ops/index/trait.Index.js new file mode 100644 index 000000000..251efa4cd --- /dev/null +++ b/pr/672/docs/trait.impl/core/ops/index/trait.Index.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_proto",[["impl Index<ConnectionHandle> for Slab<ConnectionMeta>"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[340]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/ops/index/trait.IndexMut.js b/pr/672/docs/trait.impl/core/ops/index/trait.IndexMut.js new file mode 100644 index 000000000..9d47e6d3d --- /dev/null +++ b/pr/672/docs/trait.impl/core/ops/index/trait.IndexMut.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_proto",[["impl IndexMut<ConnectionHandle> for Slab<ConnectionMeta>"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[349]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js b/pr/672/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js new file mode 100644 index 000000000..8b824f855 --- /dev/null +++ b/pr/672/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 RuntimeType",1,["bench::RuntimeType"]],["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"]]]],["noq",[["impl !RefUnwindSafe for ReadError",1,["noq::recv_stream::ReadError"]],["impl !RefUnwindSafe for ReadExactError",1,["noq::recv_stream::ReadExactError"]],["impl !RefUnwindSafe for ReadToEndError",1,["noq::recv_stream::ReadToEndError"]],["impl !RefUnwindSafe for ResetError",1,["noq::recv_stream::ResetError"]],["impl !RefUnwindSafe for SendDatagramError",1,["noq::connection::SendDatagramError"]],["impl !RefUnwindSafe for StoppedError",1,["noq::send_stream::StoppedError"]],["impl !RefUnwindSafe for WriteError",1,["noq::send_stream::WriteError"]],["impl !RefUnwindSafe for AddressDiscovery",1,["noq::path::AddressDiscovery"]],["impl !RefUnwindSafe for Closed",1,["noq::connection::Closed"]],["impl !RefUnwindSafe for Connecting",1,["noq::connection::Connecting"]],["impl !RefUnwindSafe for Endpoint",1,["noq::endpoint::Endpoint"]],["impl !RefUnwindSafe for Incoming",1,["noq::incoming::Incoming"]],["impl !RefUnwindSafe for IncomingFuture",1,["noq::incoming::IncomingFuture"]],["impl !RefUnwindSafe for NatTraversalUpdates",1,["noq::event_stream::NatTraversalUpdates"]],["impl !RefUnwindSafe for ObservedExternalAddr",1,["noq::event_stream::ObservedExternalAddr"]],["impl !RefUnwindSafe for OnClosed",1,["noq::connection::OnClosed"]],["impl !RefUnwindSafe for OpenPath",1,["noq::path::OpenPath"]],["impl !RefUnwindSafe for PathEvents",1,["noq::event_stream::PathEvents"]],["impl !RefUnwindSafe for RetryError",1,["noq::incoming::RetryError"]],["impl !RefUnwindSafe for Stopped",1,["noq::send_stream::Stopped"]],["impl !RefUnwindSafe for ZeroRttAccepted",1,["noq::connection::ZeroRttAccepted"]],["impl RefUnwindSafe for Connection",1,["noq::connection::Connection"]],["impl RefUnwindSafe for EndpointStats",1,["noq::endpoint::EndpointStats"]],["impl RefUnwindSafe for Lagged",1,["noq::event_stream::Lagged"]],["impl RefUnwindSafe for Path",1,["noq::path::Path"]],["impl RefUnwindSafe for RecvStream",1,["noq::recv_stream::RecvStream"]],["impl RefUnwindSafe for SendStream",1,["noq::send_stream::SendStream"]],["impl RefUnwindSafe for SmolRuntime",1,["noq::runtime::smol::SmolRuntime"]],["impl RefUnwindSafe for TokioRuntime",1,["noq::runtime::tokio::TokioRuntime"]],["impl RefUnwindSafe for UnorderedRecvStream",1,["noq::recv_stream::UnorderedRecvStream"]],["impl RefUnwindSafe for WeakConnectionHandle",1,["noq::connection::WeakConnectionHandle"]],["impl RefUnwindSafe for WeakPathHandle",1,["noq::path::WeakPathHandle"]],["impl<'a> !RefUnwindSafe for Accept<'a>",1,["noq::endpoint::Accept"]],["impl<'a> !RefUnwindSafe for AcceptBi<'a>",1,["noq::connection::AcceptBi"]],["impl<'a> !RefUnwindSafe for AcceptUni<'a>",1,["noq::connection::AcceptUni"]],["impl<'a> !RefUnwindSafe for OpenBi<'a>",1,["noq::connection::OpenBi"]],["impl<'a> !RefUnwindSafe for OpenUni<'a>",1,["noq::connection::OpenUni"]],["impl<'a> !RefUnwindSafe for ReadDatagram<'a>",1,["noq::connection::ReadDatagram"]],["impl<'a> !RefUnwindSafe for SendDatagram<'a>",1,["noq::connection::SendDatagram"]]]],["noq_perf",[["impl RefUnwindSafe for Commands",1,["noq_perf::Commands"]],["impl RefUnwindSafe for Cli",1,["noq_perf::Cli"]]]],["noq_proto",[["impl !RefUnwindSafe for ConnectionError",1,["noq_proto::connection::ConnectionError"]],["impl !RefUnwindSafe for DatagramEvent",1,["noq_proto::endpoint::DatagramEvent"]],["impl !RefUnwindSafe for Event",1,["noq_proto::connection::Event"]],["impl !RefUnwindSafe for QuicClientConfig",1,["noq_proto::crypto::rustls::QuicClientConfig"]],["impl !RefUnwindSafe for QuicServerConfig",1,["noq_proto::crypto::rustls::QuicServerConfig"]],["impl !RefUnwindSafe for TlsSession",1,["noq_proto::crypto::rustls::TlsSession"]],["impl !RefUnwindSafe for Keys",1,["noq_proto::crypto::Keys"]],["impl !RefUnwindSafe for AcceptError",1,["noq_proto::endpoint::AcceptError"]],["impl !RefUnwindSafe for ClientConfig",1,["noq_proto::config::ClientConfig"]],["impl !RefUnwindSafe for Connection",1,["noq_proto::connection::Connection"]],["impl !RefUnwindSafe for Endpoint",1,["noq_proto::endpoint::Endpoint"]],["impl !RefUnwindSafe for EndpointConfig",1,["noq_proto::config::EndpointConfig"]],["impl !RefUnwindSafe for Incoming",1,["noq_proto::endpoint::Incoming"]],["impl !RefUnwindSafe for QlogConfig",1,["noq_proto::config::qlog::QlogConfig"]],["impl !RefUnwindSafe for RetryError",1,["noq_proto::endpoint::RetryError"]],["impl !RefUnwindSafe for ServerConfig",1,["noq_proto::config::ServerConfig"]],["impl !RefUnwindSafe for TransportConfig",1,["noq_proto::config::transport::TransportConfig"]],["impl !RefUnwindSafe for Error",1,["noq_proto::transport_error::Error"]],["impl !RefUnwindSafe for ValidationTokenConfig",1,["noq_proto::config::ValidationTokenConfig"]],["impl RefUnwindSafe for ClosePathError",1,["noq_proto::connection::ClosePathError"]],["impl RefUnwindSafe for ConfigError",1,["noq_proto::config::ConfigError"]],["impl RefUnwindSafe for ConnectError",1,["noq_proto::endpoint::ConnectError"]],["impl RefUnwindSafe for Dir",1,["noq_proto::Dir"]],["impl RefUnwindSafe for EcnCodepoint",1,["noq_proto::shared::EcnCodepoint"]],["impl RefUnwindSafe for FinishError",1,["noq_proto::connection::streams::send::FinishError"]],["impl RefUnwindSafe for FrameType",1,["noq_proto::frame::FrameType"]],["impl RefUnwindSafe for LongType",1,["noq_proto::packet::LongType"]],["impl RefUnwindSafe for MaybeFrame",1,["noq_proto::frame::MaybeFrame"]],["impl RefUnwindSafe for PacketDecodeError",1,["noq_proto::packet::PacketDecodeError"]],["impl RefUnwindSafe for PathAbandonReason",1,["noq_proto::connection::paths::PathAbandonReason"]],["impl RefUnwindSafe for PathError",1,["noq_proto::connection::PathError"]],["impl RefUnwindSafe for PathEvent",1,["noq_proto::connection::paths::PathEvent"]],["impl RefUnwindSafe for PathStatus",1,["noq_proto::connection::paths::PathStatus"]],["impl RefUnwindSafe for ProtectedHeader",1,["noq_proto::packet::ProtectedHeader"]],["impl RefUnwindSafe for ReadError",1,["noq_proto::connection::streams::recv::ReadError"]],["impl RefUnwindSafe for ReadableError",1,["noq_proto::connection::streams::recv::ReadableError"]],["impl RefUnwindSafe for SendDatagramError",1,["noq_proto::connection::datagrams::SendDatagramError"]],["impl RefUnwindSafe for SetPathStatusError",1,["noq_proto::connection::paths::SetPathStatusError"]],["impl RefUnwindSafe for Side",1,["noq_proto::Side"]],["impl RefUnwindSafe for StreamEvent",1,["noq_proto::connection::streams::StreamEvent"]],["impl RefUnwindSafe for WriteError",1,["noq_proto::connection::streams::send::WriteError"]],["impl RefUnwindSafe for Error",1,["noq_proto::n0_nat_traversal::Error"]],["impl RefUnwindSafe for Event",1,["noq_proto::n0_nat_traversal::Event"]],["impl RefUnwindSafe for Error",1,["noq_proto::transport_parameters::Error"]],["impl RefUnwindSafe for UnexpectedEnd",1,["noq_proto::coding::UnexpectedEnd"]],["impl RefUnwindSafe for Bbr3",1,["noq_proto::congestion::bbr3::Bbr3"]],["impl RefUnwindSafe for Bbr3Config",1,["noq_proto::congestion::bbr3::Bbr3Config"]],["impl RefUnwindSafe for ControllerMetrics",1,["noq_proto::congestion::ControllerMetrics"]],["impl RefUnwindSafe for Cubic",1,["noq_proto::congestion::cubic::Cubic"]],["impl RefUnwindSafe for CubicConfig",1,["noq_proto::congestion::cubic::CubicConfig"]],["impl RefUnwindSafe for NewReno",1,["noq_proto::congestion::new_reno::NewReno"]],["impl RefUnwindSafe for NewRenoConfig",1,["noq_proto::congestion::new_reno::NewRenoConfig"]],["impl RefUnwindSafe for HandshakeData",1,["noq_proto::crypto::rustls::HandshakeData"]],["impl RefUnwindSafe for NoInitialCipherSuite",1,["noq_proto::crypto::rustls::NoInitialCipherSuite"]],["impl RefUnwindSafe for CryptoError",1,["noq_proto::crypto::CryptoError"]],["impl RefUnwindSafe for ExportKeyingMaterialError",1,["noq_proto::crypto::ExportKeyingMaterialError"]],["impl RefUnwindSafe for UnsupportedVersion",1,["noq_proto::crypto::UnsupportedVersion"]],["impl RefUnwindSafe for AckFrequencyConfig",1,["noq_proto::config::transport::AckFrequencyConfig"]],["impl RefUnwindSafe for ApplicationClose",1,["noq_proto::frame::ApplicationClose"]],["impl RefUnwindSafe for BloomTokenLog",1,["noq_proto::bloom_token_log::BloomTokenLog"]],["impl RefUnwindSafe for Chunk",1,["noq_proto::connection::assembler::Chunk"]],["impl RefUnwindSafe for ClosedPath",1,["noq_proto::connection::paths::ClosedPath"]],["impl RefUnwindSafe for ClosedStream",1,["noq_proto::connection::streams::ClosedStream"]],["impl RefUnwindSafe for ConnectionClose",1,["noq_proto::frame::ConnectionClose"]],["impl RefUnwindSafe for ConnectionEvent",1,["noq_proto::shared::ConnectionEvent"]],["impl RefUnwindSafe for ConnectionHandle",1,["noq_proto::endpoint::ConnectionHandle"]],["impl RefUnwindSafe for ConnectionId",1,["noq_proto::shared::ConnectionId"]],["impl RefUnwindSafe for ConnectionStats",1,["noq_proto::connection::stats::ConnectionStats"]],["impl RefUnwindSafe for Datagram",1,["noq_proto::frame::Datagram"]],["impl RefUnwindSafe for DatagramInfo",1,["noq_proto::frame::DatagramInfo"]],["impl RefUnwindSafe for DecryptedInitial",1,["noq_proto::endpoint::DecryptedInitial"]],["impl RefUnwindSafe for EndpointEvent",1,["noq_proto::shared::EndpointEvent"]],["impl RefUnwindSafe for FixedLengthConnectionIdParser",1,["noq_proto::packet::FixedLengthConnectionIdParser"]],["impl RefUnwindSafe for FourTuple",1,["noq_proto::FourTuple"]],["impl RefUnwindSafe for FrameStats",1,["noq_proto::connection::stats::FrameStats"]],["impl RefUnwindSafe for HashedConnectionIdGenerator",1,["noq_proto::cid_generator::HashedConnectionIdGenerator"]],["impl RefUnwindSafe for IdleTimeout",1,["noq_proto::config::transport::IdleTimeout"]],["impl RefUnwindSafe for IncomingAlpns",1,["noq_proto::endpoint::IncomingAlpns"]],["impl RefUnwindSafe for InvalidCid",1,["noq_proto::cid_generator::InvalidCid"]],["impl RefUnwindSafe for InvalidFrameId",1,["noq_proto::frame::InvalidFrameId"]],["impl RefUnwindSafe for MtuDiscoveryConfig",1,["noq_proto::config::transport::MtuDiscoveryConfig"]],["impl RefUnwindSafe for MultipathNotNegotiated",1,["noq_proto::connection::MultipathNotNegotiated"]],["impl RefUnwindSafe for NoneTokenLog",1,["noq_proto::token::NoneTokenLog"]],["impl RefUnwindSafe for NoneTokenStore",1,["noq_proto::token::NoneTokenStore"]],["impl RefUnwindSafe for PartialDecode",1,["noq_proto::packet::PartialDecode"]],["impl RefUnwindSafe for PathId",1,["noq_proto::connection::paths::PathId"]],["impl RefUnwindSafe for PathStats",1,["noq_proto::connection::stats::PathStats"]],["impl RefUnwindSafe for ProtectedInitialHeader",1,["noq_proto::packet::ProtectedInitialHeader"]],["impl RefUnwindSafe for QlogFileFactory",1,["noq_proto::config::qlog::QlogFileFactory"]],["impl RefUnwindSafe for RandomConnectionIdGenerator",1,["noq_proto::cid_generator::RandomConnectionIdGenerator"]],["impl RefUnwindSafe for RttEstimator",1,["noq_proto::connection::paths::RttEstimator"]],["impl RefUnwindSafe for ShouldTransmit",1,["noq_proto::connection::streams::ShouldTransmit"]],["impl RefUnwindSafe for StdSystemTime",1,["noq_proto::config::StdSystemTime"]],["impl RefUnwindSafe for StreamId",1,["noq_proto::StreamId"]],["impl RefUnwindSafe for StreamInfo",1,["noq_proto::frame::StreamInfo"]],["impl RefUnwindSafe for TokenMemoryCache",1,["noq_proto::token_memory_cache::TokenMemoryCache"]],["impl RefUnwindSafe for TokenReuseError",1,["noq_proto::token::TokenReuseError"]],["impl RefUnwindSafe for Transmit",1,["noq_proto::Transmit"]],["impl RefUnwindSafe for Code",1,["noq_proto::transport_error::Code"]],["impl RefUnwindSafe for UdpStats",1,["noq_proto::connection::stats::UdpStats"]],["impl RefUnwindSafe for VarInt",1,["noq_proto::varint::VarInt"]],["impl RefUnwindSafe for VarIntBoundsExceeded",1,["noq_proto::varint::VarIntBoundsExceeded"]],["impl RefUnwindSafe for TransportParameters",1,["noq_proto::transport_parameters::TransportParameters"]],["impl<'a> !RefUnwindSafe for Datagrams<'a>",1,["noq_proto::connection::datagrams::Datagrams"]],["impl<'a> !RefUnwindSafe for SendStream<'a>",1,["noq_proto::connection::streams::SendStream"]],["impl<'a> !RefUnwindSafe for Streams<'a>",1,["noq_proto::connection::streams::Streams"]],["impl<'a> RefUnwindSafe for Chunks<'a>",1,["noq_proto::connection::streams::recv::Chunks"]],["impl<'a> RefUnwindSafe for RecvStream<'a>",1,["noq_proto::connection::streams::RecvStream"]],["impl<T> RefUnwindSafe for KeyPair<T>
where\n T: RefUnwindSafe,
",1,["noq_proto::crypto::KeyPair"]]]],["noq_udp",[["impl RefUnwindSafe for EcnCodepoint",1,["noq_udp::EcnCodepoint"]],["impl RefUnwindSafe for RecvMeta",1,["noq_udp::RecvMeta"]],["impl RefUnwindSafe for UdpSocketState",1,["noq_udp::imp::UdpSocketState"]],["impl<'a> RefUnwindSafe for Transmit<'a>",1,["noq_udp::Transmit"]],["impl<'a> RefUnwindSafe for UdpSockRef<'a>",1,["noq_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"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[2013,341,414,13366,649,40405,1736,3221]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js b/pr/672/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js new file mode 100644 index 000000000..2b78edca3 --- /dev/null +++ b/pr/672/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 RuntimeType",1,["bench::RuntimeType"]],["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"]]]],["noq",[["impl !UnwindSafe for ReadError",1,["noq::recv_stream::ReadError"]],["impl !UnwindSafe for ReadExactError",1,["noq::recv_stream::ReadExactError"]],["impl !UnwindSafe for ReadToEndError",1,["noq::recv_stream::ReadToEndError"]],["impl !UnwindSafe for ResetError",1,["noq::recv_stream::ResetError"]],["impl !UnwindSafe for SendDatagramError",1,["noq::connection::SendDatagramError"]],["impl !UnwindSafe for StoppedError",1,["noq::send_stream::StoppedError"]],["impl !UnwindSafe for WriteError",1,["noq::send_stream::WriteError"]],["impl !UnwindSafe for AddressDiscovery",1,["noq::path::AddressDiscovery"]],["impl !UnwindSafe for Closed",1,["noq::connection::Closed"]],["impl !UnwindSafe for Connecting",1,["noq::connection::Connecting"]],["impl !UnwindSafe for Endpoint",1,["noq::endpoint::Endpoint"]],["impl !UnwindSafe for Incoming",1,["noq::incoming::Incoming"]],["impl !UnwindSafe for IncomingFuture",1,["noq::incoming::IncomingFuture"]],["impl !UnwindSafe for NatTraversalUpdates",1,["noq::event_stream::NatTraversalUpdates"]],["impl !UnwindSafe for ObservedExternalAddr",1,["noq::event_stream::ObservedExternalAddr"]],["impl !UnwindSafe for OnClosed",1,["noq::connection::OnClosed"]],["impl !UnwindSafe for OpenPath",1,["noq::path::OpenPath"]],["impl !UnwindSafe for PathEvents",1,["noq::event_stream::PathEvents"]],["impl !UnwindSafe for RetryError",1,["noq::incoming::RetryError"]],["impl !UnwindSafe for Stopped",1,["noq::send_stream::Stopped"]],["impl !UnwindSafe for ZeroRttAccepted",1,["noq::connection::ZeroRttAccepted"]],["impl UnwindSafe for Connection",1,["noq::connection::Connection"]],["impl UnwindSafe for EndpointStats",1,["noq::endpoint::EndpointStats"]],["impl UnwindSafe for Lagged",1,["noq::event_stream::Lagged"]],["impl UnwindSafe for Path",1,["noq::path::Path"]],["impl UnwindSafe for RecvStream",1,["noq::recv_stream::RecvStream"]],["impl UnwindSafe for SendStream",1,["noq::send_stream::SendStream"]],["impl UnwindSafe for SmolRuntime",1,["noq::runtime::smol::SmolRuntime"]],["impl UnwindSafe for TokioRuntime",1,["noq::runtime::tokio::TokioRuntime"]],["impl UnwindSafe for UnorderedRecvStream",1,["noq::recv_stream::UnorderedRecvStream"]],["impl UnwindSafe for WeakConnectionHandle",1,["noq::connection::WeakConnectionHandle"]],["impl UnwindSafe for WeakPathHandle",1,["noq::path::WeakPathHandle"]],["impl<'a> !UnwindSafe for Accept<'a>",1,["noq::endpoint::Accept"]],["impl<'a> !UnwindSafe for AcceptBi<'a>",1,["noq::connection::AcceptBi"]],["impl<'a> !UnwindSafe for AcceptUni<'a>",1,["noq::connection::AcceptUni"]],["impl<'a> !UnwindSafe for OpenBi<'a>",1,["noq::connection::OpenBi"]],["impl<'a> !UnwindSafe for OpenUni<'a>",1,["noq::connection::OpenUni"]],["impl<'a> !UnwindSafe for ReadDatagram<'a>",1,["noq::connection::ReadDatagram"]],["impl<'a> !UnwindSafe for SendDatagram<'a>",1,["noq::connection::SendDatagram"]]]],["noq_perf",[["impl UnwindSafe for Commands",1,["noq_perf::Commands"]],["impl UnwindSafe for Cli",1,["noq_perf::Cli"]]]],["noq_proto",[["impl !UnwindSafe for ConnectionError",1,["noq_proto::connection::ConnectionError"]],["impl !UnwindSafe for DatagramEvent",1,["noq_proto::endpoint::DatagramEvent"]],["impl !UnwindSafe for Event",1,["noq_proto::connection::Event"]],["impl !UnwindSafe for QuicClientConfig",1,["noq_proto::crypto::rustls::QuicClientConfig"]],["impl !UnwindSafe for QuicServerConfig",1,["noq_proto::crypto::rustls::QuicServerConfig"]],["impl !UnwindSafe for TlsSession",1,["noq_proto::crypto::rustls::TlsSession"]],["impl !UnwindSafe for Keys",1,["noq_proto::crypto::Keys"]],["impl !UnwindSafe for AcceptError",1,["noq_proto::endpoint::AcceptError"]],["impl !UnwindSafe for ClientConfig",1,["noq_proto::config::ClientConfig"]],["impl !UnwindSafe for Connection",1,["noq_proto::connection::Connection"]],["impl !UnwindSafe for Endpoint",1,["noq_proto::endpoint::Endpoint"]],["impl !UnwindSafe for EndpointConfig",1,["noq_proto::config::EndpointConfig"]],["impl !UnwindSafe for Incoming",1,["noq_proto::endpoint::Incoming"]],["impl !UnwindSafe for QlogConfig",1,["noq_proto::config::qlog::QlogConfig"]],["impl !UnwindSafe for RetryError",1,["noq_proto::endpoint::RetryError"]],["impl !UnwindSafe for ServerConfig",1,["noq_proto::config::ServerConfig"]],["impl !UnwindSafe for TransportConfig",1,["noq_proto::config::transport::TransportConfig"]],["impl !UnwindSafe for Error",1,["noq_proto::transport_error::Error"]],["impl !UnwindSafe for ValidationTokenConfig",1,["noq_proto::config::ValidationTokenConfig"]],["impl UnwindSafe for ClosePathError",1,["noq_proto::connection::ClosePathError"]],["impl UnwindSafe for ConfigError",1,["noq_proto::config::ConfigError"]],["impl UnwindSafe for ConnectError",1,["noq_proto::endpoint::ConnectError"]],["impl UnwindSafe for Dir",1,["noq_proto::Dir"]],["impl UnwindSafe for EcnCodepoint",1,["noq_proto::shared::EcnCodepoint"]],["impl UnwindSafe for FinishError",1,["noq_proto::connection::streams::send::FinishError"]],["impl UnwindSafe for FrameType",1,["noq_proto::frame::FrameType"]],["impl UnwindSafe for LongType",1,["noq_proto::packet::LongType"]],["impl UnwindSafe for MaybeFrame",1,["noq_proto::frame::MaybeFrame"]],["impl UnwindSafe for PacketDecodeError",1,["noq_proto::packet::PacketDecodeError"]],["impl UnwindSafe for PathAbandonReason",1,["noq_proto::connection::paths::PathAbandonReason"]],["impl UnwindSafe for PathError",1,["noq_proto::connection::PathError"]],["impl UnwindSafe for PathEvent",1,["noq_proto::connection::paths::PathEvent"]],["impl UnwindSafe for PathStatus",1,["noq_proto::connection::paths::PathStatus"]],["impl UnwindSafe for ProtectedHeader",1,["noq_proto::packet::ProtectedHeader"]],["impl UnwindSafe for ReadError",1,["noq_proto::connection::streams::recv::ReadError"]],["impl UnwindSafe for ReadableError",1,["noq_proto::connection::streams::recv::ReadableError"]],["impl UnwindSafe for SendDatagramError",1,["noq_proto::connection::datagrams::SendDatagramError"]],["impl UnwindSafe for SetPathStatusError",1,["noq_proto::connection::paths::SetPathStatusError"]],["impl UnwindSafe for Side",1,["noq_proto::Side"]],["impl UnwindSafe for StreamEvent",1,["noq_proto::connection::streams::StreamEvent"]],["impl UnwindSafe for WriteError",1,["noq_proto::connection::streams::send::WriteError"]],["impl UnwindSafe for Error",1,["noq_proto::n0_nat_traversal::Error"]],["impl UnwindSafe for Event",1,["noq_proto::n0_nat_traversal::Event"]],["impl UnwindSafe for Error",1,["noq_proto::transport_parameters::Error"]],["impl UnwindSafe for UnexpectedEnd",1,["noq_proto::coding::UnexpectedEnd"]],["impl UnwindSafe for Bbr3",1,["noq_proto::congestion::bbr3::Bbr3"]],["impl UnwindSafe for Bbr3Config",1,["noq_proto::congestion::bbr3::Bbr3Config"]],["impl UnwindSafe for ControllerMetrics",1,["noq_proto::congestion::ControllerMetrics"]],["impl UnwindSafe for Cubic",1,["noq_proto::congestion::cubic::Cubic"]],["impl UnwindSafe for CubicConfig",1,["noq_proto::congestion::cubic::CubicConfig"]],["impl UnwindSafe for NewReno",1,["noq_proto::congestion::new_reno::NewReno"]],["impl UnwindSafe for NewRenoConfig",1,["noq_proto::congestion::new_reno::NewRenoConfig"]],["impl UnwindSafe for HandshakeData",1,["noq_proto::crypto::rustls::HandshakeData"]],["impl UnwindSafe for NoInitialCipherSuite",1,["noq_proto::crypto::rustls::NoInitialCipherSuite"]],["impl UnwindSafe for CryptoError",1,["noq_proto::crypto::CryptoError"]],["impl UnwindSafe for ExportKeyingMaterialError",1,["noq_proto::crypto::ExportKeyingMaterialError"]],["impl UnwindSafe for UnsupportedVersion",1,["noq_proto::crypto::UnsupportedVersion"]],["impl UnwindSafe for AckFrequencyConfig",1,["noq_proto::config::transport::AckFrequencyConfig"]],["impl UnwindSafe for ApplicationClose",1,["noq_proto::frame::ApplicationClose"]],["impl UnwindSafe for BloomTokenLog",1,["noq_proto::bloom_token_log::BloomTokenLog"]],["impl UnwindSafe for Chunk",1,["noq_proto::connection::assembler::Chunk"]],["impl UnwindSafe for ClosedPath",1,["noq_proto::connection::paths::ClosedPath"]],["impl UnwindSafe for ClosedStream",1,["noq_proto::connection::streams::ClosedStream"]],["impl UnwindSafe for ConnectionClose",1,["noq_proto::frame::ConnectionClose"]],["impl UnwindSafe for ConnectionEvent",1,["noq_proto::shared::ConnectionEvent"]],["impl UnwindSafe for ConnectionHandle",1,["noq_proto::endpoint::ConnectionHandle"]],["impl UnwindSafe for ConnectionId",1,["noq_proto::shared::ConnectionId"]],["impl UnwindSafe for ConnectionStats",1,["noq_proto::connection::stats::ConnectionStats"]],["impl UnwindSafe for Datagram",1,["noq_proto::frame::Datagram"]],["impl UnwindSafe for DatagramInfo",1,["noq_proto::frame::DatagramInfo"]],["impl UnwindSafe for DecryptedInitial",1,["noq_proto::endpoint::DecryptedInitial"]],["impl UnwindSafe for EndpointEvent",1,["noq_proto::shared::EndpointEvent"]],["impl UnwindSafe for FixedLengthConnectionIdParser",1,["noq_proto::packet::FixedLengthConnectionIdParser"]],["impl UnwindSafe for FourTuple",1,["noq_proto::FourTuple"]],["impl UnwindSafe for FrameStats",1,["noq_proto::connection::stats::FrameStats"]],["impl UnwindSafe for HashedConnectionIdGenerator",1,["noq_proto::cid_generator::HashedConnectionIdGenerator"]],["impl UnwindSafe for IdleTimeout",1,["noq_proto::config::transport::IdleTimeout"]],["impl UnwindSafe for IncomingAlpns",1,["noq_proto::endpoint::IncomingAlpns"]],["impl UnwindSafe for InvalidCid",1,["noq_proto::cid_generator::InvalidCid"]],["impl UnwindSafe for InvalidFrameId",1,["noq_proto::frame::InvalidFrameId"]],["impl UnwindSafe for MtuDiscoveryConfig",1,["noq_proto::config::transport::MtuDiscoveryConfig"]],["impl UnwindSafe for MultipathNotNegotiated",1,["noq_proto::connection::MultipathNotNegotiated"]],["impl UnwindSafe for NoneTokenLog",1,["noq_proto::token::NoneTokenLog"]],["impl UnwindSafe for NoneTokenStore",1,["noq_proto::token::NoneTokenStore"]],["impl UnwindSafe for PartialDecode",1,["noq_proto::packet::PartialDecode"]],["impl UnwindSafe for PathId",1,["noq_proto::connection::paths::PathId"]],["impl UnwindSafe for PathStats",1,["noq_proto::connection::stats::PathStats"]],["impl UnwindSafe for ProtectedInitialHeader",1,["noq_proto::packet::ProtectedInitialHeader"]],["impl UnwindSafe for QlogFileFactory",1,["noq_proto::config::qlog::QlogFileFactory"]],["impl UnwindSafe for RandomConnectionIdGenerator",1,["noq_proto::cid_generator::RandomConnectionIdGenerator"]],["impl UnwindSafe for RttEstimator",1,["noq_proto::connection::paths::RttEstimator"]],["impl UnwindSafe for ShouldTransmit",1,["noq_proto::connection::streams::ShouldTransmit"]],["impl UnwindSafe for StdSystemTime",1,["noq_proto::config::StdSystemTime"]],["impl UnwindSafe for StreamId",1,["noq_proto::StreamId"]],["impl UnwindSafe for StreamInfo",1,["noq_proto::frame::StreamInfo"]],["impl UnwindSafe for TokenMemoryCache",1,["noq_proto::token_memory_cache::TokenMemoryCache"]],["impl UnwindSafe for TokenReuseError",1,["noq_proto::token::TokenReuseError"]],["impl UnwindSafe for Transmit",1,["noq_proto::Transmit"]],["impl UnwindSafe for Code",1,["noq_proto::transport_error::Code"]],["impl UnwindSafe for UdpStats",1,["noq_proto::connection::stats::UdpStats"]],["impl UnwindSafe for VarInt",1,["noq_proto::varint::VarInt"]],["impl UnwindSafe for VarIntBoundsExceeded",1,["noq_proto::varint::VarIntBoundsExceeded"]],["impl UnwindSafe for TransportParameters",1,["noq_proto::transport_parameters::TransportParameters"]],["impl<'a> !UnwindSafe for Chunks<'a>",1,["noq_proto::connection::streams::recv::Chunks"]],["impl<'a> !UnwindSafe for Datagrams<'a>",1,["noq_proto::connection::datagrams::Datagrams"]],["impl<'a> !UnwindSafe for RecvStream<'a>",1,["noq_proto::connection::streams::RecvStream"]],["impl<'a> !UnwindSafe for SendStream<'a>",1,["noq_proto::connection::streams::SendStream"]],["impl<'a> !UnwindSafe for Streams<'a>",1,["noq_proto::connection::streams::Streams"]],["impl<T> UnwindSafe for KeyPair<T>
where\n T: UnwindSafe,
",1,["noq_proto::crypto::KeyPair"]]]],["noq_udp",[["impl UnwindSafe for EcnCodepoint",1,["noq_udp::EcnCodepoint"]],["impl UnwindSafe for RecvMeta",1,["noq_udp::RecvMeta"]],["impl UnwindSafe for UdpSocketState",1,["noq_udp::imp::UdpSocketState"]],["impl<'a> UnwindSafe for Transmit<'a>",1,["noq_udp::Transmit"]],["impl<'a> UnwindSafe for UdpSockRef<'a>",1,["noq_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"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1959,332,405,13015,631,39417,1691,3140]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/core/str/traits/trait.FromStr.js b/pr/672/docs/trait.impl/core/str/traits/trait.FromStr.js new file mode 100644 index 000000000..1faaecdfd --- /dev/null +++ b/pr/672/docs/trait.impl/core/str/traits/trait.FromStr.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl FromStr for CipherSuite"],["impl FromStr for RuntimeType"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[551]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/futures_core/stream/trait.Stream.js b/pr/672/docs/trait.impl/futures_core/stream/trait.Stream.js new file mode 100644 index 000000000..286ae1016 --- /dev/null +++ b/pr/672/docs/trait.impl/futures_core/stream/trait.Stream.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[["impl Stream for AddressDiscovery"],["impl Stream for NatTraversalUpdates"],["impl Stream for ObservedExternalAddr"],["impl Stream for PathEvents"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[580]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js b/pr/672/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js new file mode 100644 index 000000000..1166e2ea7 --- /dev/null +++ b/pr/672/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[["impl AsyncRead for RecvStream"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[136]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js b/pr/672/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js new file mode 100644 index 000000000..c94c14b44 --- /dev/null +++ b/pr/672/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[["impl AsyncWrite for SendStream"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[137]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq/runtime/trait.AsyncTimer.js b/pr/672/docs/trait.impl/noq/runtime/trait.AsyncTimer.js new file mode 100644 index 000000000..b9b1e5f9e --- /dev/null +++ b/pr/672/docs/trait.impl/noq/runtime/trait.AsyncTimer.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[10]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq/runtime/trait.Runtime.js b/pr/672/docs/trait.impl/noq/runtime/trait.Runtime.js new file mode 100644 index 000000000..b9b1e5f9e --- /dev/null +++ b/pr/672/docs/trait.impl/noq/runtime/trait.Runtime.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[10]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq_proto/cid_generator/trait.ConnectionIdGenerator.js b/pr/672/docs/trait.impl/noq_proto/cid_generator/trait.ConnectionIdGenerator.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/672/docs/trait.impl/noq_proto/cid_generator/trait.ConnectionIdGenerator.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[]],["noq_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[10,17]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq_proto/coding/trait.Decodable.js b/pr/672/docs/trait.impl/noq_proto/coding/trait.Decodable.js new file mode 100644 index 000000000..31300dd74 --- /dev/null +++ b/pr/672/docs/trait.impl/noq_proto/coding/trait.Decodable.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[16]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq_proto/coding/trait.Encodable.js b/pr/672/docs/trait.impl/noq_proto/coding/trait.Encodable.js new file mode 100644 index 000000000..31300dd74 --- /dev/null +++ b/pr/672/docs/trait.impl/noq_proto/coding/trait.Encodable.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[16]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq_proto/config/qlog/trait.QlogFactory.js b/pr/672/docs/trait.impl/noq_proto/config/qlog/trait.QlogFactory.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/672/docs/trait.impl/noq_proto/config/qlog/trait.QlogFactory.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[]],["noq_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[10,17]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq_proto/config/trait.TimeSource.js b/pr/672/docs/trait.impl/noq_proto/config/trait.TimeSource.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/672/docs/trait.impl/noq_proto/config/trait.TimeSource.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[]],["noq_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[10,17]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq_proto/congestion/trait.Controller.js b/pr/672/docs/trait.impl/noq_proto/congestion/trait.Controller.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/672/docs/trait.impl/noq_proto/congestion/trait.Controller.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[]],["noq_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[10,17]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq_proto/congestion/trait.ControllerFactory.js b/pr/672/docs/trait.impl/noq_proto/congestion/trait.ControllerFactory.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/672/docs/trait.impl/noq_proto/congestion/trait.ControllerFactory.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[]],["noq_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[10,17]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq_proto/crypto/trait.ClientConfig.js b/pr/672/docs/trait.impl/noq_proto/crypto/trait.ClientConfig.js new file mode 100644 index 000000000..7ae2562df --- /dev/null +++ b/pr/672/docs/trait.impl/noq_proto/crypto/trait.ClientConfig.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[]],["noq_proto",[]],["perf",[["impl ClientConfig for NoProtectionClientConfig"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[10,17,333]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq_proto/crypto/trait.HeaderKey.js b/pr/672/docs/trait.impl/noq_proto/crypto/trait.HeaderKey.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/672/docs/trait.impl/noq_proto/crypto/trait.HeaderKey.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[]],["noq_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[10,17]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq_proto/crypto/trait.HmacKey.js b/pr/672/docs/trait.impl/noq_proto/crypto/trait.HmacKey.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/672/docs/trait.impl/noq_proto/crypto/trait.HmacKey.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[]],["noq_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[10,17]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq_proto/crypto/trait.PacketKey.js b/pr/672/docs/trait.impl/noq_proto/crypto/trait.PacketKey.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/672/docs/trait.impl/noq_proto/crypto/trait.PacketKey.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[]],["noq_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[10,17]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq_proto/crypto/trait.ServerConfig.js b/pr/672/docs/trait.impl/noq_proto/crypto/trait.ServerConfig.js new file mode 100644 index 000000000..f79d7a65d --- /dev/null +++ b/pr/672/docs/trait.impl/noq_proto/crypto/trait.ServerConfig.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[]],["noq_proto",[]],["perf",[["impl ServerConfig for NoProtectionServerConfig"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[10,17,333]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq_proto/crypto/trait.Session.js b/pr/672/docs/trait.impl/noq_proto/crypto/trait.Session.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/672/docs/trait.impl/noq_proto/crypto/trait.Session.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[]],["noq_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[10,17]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq_proto/packet/trait.ConnectionIdParser.js b/pr/672/docs/trait.impl/noq_proto/packet/trait.ConnectionIdParser.js new file mode 100644 index 000000000..31300dd74 --- /dev/null +++ b/pr/672/docs/trait.impl/noq_proto/packet/trait.ConnectionIdParser.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[16]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq_proto/token/trait.TokenLog.js b/pr/672/docs/trait.impl/noq_proto/token/trait.TokenLog.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/672/docs/trait.impl/noq_proto/token/trait.TokenLog.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[]],["noq_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[10,17]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/noq_proto/token/trait.TokenStore.js b/pr/672/docs/trait.impl/noq_proto/token/trait.TokenStore.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/672/docs/trait.impl/noq_proto/token/trait.TokenStore.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[]],["noq_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[10,17]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/rustls/verify/trait.ServerCertVerifier.js b/pr/672/docs/trait.impl/rustls/verify/trait.ServerCertVerifier.js new file mode 100644 index 000000000..45f6b7887 --- /dev/null +++ b/pr/672/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/672/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js b/pr/672/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js new file mode 100644 index 000000000..1166e2ea7 --- /dev/null +++ b/pr/672/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[["impl AsyncRead for RecvStream"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[136]} \ No newline at end of file diff --git a/pr/672/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js b/pr/672/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js new file mode 100644 index 000000000..c94c14b44 --- /dev/null +++ b/pr/672/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[["impl AsyncWrite for SendStream"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[137]} \ No newline at end of file diff --git a/pr/672/docs/type.impl/core/result/enum.Result.js b/pr/672/docs/type.impl/core/result/enum.Result.js new file mode 100644 index 000000000..549f0cc04 --- /dev/null +++ b/pr/672/docs/type.impl/core/result/enum.Result.js @@ -0,0 +1,9 @@ +(function() { + var type_impls = Object.fromEntries([["noq_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>","noq_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","noq_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","noq_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>","noq_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>","noq_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>","noq_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>","noq_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>","noq_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>","noq_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","noq_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","noq_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","noq_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","noq_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","noq_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>","noq_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","noq_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,"noq_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,"noq_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,"noq_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,"noq_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,"noq_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","noq_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>","noq_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>","noq_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","noq_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","noq_proto::coding::Result"],["
Source§

impl<T, E> CloneFromCell for Result<T, E>
where\n T: CloneFromCell,\n E: CloneFromCell,

","CloneFromCell","noq_proto::coding::Result"],["
1.0.0 · Source§

impl<T, E> Copy for Result<T, E>
where\n T: Copy,\n E: Copy,

","Copy","noq_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","noq_proto::coding::Result"],["
1.0.0 · Source§

impl<T, E> StructuralPartialEq for Result<T, E>

","StructuralPartialEq","noq_proto::coding::Result"],["
Source§

impl<T, E> UseCloned for Result<T, E>
where\n T: UseCloned,\n E: UseCloned,

","UseCloned","noq_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":[187167]} \ No newline at end of file