From caacf1d1b17536badbbdffc98342b0f8fe99129d Mon Sep 17 00:00:00 2001 From: flub Date: Mon, 16 Mar 2026 18:11:10 +0000 Subject: [PATCH] deploy: 68aa0ec739caa6bf885851c5d9cf49227d102b10 --- pr/509/docs/.lock | 0 pr/509/docs/bench/all.html | 1 + pr/509/docs/bench/enum.CipherSuite.html | 35 + .../fn.configure_tracing_subscriber.html | 1 + pr/509/docs/bench/fn.connect_client.html | 6 + pr/509/docs/bench/fn.drain_stream.html | 4 + pr/509/docs/bench/fn.rt.html | 1 + pr/509/docs/bench/fn.send_data_on_stream.html | 4 + pr/509/docs/bench/fn.server_endpoint.html | 7 + pr/509/docs/bench/fn.transport_config.html | 1 + pr/509/docs/bench/index.html | 1 + pr/509/docs/bench/sidebar-items.js | 1 + .../docs/bench/stats/fn.throughput_bps.html | 1 + pr/509/docs/bench/stats/index.html | 1 + pr/509/docs/bench/stats/sidebar-items.js | 1 + pr/509/docs/bench/stats/struct.Stats.html | 29 + .../docs/bench/stats/struct.StreamStats.html | 27 + .../bench/stats/struct.TransferResult.html | 28 + pr/509/docs/bench/struct.Opt.html | 68 + pr/509/docs/bulk/all.html | 1 + pr/509/docs/bulk/fn.client.html | 5 + pr/509/docs/bulk/fn.handle_client_stream.html | 5 + pr/509/docs/bulk/fn.main.html | 1 + pr/509/docs/bulk/fn.server.html | 1 + pr/509/docs/bulk/index.html | 1 + pr/509/docs/bulk/sidebar-items.js | 1 + pr/509/docs/bulk/struct.ClientStats.html | 27 + pr/509/docs/certificate/all.html | 1 + .../docs/certificate/fn.configure_client.html | 1 + .../fn.generate_self_signed_cert.html | 1 + pr/509/docs/certificate/fn.main.html | 1 + .../certificate/fn.read_certs_from_file.html | 1 + pr/509/docs/certificate/index.html | 1 + pr/509/docs/certificate/sidebar-items.js | 1 + .../struct.SkipServerVerification.html | 44 + pr/509/docs/crates.js | 2 + pr/509/docs/data_transfer/all.html | 1 + pr/509/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/509/docs/data_transfer/index.html | 1 + pr/509/docs/data_transfer/sidebar-items.js | 1 + pr/509/docs/help.html | 1 + pr/509/docs/noq/all.html | 1 + pr/509/docs/noq/congestion/index.html | 2 + pr/509/docs/noq/congestion/sidebar-items.js | 1 + pr/509/docs/noq/congestion/struct.Bbr.html | 52 + .../docs/noq/congestion/struct.BbrConfig.html | 33 + .../congestion/struct.ControllerMetrics.html | 32 + pr/509/docs/noq/congestion/struct.Cubic.html | 48 + .../noq/congestion/struct.CubicConfig.html | 33 + .../docs/noq/congestion/struct.NewReno.html | 52 + .../noq/congestion/struct.NewRenoConfig.html | 34 + .../docs/noq/congestion/trait.Controller.html | 79 + .../congestion/trait.ControllerFactory.html | 10 + .../connection/enum.SendDatagramError.html | 11 + .../docs/noq/connection/struct.AcceptBi.html | 11 + .../docs/noq/connection/struct.AcceptUni.html | 11 + .../noq/connection/struct.Connecting.html | 11 + .../noq/connection/struct.Connection.html | 11 + .../docs/noq/connection/struct.OnClosed.html | 11 + pr/509/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/509/docs/noq/crypto/index.html | 8 + pr/509/docs/noq/crypto/rustls/enum.Error.html | 101 + pr/509/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 | 31 + .../rustls/struct.QuicClientConfig.html | 55 + .../rustls/struct.QuicServerConfig.html | 59 + .../noq/crypto/rustls/struct.TlsSession.html | 38 + pr/509/docs/noq/crypto/sidebar-items.js | 1 + .../docs/noq/crypto/struct.CryptoError.html | 25 + .../struct.ExportKeyingMaterialError.html | 31 + pr/509/docs/noq/crypto/struct.KeyPair.html | 36 + pr/509/docs/noq/crypto/struct.Keys.html | 30 + .../noq/crypto/struct.UnsupportedVersion.html | 25 + .../docs/noq/crypto/trait.ClientConfig.html | 16 + .../noq/crypto/trait.HandshakeTokenKey.html | 23 + pr/509/docs/noq/crypto/trait.HeaderKey.html | 10 + pr/509/docs/noq/crypto/trait.HmacKey.html | 10 + pr/509/docs/noq/crypto/trait.PacketKey.html | 51 + .../docs/noq/crypto/trait.ServerConfig.html | 39 + pr/509/docs/noq/crypto/trait.Session.html | 69 + pr/509/docs/noq/endpoint/struct.Accept.html | 11 + pr/509/docs/noq/endpoint/struct.Endpoint.html | 11 + .../noq/endpoint/struct.EndpointStats.html | 11 + pr/509/docs/noq/enum.ConfigError.html | 36 + pr/509/docs/noq/enum.ConnectError.html | 51 + pr/509/docs/noq/enum.ConnectionError.html | 54 + pr/509/docs/noq/enum.Dir.html | 54 + pr/509/docs/noq/enum.EcnCodepoint.html | 41 + pr/509/docs/noq/enum.FrameType.html | 79 + pr/509/docs/noq/enum.ReadError.html | 45 + pr/509/docs/noq/enum.ReadExactError.html | 38 + pr/509/docs/noq/enum.ReadToEndError.html | 38 + pr/509/docs/noq/enum.ResetError.html | 40 + pr/509/docs/noq/enum.SendDatagramError.html | 44 + pr/509/docs/noq/enum.Side.html | 55 + pr/509/docs/noq/enum.StoppedError.html | 40 + pr/509/docs/noq/enum.WriteError.html | 45 + pr/509/docs/noq/fn.default_runtime.html | 5 + pr/509/docs/noq/incoming/struct.Incoming.html | 11 + .../noq/incoming/struct.IncomingFuture.html | 11 + .../docs/noq/incoming/struct.RetryError.html | 11 + pr/509/docs/noq/index.html | 34 + .../noq/path/struct.AddressDiscovery.html | 11 + pr/509/docs/noq/path/struct.OpenPath.html | 11 + pr/509/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/509/docs/noq/runtime/trait.AsyncTimer.html | 11 + .../noq/runtime/trait.AsyncUdpSocket.html | 11 + pr/509/docs/noq/runtime/trait.Runtime.html | 11 + pr/509/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/509/docs/noq/sidebar-items.js | 1 + pr/509/docs/noq/struct.Accept.html | 50 + pr/509/docs/noq/struct.AcceptBi.html | 54 + pr/509/docs/noq/struct.AcceptUni.html | 54 + .../docs/noq/struct.AckFrequencyConfig.html | 65 + pr/509/docs/noq/struct.AddressDiscovery.html | 173 + pr/509/docs/noq/struct.ApplicationClose.html | 38 + pr/509/docs/noq/struct.BloomTokenLog.html | 48 + pr/509/docs/noq/struct.Chunk.html | 35 + pr/509/docs/noq/struct.ClientConfig.html | 49 + pr/509/docs/noq/struct.ClosedStream.html | 33 + pr/509/docs/noq/struct.Connecting.html | 101 + pr/509/docs/noq/struct.Connection.html | 216 + pr/509/docs/noq/struct.ConnectionClose.html | 40 + pr/509/docs/noq/struct.ConnectionId.html | 2469 ++++++ pr/509/docs/noq/struct.ConnectionStats.html | 36 + pr/509/docs/noq/struct.Endpoint.html | 117 + pr/509/docs/noq/struct.EndpointConfig.html | 67 + pr/509/docs/noq/struct.EndpointStats.html | 36 + pr/509/docs/noq/struct.FrameStats.html | 65 + pr/509/docs/noq/struct.IdleTimeout.html | 52 + pr/509/docs/noq/struct.Incoming.html | 49 + pr/509/docs/noq/struct.IncomingFuture.html | 53 + pr/509/docs/noq/struct.InvalidCid.html | 27 + .../docs/noq/struct.MtuDiscoveryConfig.html | 88 + pr/509/docs/noq/struct.NoneTokenLog.html | 31 + pr/509/docs/noq/struct.NoneTokenStore.html | 25 + pr/509/docs/noq/struct.OnClosed.html | 50 + pr/509/docs/noq/struct.OpenBi.html | 54 + pr/509/docs/noq/struct.OpenPath.html | 57 + pr/509/docs/noq/struct.OpenUni.html | 54 + pr/509/docs/noq/struct.Path.html | 58 + pr/509/docs/noq/struct.PathId.html | 53 + pr/509/docs/noq/struct.PathStats.html | 56 + pr/509/docs/noq/struct.QlogConfig.html | 34 + pr/509/docs/noq/struct.QlogFileFactory.html | 39 + pr/509/docs/noq/struct.ReadDatagram.html | 54 + pr/509/docs/noq/struct.RecvStream.html | 210 + pr/509/docs/noq/struct.RetryError.html | 27 + pr/509/docs/noq/struct.SendDatagram.html | 54 + pr/509/docs/noq/struct.SendStream.html | 220 + pr/509/docs/noq/struct.ServerConfig.html | 114 + pr/509/docs/noq/struct.SmolRuntime.html | 25 + pr/509/docs/noq/struct.StdSystemTime.html | 26 + pr/509/docs/noq/struct.Stopped.html | 54 + pr/509/docs/noq/struct.StreamId.html | 55 + pr/509/docs/noq/struct.TokenMemoryCache.html | 31 + pr/509/docs/noq/struct.TokenReuseError.html | 25 + pr/509/docs/noq/struct.TokioRuntime.html | 25 + pr/509/docs/noq/struct.Transmit.html | 37 + pr/509/docs/noq/struct.TransportConfig.html | 252 + .../docs/noq/struct.TransportErrorCode.html | 57 + pr/509/docs/noq/struct.UdpStats.html | 41 + .../docs/noq/struct.UnorderedRecvStream.html | 54 + .../noq/struct.ValidationTokenConfig.html | 74 + pr/509/docs/noq/struct.VarInt.html | 65 + .../docs/noq/struct.VarIntBoundsExceeded.html | 33 + .../docs/noq/struct.WeakConnectionHandle.html | 32 + pr/509/docs/noq/struct.WeakPathHandle.html | 38 + pr/509/docs/noq/struct.Written.html | 38 + pr/509/docs/noq/struct.ZeroRttAccepted.html | 51 + pr/509/docs/noq/trait.AsyncTimer.html | 11 + pr/509/docs/noq/trait.AsyncUdpSocket.html | 38 + .../docs/noq/trait.ConnectionIdGenerator.html | 21 + pr/509/docs/noq/trait.NetworkChangeHint.html | 15 + pr/509/docs/noq/trait.QlogFactory.html | 23 + pr/509/docs/noq/trait.Runtime.html | 19 + pr/509/docs/noq/trait.TimeSource.html | 7 + pr/509/docs/noq/trait.TokenLog.html | 45 + pr/509/docs/noq/trait.TokenStore.html | 13 + pr/509/docs/noq/trait.UdpSender.html | 30 + pr/509/docs/noq_perf/all.html | 1 + pr/509/docs/noq_perf/enum.Commands.html | 40 + pr/509/docs/noq_perf/fn.main.html | 1 + pr/509/docs/noq_perf/index.html | 1 + pr/509/docs/noq_perf/sidebar-items.js | 1 + pr/509/docs/noq_perf/struct.Cli.html | 45 + pr/509/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/509/docs/noq_proto/coding/index.html | 2 + pr/509/docs/noq_proto/coding/sidebar-items.js | 1 + .../coding/struct.UnexpectedEnd.html | 33 + .../noq_proto/coding/trait.Decodable.html | 6 + .../noq_proto/coding/trait.Encodable.html | 6 + pr/509/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 + ...NCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html | 11 + .../transport/struct.AckFrequencyConfig.html | 11 + .../config/transport/struct.IdleTimeout.html | 11 + .../transport/struct.MtuDiscoveryConfig.html | 11 + .../transport/struct.TransportConfig.html | 11 + .../noq_proto/congestion/bbr/struct.Bbr.html | 11 + .../congestion/bbr/struct.BbrConfig.html | 11 + .../congestion/cubic/struct.Cubic.html | 11 + .../congestion/cubic/struct.CubicConfig.html | 11 + pr/509/docs/noq_proto/congestion/index.html | 2 + .../congestion/new_reno/struct.NewReno.html | 11 + .../new_reno/struct.NewRenoConfig.html | 11 + .../noq_proto/congestion/sidebar-items.js | 1 + .../docs/noq_proto/congestion/struct.Bbr.html | 52 + .../congestion/struct.BbrConfig.html | 33 + .../congestion/struct.ControllerMetrics.html | 32 + .../noq_proto/congestion/struct.Cubic.html | 48 + .../congestion/struct.CubicConfig.html | 29 + .../noq_proto/congestion/struct.NewReno.html | 48 + .../congestion/struct.NewRenoConfig.html | 30 + .../congestion/trait.Controller.html | 79 + .../congestion/trait.ControllerFactory.html | 10 + .../connection/assembler/struct.Chunk.html | 11 + .../datagrams/enum.SendDatagramError.html | 11 + .../datagrams/struct.Datagrams.html | 11 + .../connection/enum.ClosePathError.html | 11 + .../connection/enum.ConnectionError.html | 11 + .../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/send/struct.Written.html | 11 + .../streams/struct.ClosedStream.html | 11 + .../connection/streams/struct.RecvStream.html | 11 + .../connection/streams/struct.SendStream.html | 11 + .../streams/struct.ShouldTransmit.html | 11 + .../connection/streams/struct.Streams.html | 11 + .../connection/struct.Connection.html | 11 + .../struct.MultipathNotNegotiated.html | 11 + .../connection/trait.NetworkChangeHint.html | 11 + ...NCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html | 3 + .../constant.DEFAULT_SUPPORTED_VERSIONS.html | 2 + pr/509/docs/noq_proto/crypto/index.html | 8 + .../noq_proto/crypto/rustls/enum.Error.html | 101 + .../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 | 31 + .../rustls/struct.QuicClientConfig.html | 51 + .../rustls/struct.QuicServerConfig.html | 55 + .../crypto/rustls/struct.TlsSession.html | 40 + pr/509/docs/noq_proto/crypto/sidebar-items.js | 1 + .../noq_proto/crypto/struct.CryptoError.html | 25 + .../struct.ExportKeyingMaterialError.html | 31 + .../docs/noq_proto/crypto/struct.KeyPair.html | 36 + pr/509/docs/noq_proto/crypto/struct.Keys.html | 30 + .../crypto/struct.UnsupportedVersion.html | 25 + .../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/509/docs/noq_proto/enum.ConfigError.html | 36 + pr/509/docs/noq_proto/enum.ConnectError.html | 51 + .../docs/noq_proto/enum.ConnectionError.html | 54 + pr/509/docs/noq_proto/enum.DatagramEvent.html | 32 + pr/509/docs/noq_proto/enum.Dir.html | 53 + pr/509/docs/noq_proto/enum.EcnCodepoint.html | 41 + pr/509/docs/noq_proto/enum.Event.html | 48 + pr/509/docs/noq_proto/enum.FinishError.html | 40 + pr/509/docs/noq_proto/enum.FrameType.html | 77 + pr/509/docs/noq_proto/enum.LongType.html | 37 + pr/509/docs/noq_proto/enum.MaybeFrame.html | 40 + .../noq_proto/enum.PacketDecodeError.html | 55 + .../noq_proto/enum.PathAbandonReason.html | 51 + pr/509/docs/noq_proto/enum.PathError.html | 46 + pr/509/docs/noq_proto/enum.PathEvent.html | 72 + pr/509/docs/noq_proto/enum.PathStatus.html | 43 + .../docs/noq_proto/enum.ProtectedHeader.html | 75 + pr/509/docs/noq_proto/enum.ReadError.html | 51 + pr/509/docs/noq_proto/enum.ReadableError.html | 50 + .../noq_proto/enum.SendDatagramError.html | 54 + .../noq_proto/enum.SetPathStatusError.html | 38 + pr/509/docs/noq_proto/enum.Side.html | 54 + pr/509/docs/noq_proto/enum.StreamEvent.html | 64 + pr/509/docs/noq_proto/enum.WriteError.html | 54 + .../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/509/docs/noq_proto/index.html | 14 + .../n0_nat_traversal/enum.Error.html | 39 + .../n0_nat_traversal/enum.Event.html | 32 + .../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/509/docs/noq_proto/sidebar-items.js | 1 + pr/509/docs/noq_proto/struct.AcceptError.html | 30 + .../noq_proto/struct.AckFrequencyConfig.html | 59 + .../noq_proto/struct.ApplicationClose.html | 38 + .../docs/noq_proto/struct.BloomTokenLog.html | 48 + pr/509/docs/noq_proto/struct.Chunk.html | 35 + pr/509/docs/noq_proto/struct.Chunks.html | 41 + .../docs/noq_proto/struct.ClientConfig.html | 46 + pr/509/docs/noq_proto/struct.ClosedPath.html | 33 + .../docs/noq_proto/struct.ClosedStream.html | 33 + pr/509/docs/noq_proto/struct.Connection.html | 264 + .../noq_proto/struct.ConnectionClose.html | 40 + .../noq_proto/struct.ConnectionEvent.html | 25 + .../noq_proto/struct.ConnectionHandle.html | 42 + .../docs/noq_proto/struct.ConnectionId.html | 2468 ++++++ .../noq_proto/struct.ConnectionStats.html | 36 + pr/509/docs/noq_proto/struct.Datagram.html | 31 + .../docs/noq_proto/struct.DatagramInfo.html | 32 + pr/509/docs/noq_proto/struct.Datagrams.html | 44 + .../noq_proto/struct.DecryptedInitial.html | 31 + pr/509/docs/noq_proto/struct.Endpoint.html | 78 + .../docs/noq_proto/struct.EndpointConfig.html | 66 + .../docs/noq_proto/struct.EndpointEvent.html | 30 + .../struct.FixedLengthConnectionIdParser.html | 26 + pr/509/docs/noq_proto/struct.FourTuple.html | 63 + pr/509/docs/noq_proto/struct.FrameStats.html | 65 + .../struct.HashedConnectionIdGenerator.html | 33 + pr/509/docs/noq_proto/struct.IdleTimeout.html | 49 + pr/509/docs/noq_proto/struct.Incoming.html | 39 + .../docs/noq_proto/struct.IncomingAlpns.html | 638 ++ pr/509/docs/noq_proto/struct.InvalidCid.html | 27 + .../docs/noq_proto/struct.InvalidFrameId.html | 31 + .../noq_proto/struct.MtuDiscoveryConfig.html | 85 + .../struct.MultipathNotNegotiated.html | 28 + .../docs/noq_proto/struct.NoneTokenLog.html | 31 + .../docs/noq_proto/struct.NoneTokenStore.html | 25 + .../docs/noq_proto/struct.PartialDecode.html | 42 + pr/509/docs/noq_proto/struct.PathId.html | 49 + pr/509/docs/noq_proto/struct.PathStats.html | 56 + .../struct.ProtectedInitialHeader.html | 38 + pr/509/docs/noq_proto/struct.QlogConfig.html | 34 + .../noq_proto/struct.QlogFileFactory.html | 39 + .../struct.RandomConnectionIdGenerator.html | 33 + pr/509/docs/noq_proto/struct.RecvStream.html | 44 + pr/509/docs/noq_proto/struct.RetryError.html | 27 + .../docs/noq_proto/struct.RttEstimator.html | 32 + pr/509/docs/noq_proto/struct.SendStream.html | 53 + .../docs/noq_proto/struct.ServerConfig.html | 102 + .../docs/noq_proto/struct.ShouldTransmit.html | 35 + .../docs/noq_proto/struct.StdSystemTime.html | 26 + pr/509/docs/noq_proto/struct.StreamId.html | 52 + pr/509/docs/noq_proto/struct.StreamInfo.html | 32 + pr/509/docs/noq_proto/struct.Streams.html | 35 + .../noq_proto/struct.TokenMemoryCache.html | 28 + .../noq_proto/struct.TokenReuseError.html | 25 + pr/509/docs/noq_proto/struct.Transmit.html | 37 + .../noq_proto/struct.TransportConfig.html | 222 + .../docs/noq_proto/struct.TransportError.html | 45 + .../noq_proto/struct.TransportErrorCode.html | 55 + pr/509/docs/noq_proto/struct.UdpStats.html | 41 + .../struct.ValidationTokenConfig.html | 74 + pr/509/docs/noq_proto/struct.VarInt.html | 62 + .../struct.VarIntBoundsExceeded.html | 33 + pr/509/docs/noq_proto/struct.Written.html | 38 + .../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/509/docs/noq_proto/trait.QlogFactory.html | 23 + pr/509/docs/noq_proto/trait.TimeSource.html | 7 + pr/509/docs/noq_proto/trait.TokenLog.html | 45 + pr/509/docs/noq_proto/trait.TokenStore.html | 13 + .../transport_error/struct.Code.html | 11 + .../transport_error/struct.Error.html | 11 + .../transport_parameters/enum.Error.html | 38 + .../noq_proto/transport_parameters/index.html | 7 + .../transport_parameters/sidebar-items.js | 1 + .../struct.TransportParameters.html | 38 + .../docs/noq_proto/varint/struct.VarInt.html | 11 + .../varint/struct.VarIntBoundsExceeded.html | 11 + pr/509/docs/noq_udp/all.html | 1 + pr/509/docs/noq_udp/constant.BATCH_SIZE.html | 2 + pr/509/docs/noq_udp/enum.EcnCodepoint.html | 28 + .../noq_udp/imp/struct.UdpSocketState.html | 11 + pr/509/docs/noq_udp/index.html | 22 + pr/509/docs/noq_udp/sidebar-items.js | 1 + pr/509/docs/noq_udp/struct.RecvMeta.html | 41 + pr/509/docs/noq_udp/struct.Transmit.html | 31 + pr/509/docs/noq_udp/struct.UdpSockRef.html | 19 + .../docs/noq_udp/struct.UdpSocketState.html | 62 + pr/509/docs/perf/all.html | 1 + pr/509/docs/perf/client/fn.run.html | 1 + pr/509/docs/perf/client/index.html | 1 + pr/509/docs/perf/client/sidebar-items.js | 1 + pr/509/docs/perf/client/struct.Opt.html | 43 + .../docs/perf/enum.CongestionAlgorithm.html | 30 + pr/509/docs/perf/fn.parse_byte_size.html | 1 + pr/509/docs/perf/index.html | 1 + pr/509/docs/perf/noprotection/index.html | 1 + .../docs/perf/noprotection/sidebar-items.js | 1 + .../struct.NoProtectionClientConfig.html | 31 + .../struct.NoProtectionServerConfig.html | 39 + pr/509/docs/perf/server/fn.run.html | 1 + pr/509/docs/perf/server/index.html | 1 + pr/509/docs/perf/server/sidebar-items.js | 1 + pr/509/docs/perf/server/struct.Opt.html | 42 + pr/509/docs/perf/sidebar-items.js | 1 + .../docs/perf/static.PERF_CIPHER_SUITES.html | 1 + pr/509/docs/perf/stats/index.html | 1 + pr/509/docs/perf/stats/sidebar-items.js | 1 + .../perf/stats/struct.OpenStreamStats.html | 35 + pr/509/docs/perf/stats/struct.Stats.html | 24 + .../docs/perf/stats/struct.StreamStats.html | 24 + pr/509/docs/perf/struct.CommonOpt.html | 84 + pr/509/docs/search.index/021c1e79bdd9.js | 1 + pr/509/docs/search.index/02804be949b0.js | 1 + pr/509/docs/search.index/0452ffab0bec.js | 1 + pr/509/docs/search.index/04e28bec524a.js | 1 + pr/509/docs/search.index/0504377ac740.js | 1 + pr/509/docs/search.index/062704961ddf.js | 1 + pr/509/docs/search.index/066d6a9a769d.js | 1 + pr/509/docs/search.index/06c602595384.js | 1 + pr/509/docs/search.index/06cb80e90e12.js | 1 + pr/509/docs/search.index/0808e3817873.js | 1 + pr/509/docs/search.index/0920e65a8fea.js | 1 + pr/509/docs/search.index/094d93788e8b.js | 1 + pr/509/docs/search.index/0b1f340b7ad1.js | 1 + pr/509/docs/search.index/0b471b6e9f68.js | 1 + pr/509/docs/search.index/0b9e3bf94d8e.js | 1 + pr/509/docs/search.index/0ba637addba8.js | 1 + pr/509/docs/search.index/0bb8ecd0000d.js | 1 + pr/509/docs/search.index/0d3c240b6746.js | 1 + pr/509/docs/search.index/0d40bbfaf512.js | 1 + pr/509/docs/search.index/0e7685c20a2a.js | 1 + pr/509/docs/search.index/0ee8581ceb4b.js | 1 + pr/509/docs/search.index/0f8ffae0cedd.js | 1 + pr/509/docs/search.index/0fc7936ce68d.js | 1 + pr/509/docs/search.index/10020ef0a247.js | 1 + pr/509/docs/search.index/10071b17a468.js | 1 + pr/509/docs/search.index/1062f3057d85.js | 1 + pr/509/docs/search.index/10722fb15319.js | 1 + pr/509/docs/search.index/116b929a1e0d.js | 1 + pr/509/docs/search.index/11770572d7de.js | 1 + pr/509/docs/search.index/11d9d1c24f11.js | 1 + pr/509/docs/search.index/12e2aa203dff.js | 1 + pr/509/docs/search.index/136ec6b58ef2.js | 1 + pr/509/docs/search.index/13bc5713853d.js | 1 + pr/509/docs/search.index/14600ff5b785.js | 1 + pr/509/docs/search.index/1471da9f23be.js | 1 + pr/509/docs/search.index/1473e4141b70.js | 1 + pr/509/docs/search.index/14e2eb248d40.js | 1 + pr/509/docs/search.index/14e8961c1d4b.js | 1 + pr/509/docs/search.index/14f8d441cdf1.js | 1 + pr/509/docs/search.index/15aa8d41a495.js | 1 + pr/509/docs/search.index/15d86d685190.js | 1 + pr/509/docs/search.index/166712222acb.js | 1 + pr/509/docs/search.index/16760629a188.js | 1 + pr/509/docs/search.index/16ac7dbe5c38.js | 1 + pr/509/docs/search.index/174deafe44ae.js | 1 + pr/509/docs/search.index/17adc63e271c.js | 1 + pr/509/docs/search.index/1940d69372f3.js | 1 + pr/509/docs/search.index/19935bf303f2.js | 1 + pr/509/docs/search.index/1b0ddf9ba1d4.js | 1 + pr/509/docs/search.index/1b58e8099167.js | 1 + pr/509/docs/search.index/1b8da10f40ca.js | 1 + pr/509/docs/search.index/1bce199876db.js | 1 + pr/509/docs/search.index/1c551ca2e064.js | 1 + pr/509/docs/search.index/1cc87b30a9de.js | 1 + pr/509/docs/search.index/1fad72632495.js | 1 + pr/509/docs/search.index/1fb07216cf6d.js | 1 + pr/509/docs/search.index/2045e5eacc73.js | 1 + pr/509/docs/search.index/2096ecc2779b.js | 1 + pr/509/docs/search.index/210dbcbbd7f7.js | 1 + pr/509/docs/search.index/218355186cfc.js | 1 + pr/509/docs/search.index/2209e95f2e0d.js | 1 + pr/509/docs/search.index/221f869c6ba4.js | 1 + pr/509/docs/search.index/22f4aa7b7735.js | 1 + pr/509/docs/search.index/23f856af9139.js | 1 + pr/509/docs/search.index/2415f9b5fd49.js | 1 + pr/509/docs/search.index/24b191264c4c.js | 1 + pr/509/docs/search.index/250587bf9bab.js | 1 + pr/509/docs/search.index/253763ab1d88.js | 1 + pr/509/docs/search.index/267754446ed8.js | 1 + pr/509/docs/search.index/26bbc9b847a5.js | 1 + pr/509/docs/search.index/27c9a562bd0e.js | 1 + pr/509/docs/search.index/28367a06fb2b.js | 1 + pr/509/docs/search.index/2929fb67221c.js | 1 + pr/509/docs/search.index/296f1368d1fa.js | 1 + pr/509/docs/search.index/2a75dabf6bdd.js | 1 + pr/509/docs/search.index/2b1d461fecf9.js | 1 + pr/509/docs/search.index/2b391fb231c1.js | 1 + pr/509/docs/search.index/2b4f77bea34a.js | 1 + pr/509/docs/search.index/2c5dcb185a0c.js | 1 + pr/509/docs/search.index/2d5000411b5a.js | 1 + pr/509/docs/search.index/2e4ab4883d2f.js | 1 + pr/509/docs/search.index/2ef7b6b51b41.js | 1 + pr/509/docs/search.index/30aa3843eff5.js | 1 + pr/509/docs/search.index/30e186f1b9e2.js | 1 + pr/509/docs/search.index/315e1edbed3e.js | 1 + pr/509/docs/search.index/323e46e00169.js | 1 + pr/509/docs/search.index/3289e9ef46be.js | 1 + pr/509/docs/search.index/337c243b1b41.js | 1 + pr/509/docs/search.index/34116ea5fc40.js | 1 + pr/509/docs/search.index/34d132c9cb45.js | 1 + pr/509/docs/search.index/352e0d990534.js | 1 + pr/509/docs/search.index/358ecde5a5a1.js | 1 + pr/509/docs/search.index/36786b3fe579.js | 1 + pr/509/docs/search.index/3884b95c3dd3.js | 1 + pr/509/docs/search.index/3889638da158.js | 1 + pr/509/docs/search.index/38b550452ed6.js | 1 + pr/509/docs/search.index/38d210a6c887.js | 1 + pr/509/docs/search.index/394aea3c11a3.js | 1 + pr/509/docs/search.index/3965b74da6b0.js | 1 + pr/509/docs/search.index/39dc67d07722.js | 1 + pr/509/docs/search.index/3a250acbb1fb.js | 1 + pr/509/docs/search.index/3b41ad6096cb.js | 1 + pr/509/docs/search.index/3b6343e13631.js | 1 + pr/509/docs/search.index/3bc9fac5887b.js | 1 + pr/509/docs/search.index/3c467f5cbf13.js | 1 + pr/509/docs/search.index/3c6fb78251b2.js | 1 + pr/509/docs/search.index/3e97732e8a32.js | 1 + pr/509/docs/search.index/3ee5422cd9bc.js | 1 + pr/509/docs/search.index/3eea839e1662.js | 1 + pr/509/docs/search.index/3fe3c85d7012.js | 1 + pr/509/docs/search.index/40de1d99c33a.js | 1 + pr/509/docs/search.index/4137d3501001.js | 1 + pr/509/docs/search.index/41a15bcea3f9.js | 1 + pr/509/docs/search.index/41dc73cd19e1.js | 1 + pr/509/docs/search.index/4215b1682697.js | 1 + pr/509/docs/search.index/424d5a7a4fa1.js | 1 + pr/509/docs/search.index/4279670fcfbe.js | 1 + pr/509/docs/search.index/427bea082f92.js | 1 + pr/509/docs/search.index/43d54762ebae.js | 1 + pr/509/docs/search.index/43d7eeba4357.js | 1 + pr/509/docs/search.index/4417fc0d17a5.js | 1 + pr/509/docs/search.index/444aae938267.js | 1 + pr/509/docs/search.index/44cfcd43b029.js | 1 + pr/509/docs/search.index/45261c45eedf.js | 1 + pr/509/docs/search.index/4656a4fb98bd.js | 1 + pr/509/docs/search.index/46b2a1fc2fe3.js | 1 + pr/509/docs/search.index/471028459586.js | 1 + pr/509/docs/search.index/4763fd511429.js | 1 + pr/509/docs/search.index/477aba3c874c.js | 1 + pr/509/docs/search.index/47876021cef2.js | 1 + pr/509/docs/search.index/48234c118f23.js | 1 + pr/509/docs/search.index/48ea2bc1c340.js | 1 + pr/509/docs/search.index/49462535504e.js | 1 + pr/509/docs/search.index/498baefc1eaa.js | 1 + pr/509/docs/search.index/4a2f3dcae3f4.js | 1 + pr/509/docs/search.index/4a39f4f617e6.js | 1 + pr/509/docs/search.index/4a715899379d.js | 1 + pr/509/docs/search.index/4b1b519a3f3d.js | 1 + pr/509/docs/search.index/4b1ce8d67124.js | 1 + pr/509/docs/search.index/4bc8bd135a43.js | 1 + pr/509/docs/search.index/4c7fab31325f.js | 1 + pr/509/docs/search.index/4c970f423583.js | 1 + pr/509/docs/search.index/4dcbd5c1822e.js | 1 + pr/509/docs/search.index/4dd7e14fd0c3.js | 1 + pr/509/docs/search.index/4e1e65dd0971.js | 1 + pr/509/docs/search.index/4e81395114a0.js | 1 + pr/509/docs/search.index/4f1c4cfa01fd.js | 1 + pr/509/docs/search.index/4f1dbded47de.js | 1 + pr/509/docs/search.index/4f6d4bec726c.js | 1 + pr/509/docs/search.index/501107c77886.js | 1 + pr/509/docs/search.index/5017d5163d40.js | 1 + pr/509/docs/search.index/5041ebb4629e.js | 1 + pr/509/docs/search.index/50ff48e6df46.js | 1 + pr/509/docs/search.index/51394b8419e6.js | 1 + pr/509/docs/search.index/5188e972bd4c.js | 1 + pr/509/docs/search.index/51a24df5c4b4.js | 1 + pr/509/docs/search.index/5206354980a8.js | 1 + pr/509/docs/search.index/5240bbf87b4a.js | 1 + pr/509/docs/search.index/5244d6639a96.js | 1 + pr/509/docs/search.index/52c94dd0f0aa.js | 1 + pr/509/docs/search.index/52d9d2ca2fb3.js | 1 + pr/509/docs/search.index/52e3970e8139.js | 1 + pr/509/docs/search.index/52f9dda67630.js | 1 + pr/509/docs/search.index/550e643162da.js | 1 + pr/509/docs/search.index/564d23e1fbb1.js | 1 + pr/509/docs/search.index/566273acdbeb.js | 1 + pr/509/docs/search.index/56ea93cff34a.js | 1 + pr/509/docs/search.index/57e0023cd6b3.js | 1 + pr/509/docs/search.index/57f16691bc95.js | 1 + pr/509/docs/search.index/59cb3d108e66.js | 1 + pr/509/docs/search.index/5a87fbcb1ab9.js | 1 + pr/509/docs/search.index/5aa9de2b1ec7.js | 1 + pr/509/docs/search.index/5b9b294539ab.js | 1 + pr/509/docs/search.index/5c1fe6b3e4a8.js | 1 + pr/509/docs/search.index/5efb7b1e3360.js | 1 + pr/509/docs/search.index/5f36959d2de3.js | 1 + pr/509/docs/search.index/611ff1ea6784.js | 1 + pr/509/docs/search.index/61b4c5b2a89e.js | 1 + pr/509/docs/search.index/61fe3550b1f8.js | 1 + pr/509/docs/search.index/6386aba6a284.js | 1 + pr/509/docs/search.index/6410b272eb51.js | 1 + pr/509/docs/search.index/64b4fe03993a.js | 1 + pr/509/docs/search.index/64d9a6218304.js | 1 + pr/509/docs/search.index/665ebf7362b0.js | 1 + pr/509/docs/search.index/669a36c47371.js | 1 + pr/509/docs/search.index/674f02e77287.js | 1 + pr/509/docs/search.index/689d2c6c80c6.js | 1 + pr/509/docs/search.index/68b6cd216296.js | 1 + pr/509/docs/search.index/69496b573a06.js | 1 + pr/509/docs/search.index/69993716acd0.js | 1 + pr/509/docs/search.index/6b029a433070.js | 1 + pr/509/docs/search.index/6d657b4263d8.js | 1 + pr/509/docs/search.index/6fe836e11237.js | 1 + pr/509/docs/search.index/711a3a237476.js | 1 + pr/509/docs/search.index/714a27f59b30.js | 1 + pr/509/docs/search.index/7250c12d601c.js | 1 + pr/509/docs/search.index/72d51fa47912.js | 1 + pr/509/docs/search.index/74a202ff2fce.js | 1 + pr/509/docs/search.index/74ebe484d8fb.js | 1 + pr/509/docs/search.index/74fff8bb6461.js | 1 + pr/509/docs/search.index/7511c2f31675.js | 1 + pr/509/docs/search.index/7545996f0dd7.js | 1 + pr/509/docs/search.index/758f270c34be.js | 1 + pr/509/docs/search.index/7643889a294b.js | 1 + pr/509/docs/search.index/768d33a8e276.js | 1 + pr/509/docs/search.index/768fd5752adb.js | 1 + pr/509/docs/search.index/77136bfd9216.js | 1 + pr/509/docs/search.index/77277d28d69a.js | 1 + pr/509/docs/search.index/779f19a4182a.js | 1 + pr/509/docs/search.index/791ca440f256.js | 1 + pr/509/docs/search.index/7a10444b994a.js | 1 + pr/509/docs/search.index/7abae646c037.js | 1 + pr/509/docs/search.index/7c04cb118ed6.js | 1 + pr/509/docs/search.index/7cb7d80cd09e.js | 1 + pr/509/docs/search.index/7cb8f8f69140.js | 1 + pr/509/docs/search.index/7da1c8fa437d.js | 1 + pr/509/docs/search.index/7df5bd0efad4.js | 1 + pr/509/docs/search.index/7f5125453ec9.js | 1 + pr/509/docs/search.index/7fb39206b4b2.js | 1 + pr/509/docs/search.index/7fc9cfd68b15.js | 1 + pr/509/docs/search.index/7fe8c77cc844.js | 1 + .../docs/search.index/alias/a762150c532c.js | 1 + .../search.index/crateNames/2450c9cb4357.js | 1 + pr/509/docs/search.index/desc/0f6717b5c42f.js | 1 + .../docs/search.index/entry/3e7a9e6dac3d.js | 1 + .../search.index/function/c234980f56b0.js | 1 + .../generic_inverted_index/b518a2effe00.js | 1 + pr/509/docs/search.index/name/07d6c6249cec.js | 1 + .../normalizedName/dd897f13bf77.js | 1 + pr/509/docs/search.index/path/772450b83c16.js | 1 + pr/509/docs/search.index/root.js | 1 + pr/509/docs/search.index/type/f7cf3408e9aa.js | 1 + pr/509/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/509/docs/set_up_connection/fn.client.html | 1 + pr/509/docs/set_up_connection/fn.main.html | 1 + pr/509/docs/set_up_connection/fn.server.html | 1 + pr/509/docs/set_up_connection/index.html | 1 + .../docs/set_up_connection/sidebar-items.js | 1 + pr/509/docs/settings.html | 1 + pr/509/docs/src-files.js | 2 + pr/509/docs/src/bench/lib.rs.html | 256 + pr/509/docs/src/bench/stats.rs.html | 95 + pr/509/docs/src/bulk/bulk.rs.html | 219 + .../docs/src/certificate/certificate.rs.html | 106 + .../src/data_transfer/data-transfer.rs.html | 56 + pr/509/docs/src/noq/connection.rs.html | 1839 ++++ pr/509/docs/src/noq/endpoint.rs.html | 968 +++ pr/509/docs/src/noq/incoming.rs.html | 160 + pr/509/docs/src/noq/lib.rs.html | 140 + pr/509/docs/src/noq/mutex.rs.html | 163 + pr/509/docs/src/noq/path.rs.html | 421 + pr/509/docs/src/noq/recv_stream.rs.html | 759 ++ pr/509/docs/src/noq/runtime/mod.rs.html | 251 + pr/509/docs/src/noq/runtime/smol.rs.html | 102 + pr/509/docs/src/noq/runtime/tokio.rs.html | 106 + pr/509/docs/src/noq/send_stream.rs.html | 482 ++ pr/509/docs/src/noq/work_limiter.rs.html | 224 + pr/509/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 | 180 + pr/509/docs/src/noq_proto/cid_queue.rs.html | 517 ++ pr/509/docs/src/noq_proto/coding.rs.html | 152 + pr/509/docs/src/noq_proto/config/mod.rs.html | 688 ++ pr/509/docs/src/noq_proto/config/qlog.rs.html | 173 + .../src/noq_proto/config/transport.rs.html | 912 ++ pr/509/docs/src/noq_proto/congestion.rs.html | 112 + .../congestion/bbr/bw_estimation.rs.html | 100 + .../noq_proto/congestion/bbr/min_max.rs.html | 152 + .../src/noq_proto/congestion/bbr/mod.rs.html | 651 ++ .../src/noq_proto/congestion/cubic.rs.html | 299 + .../src/noq_proto/congestion/new_reno.rs.html | 173 + .../connection/ack_frequency.rs.html | 159 + .../noq_proto/connection/assembler.rs.html | 845 ++ .../noq_proto/connection/cid_state.rs.html | 228 + .../noq_proto/connection/datagrams.rs.html | 221 + .../docs/src/noq_proto/connection/mod.rs.html | 7410 +++++++++++++++++ .../src/noq_proto/connection/mtud.rs.html | 982 +++ .../src/noq_proto/connection/pacing.rs.html | 325 + .../connection/packet_builder.rs.html | 432 + .../connection/packet_crypto.rs.html | 501 ++ .../src/noq_proto/connection/paths.rs.html | 1091 +++ .../src/noq_proto/connection/qlog.rs.html | 1280 +++ .../noq_proto/connection/send_buffer.rs.html | 762 ++ .../src/noq_proto/connection/spaces.rs.html | 1533 ++++ .../src/noq_proto/connection/state.rs.html | 433 + .../src/noq_proto/connection/stats.rs.html | 205 + .../noq_proto/connection/streams/mod.rs.html | 554 ++ .../noq_proto/connection/streams/recv.rs.html | 544 ++ .../noq_proto/connection/streams/send.rs.html | 414 + .../connection/streams/state.rs.html | 2032 +++++ .../src/noq_proto/connection/timer.rs.html | 498 ++ .../noq_proto/connection/transmit_buf.rs.html | 236 + .../docs/src/noq_proto/constant_time.rs.html | 22 + pr/509/docs/src/noq_proto/crypto.rs.html | 228 + .../src/noq_proto/crypto/ring_like.rs.html | 77 + .../docs/src/noq_proto/crypto/rustls.rs.html | 680 ++ pr/509/docs/src/noq_proto/endpoint.rs.html | 1633 ++++ pr/509/docs/src/noq_proto/frame.rs.html | 2828 +++++++ pr/509/docs/src/noq_proto/lib.rs.html | 435 + .../src/noq_proto/n0_nat_traversal.rs.html | 578 ++ pr/509/docs/src/noq_proto/packet.rs.html | 1089 +++ .../range_set/array_range_set.rs.html | 253 + .../docs/src/noq_proto/range_set/mod.rs.html | 5 + pr/509/docs/src/noq_proto/shared.rs.html | 208 + pr/509/docs/src/noq_proto/token.rs.html | 491 ++ .../src/noq_proto/token_memory_cache.rs.html | 247 + .../src/noq_proto/transport_error.rs.html | 166 + .../noq_proto/transport_parameters.rs.html | 977 +++ pr/509/docs/src/noq_proto/varint.rs.html | 233 + pr/509/docs/src/noq_udp/cmsg/mod.rs.html | 157 + pr/509/docs/src/noq_udp/cmsg/unix.rs.html | 81 + pr/509/docs/src/noq_udp/lib.rs.html | 301 + pr/509/docs/src/noq_udp/unix.rs.html | 1077 +++ pr/509/docs/src/perf/client.rs.html | 419 + pr/509/docs/src/perf/lib.rs.html | 219 + pr/509/docs/src/perf/noprotection.rs.html | 205 + pr/509/docs/src/perf/server.rs.html | 202 + pr/509/docs/src/perf/stats.rs.html | 413 + .../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/509/docs/static.files/favicon-044be391.svg | 24 + .../static.files/favicon-32x32-eab170b8.png | Bin 0 -> 690 bytes pr/509/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/509/docs/static.files/rustdoc-ca0dd0c4.css | 86 + .../static.files/scrape-examples-2bbcccac.js | 1 + pr/509/docs/static.files/search-8d3311b9.js | 5 + pr/509/docs/static.files/settings-c38705f0.js | 17 + .../docs/static.files/src-script-813739b1.js | 1 + pr/509/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/509/docs/trait.impl/core/cmp/trait.Eq.js | 9 + pr/509/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 + .../docs/trait.impl/core/ops/bit/trait.Not.js | 9 + .../trait.impl/core/ops/deref/trait.Deref.js | 9 + .../core/ops/deref/trait.DerefMut.js | 9 + .../trait.impl/core/ops/drop/trait.Drop.js | 9 + .../trait.impl/core/ops/index/trait.Index.js | 9 + .../core/ops/index/trait.IndexMut.js | 9 + .../panic/unwind_safe/trait.RefUnwindSafe.js | 9 + .../panic/unwind_safe/trait.UnwindSafe.js | 9 + .../core/str/traits/trait.FromStr.js | 9 + .../futures_core/stream/trait.Stream.js | 9 + .../futures_io/if_std/trait.AsyncRead.js | 9 + .../futures_io/if_std/trait.AsyncWrite.js | 9 + .../identity_hash/trait.IdentityHashable.js | 9 + .../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 + 922 files changed, 66223 insertions(+) create mode 100644 pr/509/docs/.lock create mode 100644 pr/509/docs/bench/all.html create mode 100644 pr/509/docs/bench/enum.CipherSuite.html create mode 100644 pr/509/docs/bench/fn.configure_tracing_subscriber.html create mode 100644 pr/509/docs/bench/fn.connect_client.html create mode 100644 pr/509/docs/bench/fn.drain_stream.html create mode 100644 pr/509/docs/bench/fn.rt.html create mode 100644 pr/509/docs/bench/fn.send_data_on_stream.html create mode 100644 pr/509/docs/bench/fn.server_endpoint.html create mode 100644 pr/509/docs/bench/fn.transport_config.html create mode 100644 pr/509/docs/bench/index.html create mode 100644 pr/509/docs/bench/sidebar-items.js create mode 100644 pr/509/docs/bench/stats/fn.throughput_bps.html create mode 100644 pr/509/docs/bench/stats/index.html create mode 100644 pr/509/docs/bench/stats/sidebar-items.js create mode 100644 pr/509/docs/bench/stats/struct.Stats.html create mode 100644 pr/509/docs/bench/stats/struct.StreamStats.html create mode 100644 pr/509/docs/bench/stats/struct.TransferResult.html create mode 100644 pr/509/docs/bench/struct.Opt.html create mode 100644 pr/509/docs/bulk/all.html create mode 100644 pr/509/docs/bulk/fn.client.html create mode 100644 pr/509/docs/bulk/fn.handle_client_stream.html create mode 100644 pr/509/docs/bulk/fn.main.html create mode 100644 pr/509/docs/bulk/fn.server.html create mode 100644 pr/509/docs/bulk/index.html create mode 100644 pr/509/docs/bulk/sidebar-items.js create mode 100644 pr/509/docs/bulk/struct.ClientStats.html create mode 100644 pr/509/docs/certificate/all.html create mode 100644 pr/509/docs/certificate/fn.configure_client.html create mode 100644 pr/509/docs/certificate/fn.generate_self_signed_cert.html create mode 100644 pr/509/docs/certificate/fn.main.html create mode 100644 pr/509/docs/certificate/fn.read_certs_from_file.html create mode 100644 pr/509/docs/certificate/index.html create mode 100644 pr/509/docs/certificate/sidebar-items.js create mode 100644 pr/509/docs/certificate/struct.SkipServerVerification.html create mode 100644 pr/509/docs/crates.js create mode 100644 pr/509/docs/data_transfer/all.html create mode 100644 pr/509/docs/data_transfer/fn.main.html create mode 100644 pr/509/docs/data_transfer/fn.open_bidirectional_stream.html create mode 100644 pr/509/docs/data_transfer/fn.open_unidirectional_stream.html create mode 100644 pr/509/docs/data_transfer/fn.receive_bidirectional_stream.html create mode 100644 pr/509/docs/data_transfer/fn.receive_datagram.html create mode 100644 pr/509/docs/data_transfer/fn.receive_unidirectional_stream.html create mode 100644 pr/509/docs/data_transfer/fn.send_unreliable.html create mode 100644 pr/509/docs/data_transfer/index.html create mode 100644 pr/509/docs/data_transfer/sidebar-items.js create mode 100644 pr/509/docs/help.html create mode 100644 pr/509/docs/noq/all.html create mode 100644 pr/509/docs/noq/congestion/index.html create mode 100644 pr/509/docs/noq/congestion/sidebar-items.js create mode 100644 pr/509/docs/noq/congestion/struct.Bbr.html create mode 100644 pr/509/docs/noq/congestion/struct.BbrConfig.html create mode 100644 pr/509/docs/noq/congestion/struct.ControllerMetrics.html create mode 100644 pr/509/docs/noq/congestion/struct.Cubic.html create mode 100644 pr/509/docs/noq/congestion/struct.CubicConfig.html create mode 100644 pr/509/docs/noq/congestion/struct.NewReno.html create mode 100644 pr/509/docs/noq/congestion/struct.NewRenoConfig.html create mode 100644 pr/509/docs/noq/congestion/trait.Controller.html create mode 100644 pr/509/docs/noq/congestion/trait.ControllerFactory.html create mode 100644 pr/509/docs/noq/connection/enum.SendDatagramError.html create mode 100644 pr/509/docs/noq/connection/struct.AcceptBi.html create mode 100644 pr/509/docs/noq/connection/struct.AcceptUni.html create mode 100644 pr/509/docs/noq/connection/struct.Connecting.html create mode 100644 pr/509/docs/noq/connection/struct.Connection.html create mode 100644 pr/509/docs/noq/connection/struct.OnClosed.html create mode 100644 pr/509/docs/noq/connection/struct.OpenBi.html create mode 100644 pr/509/docs/noq/connection/struct.OpenUni.html create mode 100644 pr/509/docs/noq/connection/struct.ReadDatagram.html create mode 100644 pr/509/docs/noq/connection/struct.SendDatagram.html create mode 100644 pr/509/docs/noq/connection/struct.WeakConnectionHandle.html create mode 100644 pr/509/docs/noq/connection/struct.ZeroRttAccepted.html create mode 100644 pr/509/docs/noq/crypto/index.html create mode 100644 pr/509/docs/noq/crypto/rustls/enum.Error.html create mode 100644 pr/509/docs/noq/crypto/rustls/index.html create mode 100644 pr/509/docs/noq/crypto/rustls/sidebar-items.js create mode 100644 pr/509/docs/noq/crypto/rustls/struct.HandshakeData.html create mode 100644 pr/509/docs/noq/crypto/rustls/struct.NoInitialCipherSuite.html create mode 100644 pr/509/docs/noq/crypto/rustls/struct.QuicClientConfig.html create mode 100644 pr/509/docs/noq/crypto/rustls/struct.QuicServerConfig.html create mode 100644 pr/509/docs/noq/crypto/rustls/struct.TlsSession.html create mode 100644 pr/509/docs/noq/crypto/sidebar-items.js create mode 100644 pr/509/docs/noq/crypto/struct.CryptoError.html create mode 100644 pr/509/docs/noq/crypto/struct.ExportKeyingMaterialError.html create mode 100644 pr/509/docs/noq/crypto/struct.KeyPair.html create mode 100644 pr/509/docs/noq/crypto/struct.Keys.html create mode 100644 pr/509/docs/noq/crypto/struct.UnsupportedVersion.html create mode 100644 pr/509/docs/noq/crypto/trait.ClientConfig.html create mode 100644 pr/509/docs/noq/crypto/trait.HandshakeTokenKey.html create mode 100644 pr/509/docs/noq/crypto/trait.HeaderKey.html create mode 100644 pr/509/docs/noq/crypto/trait.HmacKey.html create mode 100644 pr/509/docs/noq/crypto/trait.PacketKey.html create mode 100644 pr/509/docs/noq/crypto/trait.ServerConfig.html create mode 100644 pr/509/docs/noq/crypto/trait.Session.html create mode 100644 pr/509/docs/noq/endpoint/struct.Accept.html create mode 100644 pr/509/docs/noq/endpoint/struct.Endpoint.html create mode 100644 pr/509/docs/noq/endpoint/struct.EndpointStats.html create mode 100644 pr/509/docs/noq/enum.ConfigError.html create mode 100644 pr/509/docs/noq/enum.ConnectError.html create mode 100644 pr/509/docs/noq/enum.ConnectionError.html create mode 100644 pr/509/docs/noq/enum.Dir.html create mode 100644 pr/509/docs/noq/enum.EcnCodepoint.html create mode 100644 pr/509/docs/noq/enum.FrameType.html create mode 100644 pr/509/docs/noq/enum.ReadError.html create mode 100644 pr/509/docs/noq/enum.ReadExactError.html create mode 100644 pr/509/docs/noq/enum.ReadToEndError.html create mode 100644 pr/509/docs/noq/enum.ResetError.html create mode 100644 pr/509/docs/noq/enum.SendDatagramError.html create mode 100644 pr/509/docs/noq/enum.Side.html create mode 100644 pr/509/docs/noq/enum.StoppedError.html create mode 100644 pr/509/docs/noq/enum.WriteError.html create mode 100644 pr/509/docs/noq/fn.default_runtime.html create mode 100644 pr/509/docs/noq/incoming/struct.Incoming.html create mode 100644 pr/509/docs/noq/incoming/struct.IncomingFuture.html create mode 100644 pr/509/docs/noq/incoming/struct.RetryError.html create mode 100644 pr/509/docs/noq/index.html create mode 100644 pr/509/docs/noq/path/struct.AddressDiscovery.html create mode 100644 pr/509/docs/noq/path/struct.OpenPath.html create mode 100644 pr/509/docs/noq/path/struct.Path.html create mode 100644 pr/509/docs/noq/path/struct.WeakPathHandle.html create mode 100644 pr/509/docs/noq/recv_stream/enum.ReadError.html create mode 100644 pr/509/docs/noq/recv_stream/enum.ReadExactError.html create mode 100644 pr/509/docs/noq/recv_stream/enum.ReadToEndError.html create mode 100644 pr/509/docs/noq/recv_stream/enum.ResetError.html create mode 100644 pr/509/docs/noq/recv_stream/struct.RecvStream.html create mode 100644 pr/509/docs/noq/recv_stream/struct.UnorderedRecvStream.html create mode 100644 pr/509/docs/noq/runtime/fn.default_runtime.html create mode 100644 pr/509/docs/noq/runtime/smol/struct.SmolRuntime.html create mode 100644 pr/509/docs/noq/runtime/tokio/struct.TokioRuntime.html create mode 100644 pr/509/docs/noq/runtime/trait.AsyncTimer.html create mode 100644 pr/509/docs/noq/runtime/trait.AsyncUdpSocket.html create mode 100644 pr/509/docs/noq/runtime/trait.Runtime.html create mode 100644 pr/509/docs/noq/runtime/trait.UdpSender.html create mode 100644 pr/509/docs/noq/send_stream/enum.StoppedError.html create mode 100644 pr/509/docs/noq/send_stream/enum.WriteError.html create mode 100644 pr/509/docs/noq/send_stream/struct.SendStream.html create mode 100644 pr/509/docs/noq/send_stream/struct.Stopped.html create mode 100644 pr/509/docs/noq/sidebar-items.js create mode 100644 pr/509/docs/noq/struct.Accept.html create mode 100644 pr/509/docs/noq/struct.AcceptBi.html create mode 100644 pr/509/docs/noq/struct.AcceptUni.html create mode 100644 pr/509/docs/noq/struct.AckFrequencyConfig.html create mode 100644 pr/509/docs/noq/struct.AddressDiscovery.html create mode 100644 pr/509/docs/noq/struct.ApplicationClose.html create mode 100644 pr/509/docs/noq/struct.BloomTokenLog.html create mode 100644 pr/509/docs/noq/struct.Chunk.html create mode 100644 pr/509/docs/noq/struct.ClientConfig.html create mode 100644 pr/509/docs/noq/struct.ClosedStream.html create mode 100644 pr/509/docs/noq/struct.Connecting.html create mode 100644 pr/509/docs/noq/struct.Connection.html create mode 100644 pr/509/docs/noq/struct.ConnectionClose.html create mode 100644 pr/509/docs/noq/struct.ConnectionId.html create mode 100644 pr/509/docs/noq/struct.ConnectionStats.html create mode 100644 pr/509/docs/noq/struct.Endpoint.html create mode 100644 pr/509/docs/noq/struct.EndpointConfig.html create mode 100644 pr/509/docs/noq/struct.EndpointStats.html create mode 100644 pr/509/docs/noq/struct.FrameStats.html create mode 100644 pr/509/docs/noq/struct.IdleTimeout.html create mode 100644 pr/509/docs/noq/struct.Incoming.html create mode 100644 pr/509/docs/noq/struct.IncomingFuture.html create mode 100644 pr/509/docs/noq/struct.InvalidCid.html create mode 100644 pr/509/docs/noq/struct.MtuDiscoveryConfig.html create mode 100644 pr/509/docs/noq/struct.NoneTokenLog.html create mode 100644 pr/509/docs/noq/struct.NoneTokenStore.html create mode 100644 pr/509/docs/noq/struct.OnClosed.html create mode 100644 pr/509/docs/noq/struct.OpenBi.html create mode 100644 pr/509/docs/noq/struct.OpenPath.html create mode 100644 pr/509/docs/noq/struct.OpenUni.html create mode 100644 pr/509/docs/noq/struct.Path.html create mode 100644 pr/509/docs/noq/struct.PathId.html create mode 100644 pr/509/docs/noq/struct.PathStats.html create mode 100644 pr/509/docs/noq/struct.QlogConfig.html create mode 100644 pr/509/docs/noq/struct.QlogFileFactory.html create mode 100644 pr/509/docs/noq/struct.ReadDatagram.html create mode 100644 pr/509/docs/noq/struct.RecvStream.html create mode 100644 pr/509/docs/noq/struct.RetryError.html create mode 100644 pr/509/docs/noq/struct.SendDatagram.html create mode 100644 pr/509/docs/noq/struct.SendStream.html create mode 100644 pr/509/docs/noq/struct.ServerConfig.html create mode 100644 pr/509/docs/noq/struct.SmolRuntime.html create mode 100644 pr/509/docs/noq/struct.StdSystemTime.html create mode 100644 pr/509/docs/noq/struct.Stopped.html create mode 100644 pr/509/docs/noq/struct.StreamId.html create mode 100644 pr/509/docs/noq/struct.TokenMemoryCache.html create mode 100644 pr/509/docs/noq/struct.TokenReuseError.html create mode 100644 pr/509/docs/noq/struct.TokioRuntime.html create mode 100644 pr/509/docs/noq/struct.Transmit.html create mode 100644 pr/509/docs/noq/struct.TransportConfig.html create mode 100644 pr/509/docs/noq/struct.TransportErrorCode.html create mode 100644 pr/509/docs/noq/struct.UdpStats.html create mode 100644 pr/509/docs/noq/struct.UnorderedRecvStream.html create mode 100644 pr/509/docs/noq/struct.ValidationTokenConfig.html create mode 100644 pr/509/docs/noq/struct.VarInt.html create mode 100644 pr/509/docs/noq/struct.VarIntBoundsExceeded.html create mode 100644 pr/509/docs/noq/struct.WeakConnectionHandle.html create mode 100644 pr/509/docs/noq/struct.WeakPathHandle.html create mode 100644 pr/509/docs/noq/struct.Written.html create mode 100644 pr/509/docs/noq/struct.ZeroRttAccepted.html create mode 100644 pr/509/docs/noq/trait.AsyncTimer.html create mode 100644 pr/509/docs/noq/trait.AsyncUdpSocket.html create mode 100644 pr/509/docs/noq/trait.ConnectionIdGenerator.html create mode 100644 pr/509/docs/noq/trait.NetworkChangeHint.html create mode 100644 pr/509/docs/noq/trait.QlogFactory.html create mode 100644 pr/509/docs/noq/trait.Runtime.html create mode 100644 pr/509/docs/noq/trait.TimeSource.html create mode 100644 pr/509/docs/noq/trait.TokenLog.html create mode 100644 pr/509/docs/noq/trait.TokenStore.html create mode 100644 pr/509/docs/noq/trait.UdpSender.html create mode 100644 pr/509/docs/noq_perf/all.html create mode 100644 pr/509/docs/noq_perf/enum.Commands.html create mode 100644 pr/509/docs/noq_perf/fn.main.html create mode 100644 pr/509/docs/noq_perf/index.html create mode 100644 pr/509/docs/noq_perf/sidebar-items.js create mode 100644 pr/509/docs/noq_perf/struct.Cli.html create mode 100644 pr/509/docs/noq_proto/all.html create mode 100644 pr/509/docs/noq_proto/bench_exports/index.html create mode 100644 pr/509/docs/noq_proto/bench_exports/send_buffer_benches/fn.get_into_many_segments.html create mode 100644 pr/509/docs/noq_proto/bench_exports/send_buffer_benches/fn.get_loop_many_segments.html create mode 100644 pr/509/docs/noq_proto/bench_exports/send_buffer_benches/index.html create mode 100644 pr/509/docs/noq_proto/bench_exports/send_buffer_benches/sidebar-items.js create mode 100644 pr/509/docs/noq_proto/bench_exports/sidebar-items.js create mode 100644 pr/509/docs/noq_proto/bloom_token_log/struct.BloomTokenLog.html create mode 100644 pr/509/docs/noq_proto/cid_generator/struct.HashedConnectionIdGenerator.html create mode 100644 pr/509/docs/noq_proto/cid_generator/struct.InvalidCid.html create mode 100644 pr/509/docs/noq_proto/cid_generator/struct.RandomConnectionIdGenerator.html create mode 100644 pr/509/docs/noq_proto/cid_generator/trait.ConnectionIdGenerator.html create mode 100644 pr/509/docs/noq_proto/coding/index.html create mode 100644 pr/509/docs/noq_proto/coding/sidebar-items.js create mode 100644 pr/509/docs/noq_proto/coding/struct.UnexpectedEnd.html create mode 100644 pr/509/docs/noq_proto/coding/trait.Decodable.html create mode 100644 pr/509/docs/noq_proto/coding/trait.Encodable.html create mode 100644 pr/509/docs/noq_proto/coding/type.Result.html create mode 100644 pr/509/docs/noq_proto/config/enum.ConfigError.html create mode 100644 pr/509/docs/noq_proto/config/qlog/struct.QlogConfig.html create mode 100644 pr/509/docs/noq_proto/config/qlog/struct.QlogFileFactory.html create mode 100644 pr/509/docs/noq_proto/config/qlog/trait.QlogFactory.html create mode 100644 pr/509/docs/noq_proto/config/struct.ClientConfig.html create mode 100644 pr/509/docs/noq_proto/config/struct.EndpointConfig.html create mode 100644 pr/509/docs/noq_proto/config/struct.ServerConfig.html create mode 100644 pr/509/docs/noq_proto/config/struct.StdSystemTime.html create mode 100644 pr/509/docs/noq_proto/config/struct.ValidationTokenConfig.html create mode 100644 pr/509/docs/noq_proto/config/trait.TimeSource.html create mode 100644 pr/509/docs/noq_proto/config/transport/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html create mode 100644 pr/509/docs/noq_proto/config/transport/struct.AckFrequencyConfig.html create mode 100644 pr/509/docs/noq_proto/config/transport/struct.IdleTimeout.html create mode 100644 pr/509/docs/noq_proto/config/transport/struct.MtuDiscoveryConfig.html create mode 100644 pr/509/docs/noq_proto/config/transport/struct.TransportConfig.html create mode 100644 pr/509/docs/noq_proto/congestion/bbr/struct.Bbr.html create mode 100644 pr/509/docs/noq_proto/congestion/bbr/struct.BbrConfig.html create mode 100644 pr/509/docs/noq_proto/congestion/cubic/struct.Cubic.html create mode 100644 pr/509/docs/noq_proto/congestion/cubic/struct.CubicConfig.html create mode 100644 pr/509/docs/noq_proto/congestion/index.html create mode 100644 pr/509/docs/noq_proto/congestion/new_reno/struct.NewReno.html create mode 100644 pr/509/docs/noq_proto/congestion/new_reno/struct.NewRenoConfig.html create mode 100644 pr/509/docs/noq_proto/congestion/sidebar-items.js create mode 100644 pr/509/docs/noq_proto/congestion/struct.Bbr.html create mode 100644 pr/509/docs/noq_proto/congestion/struct.BbrConfig.html create mode 100644 pr/509/docs/noq_proto/congestion/struct.ControllerMetrics.html create mode 100644 pr/509/docs/noq_proto/congestion/struct.Cubic.html create mode 100644 pr/509/docs/noq_proto/congestion/struct.CubicConfig.html create mode 100644 pr/509/docs/noq_proto/congestion/struct.NewReno.html create mode 100644 pr/509/docs/noq_proto/congestion/struct.NewRenoConfig.html create mode 100644 pr/509/docs/noq_proto/congestion/trait.Controller.html create mode 100644 pr/509/docs/noq_proto/congestion/trait.ControllerFactory.html create mode 100644 pr/509/docs/noq_proto/connection/assembler/struct.Chunk.html create mode 100644 pr/509/docs/noq_proto/connection/datagrams/enum.SendDatagramError.html create mode 100644 pr/509/docs/noq_proto/connection/datagrams/struct.Datagrams.html create mode 100644 pr/509/docs/noq_proto/connection/enum.ClosePathError.html create mode 100644 pr/509/docs/noq_proto/connection/enum.ConnectionError.html create mode 100644 pr/509/docs/noq_proto/connection/enum.Event.html create mode 100644 pr/509/docs/noq_proto/connection/enum.PathError.html create mode 100644 pr/509/docs/noq_proto/connection/paths/enum.PathAbandonReason.html create mode 100644 pr/509/docs/noq_proto/connection/paths/enum.PathEvent.html create mode 100644 pr/509/docs/noq_proto/connection/paths/enum.PathStatus.html create mode 100644 pr/509/docs/noq_proto/connection/paths/enum.SetPathStatusError.html create mode 100644 pr/509/docs/noq_proto/connection/paths/struct.ClosedPath.html create mode 100644 pr/509/docs/noq_proto/connection/paths/struct.PathId.html create mode 100644 pr/509/docs/noq_proto/connection/paths/struct.RttEstimator.html create mode 100644 pr/509/docs/noq_proto/connection/send_buffer/send_buffer_benches/fn.get_into_many_segments.html create mode 100644 pr/509/docs/noq_proto/connection/send_buffer/send_buffer_benches/fn.get_loop_many_segments.html create mode 100644 pr/509/docs/noq_proto/connection/send_buffer/send_buffer_benches/index.html create mode 100644 pr/509/docs/noq_proto/connection/stats/struct.ConnectionStats.html create mode 100644 pr/509/docs/noq_proto/connection/stats/struct.FrameStats.html create mode 100644 pr/509/docs/noq_proto/connection/stats/struct.PathStats.html create mode 100644 pr/509/docs/noq_proto/connection/stats/struct.UdpStats.html create mode 100644 pr/509/docs/noq_proto/connection/streams/enum.StreamEvent.html create mode 100644 pr/509/docs/noq_proto/connection/streams/recv/enum.ReadError.html create mode 100644 pr/509/docs/noq_proto/connection/streams/recv/enum.ReadableError.html create mode 100644 pr/509/docs/noq_proto/connection/streams/recv/struct.Chunks.html create mode 100644 pr/509/docs/noq_proto/connection/streams/send/enum.FinishError.html create mode 100644 pr/509/docs/noq_proto/connection/streams/send/enum.WriteError.html create mode 100644 pr/509/docs/noq_proto/connection/streams/send/struct.Written.html create mode 100644 pr/509/docs/noq_proto/connection/streams/struct.ClosedStream.html create mode 100644 pr/509/docs/noq_proto/connection/streams/struct.RecvStream.html create mode 100644 pr/509/docs/noq_proto/connection/streams/struct.SendStream.html create mode 100644 pr/509/docs/noq_proto/connection/streams/struct.ShouldTransmit.html create mode 100644 pr/509/docs/noq_proto/connection/streams/struct.Streams.html create mode 100644 pr/509/docs/noq_proto/connection/struct.Connection.html create mode 100644 pr/509/docs/noq_proto/connection/struct.MultipathNotNegotiated.html create mode 100644 pr/509/docs/noq_proto/connection/trait.NetworkChangeHint.html create mode 100644 pr/509/docs/noq_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html create mode 100644 pr/509/docs/noq_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html create mode 100644 pr/509/docs/noq_proto/crypto/index.html create mode 100644 pr/509/docs/noq_proto/crypto/rustls/enum.Error.html create mode 100644 pr/509/docs/noq_proto/crypto/rustls/index.html create mode 100644 pr/509/docs/noq_proto/crypto/rustls/sidebar-items.js create mode 100644 pr/509/docs/noq_proto/crypto/rustls/struct.HandshakeData.html create mode 100644 pr/509/docs/noq_proto/crypto/rustls/struct.NoInitialCipherSuite.html create mode 100644 pr/509/docs/noq_proto/crypto/rustls/struct.QuicClientConfig.html create mode 100644 pr/509/docs/noq_proto/crypto/rustls/struct.QuicServerConfig.html create mode 100644 pr/509/docs/noq_proto/crypto/rustls/struct.TlsSession.html create mode 100644 pr/509/docs/noq_proto/crypto/sidebar-items.js create mode 100644 pr/509/docs/noq_proto/crypto/struct.CryptoError.html create mode 100644 pr/509/docs/noq_proto/crypto/struct.ExportKeyingMaterialError.html create mode 100644 pr/509/docs/noq_proto/crypto/struct.KeyPair.html create mode 100644 pr/509/docs/noq_proto/crypto/struct.Keys.html create mode 100644 pr/509/docs/noq_proto/crypto/struct.UnsupportedVersion.html create mode 100644 pr/509/docs/noq_proto/crypto/trait.ClientConfig.html create mode 100644 pr/509/docs/noq_proto/crypto/trait.HandshakeTokenKey.html create mode 100644 pr/509/docs/noq_proto/crypto/trait.HeaderKey.html create mode 100644 pr/509/docs/noq_proto/crypto/trait.HmacKey.html create mode 100644 pr/509/docs/noq_proto/crypto/trait.PacketKey.html create mode 100644 pr/509/docs/noq_proto/crypto/trait.ServerConfig.html create mode 100644 pr/509/docs/noq_proto/crypto/trait.Session.html create mode 100644 pr/509/docs/noq_proto/endpoint/enum.ConnectError.html create mode 100644 pr/509/docs/noq_proto/endpoint/enum.DatagramEvent.html create mode 100644 pr/509/docs/noq_proto/endpoint/struct.AcceptError.html create mode 100644 pr/509/docs/noq_proto/endpoint/struct.ConnectionHandle.html create mode 100644 pr/509/docs/noq_proto/endpoint/struct.DecryptedInitial.html create mode 100644 pr/509/docs/noq_proto/endpoint/struct.Endpoint.html create mode 100644 pr/509/docs/noq_proto/endpoint/struct.Incoming.html create mode 100644 pr/509/docs/noq_proto/endpoint/struct.IncomingAlpns.html create mode 100644 pr/509/docs/noq_proto/endpoint/struct.RetryError.html create mode 100644 pr/509/docs/noq_proto/enum.ClosePathError.html create mode 100644 pr/509/docs/noq_proto/enum.ConfigError.html create mode 100644 pr/509/docs/noq_proto/enum.ConnectError.html create mode 100644 pr/509/docs/noq_proto/enum.ConnectionError.html create mode 100644 pr/509/docs/noq_proto/enum.DatagramEvent.html create mode 100644 pr/509/docs/noq_proto/enum.Dir.html create mode 100644 pr/509/docs/noq_proto/enum.EcnCodepoint.html create mode 100644 pr/509/docs/noq_proto/enum.Event.html create mode 100644 pr/509/docs/noq_proto/enum.FinishError.html create mode 100644 pr/509/docs/noq_proto/enum.FrameType.html create mode 100644 pr/509/docs/noq_proto/enum.LongType.html create mode 100644 pr/509/docs/noq_proto/enum.MaybeFrame.html create mode 100644 pr/509/docs/noq_proto/enum.PacketDecodeError.html create mode 100644 pr/509/docs/noq_proto/enum.PathAbandonReason.html create mode 100644 pr/509/docs/noq_proto/enum.PathError.html create mode 100644 pr/509/docs/noq_proto/enum.PathEvent.html create mode 100644 pr/509/docs/noq_proto/enum.PathStatus.html create mode 100644 pr/509/docs/noq_proto/enum.ProtectedHeader.html create mode 100644 pr/509/docs/noq_proto/enum.ReadError.html create mode 100644 pr/509/docs/noq_proto/enum.ReadableError.html create mode 100644 pr/509/docs/noq_proto/enum.SendDatagramError.html create mode 100644 pr/509/docs/noq_proto/enum.SetPathStatusError.html create mode 100644 pr/509/docs/noq_proto/enum.Side.html create mode 100644 pr/509/docs/noq_proto/enum.StreamEvent.html create mode 100644 pr/509/docs/noq_proto/enum.WriteError.html create mode 100644 pr/509/docs/noq_proto/frame/enum.FrameType.html create mode 100644 pr/509/docs/noq_proto/frame/enum.MaybeFrame.html create mode 100644 pr/509/docs/noq_proto/frame/struct.ApplicationClose.html create mode 100644 pr/509/docs/noq_proto/frame/struct.ConnectionClose.html create mode 100644 pr/509/docs/noq_proto/frame/struct.Datagram.html create mode 100644 pr/509/docs/noq_proto/frame/struct.DatagramInfo.html create mode 100644 pr/509/docs/noq_proto/frame/struct.InvalidFrameId.html create mode 100644 pr/509/docs/noq_proto/frame/struct.StreamInfo.html create mode 100644 pr/509/docs/noq_proto/index.html create mode 100644 pr/509/docs/noq_proto/n0_nat_traversal/enum.Error.html create mode 100644 pr/509/docs/noq_proto/n0_nat_traversal/enum.Event.html create mode 100644 pr/509/docs/noq_proto/n0_nat_traversal/index.html create mode 100644 pr/509/docs/noq_proto/n0_nat_traversal/sidebar-items.js create mode 100644 pr/509/docs/noq_proto/packet/enum.LongType.html create mode 100644 pr/509/docs/noq_proto/packet/enum.PacketDecodeError.html create mode 100644 pr/509/docs/noq_proto/packet/enum.ProtectedHeader.html create mode 100644 pr/509/docs/noq_proto/packet/struct.FixedLengthConnectionIdParser.html create mode 100644 pr/509/docs/noq_proto/packet/struct.PartialDecode.html create mode 100644 pr/509/docs/noq_proto/packet/struct.ProtectedInitialHeader.html create mode 100644 pr/509/docs/noq_proto/packet/trait.ConnectionIdParser.html create mode 100644 pr/509/docs/noq_proto/shared/enum.EcnCodepoint.html create mode 100644 pr/509/docs/noq_proto/shared/struct.ConnectionEvent.html create mode 100644 pr/509/docs/noq_proto/shared/struct.ConnectionId.html create mode 100644 pr/509/docs/noq_proto/shared/struct.EndpointEvent.html create mode 100644 pr/509/docs/noq_proto/sidebar-items.js create mode 100644 pr/509/docs/noq_proto/struct.AcceptError.html create mode 100644 pr/509/docs/noq_proto/struct.AckFrequencyConfig.html create mode 100644 pr/509/docs/noq_proto/struct.ApplicationClose.html create mode 100644 pr/509/docs/noq_proto/struct.BloomTokenLog.html create mode 100644 pr/509/docs/noq_proto/struct.Chunk.html create mode 100644 pr/509/docs/noq_proto/struct.Chunks.html create mode 100644 pr/509/docs/noq_proto/struct.ClientConfig.html create mode 100644 pr/509/docs/noq_proto/struct.ClosedPath.html create mode 100644 pr/509/docs/noq_proto/struct.ClosedStream.html create mode 100644 pr/509/docs/noq_proto/struct.Connection.html create mode 100644 pr/509/docs/noq_proto/struct.ConnectionClose.html create mode 100644 pr/509/docs/noq_proto/struct.ConnectionEvent.html create mode 100644 pr/509/docs/noq_proto/struct.ConnectionHandle.html create mode 100644 pr/509/docs/noq_proto/struct.ConnectionId.html create mode 100644 pr/509/docs/noq_proto/struct.ConnectionStats.html create mode 100644 pr/509/docs/noq_proto/struct.Datagram.html create mode 100644 pr/509/docs/noq_proto/struct.DatagramInfo.html create mode 100644 pr/509/docs/noq_proto/struct.Datagrams.html create mode 100644 pr/509/docs/noq_proto/struct.DecryptedInitial.html create mode 100644 pr/509/docs/noq_proto/struct.Endpoint.html create mode 100644 pr/509/docs/noq_proto/struct.EndpointConfig.html create mode 100644 pr/509/docs/noq_proto/struct.EndpointEvent.html create mode 100644 pr/509/docs/noq_proto/struct.FixedLengthConnectionIdParser.html create mode 100644 pr/509/docs/noq_proto/struct.FourTuple.html create mode 100644 pr/509/docs/noq_proto/struct.FrameStats.html create mode 100644 pr/509/docs/noq_proto/struct.HashedConnectionIdGenerator.html create mode 100644 pr/509/docs/noq_proto/struct.IdleTimeout.html create mode 100644 pr/509/docs/noq_proto/struct.Incoming.html create mode 100644 pr/509/docs/noq_proto/struct.IncomingAlpns.html create mode 100644 pr/509/docs/noq_proto/struct.InvalidCid.html create mode 100644 pr/509/docs/noq_proto/struct.InvalidFrameId.html create mode 100644 pr/509/docs/noq_proto/struct.MtuDiscoveryConfig.html create mode 100644 pr/509/docs/noq_proto/struct.MultipathNotNegotiated.html create mode 100644 pr/509/docs/noq_proto/struct.NoneTokenLog.html create mode 100644 pr/509/docs/noq_proto/struct.NoneTokenStore.html create mode 100644 pr/509/docs/noq_proto/struct.PartialDecode.html create mode 100644 pr/509/docs/noq_proto/struct.PathId.html create mode 100644 pr/509/docs/noq_proto/struct.PathStats.html create mode 100644 pr/509/docs/noq_proto/struct.ProtectedInitialHeader.html create mode 100644 pr/509/docs/noq_proto/struct.QlogConfig.html create mode 100644 pr/509/docs/noq_proto/struct.QlogFileFactory.html create mode 100644 pr/509/docs/noq_proto/struct.RandomConnectionIdGenerator.html create mode 100644 pr/509/docs/noq_proto/struct.RecvStream.html create mode 100644 pr/509/docs/noq_proto/struct.RetryError.html create mode 100644 pr/509/docs/noq_proto/struct.RttEstimator.html create mode 100644 pr/509/docs/noq_proto/struct.SendStream.html create mode 100644 pr/509/docs/noq_proto/struct.ServerConfig.html create mode 100644 pr/509/docs/noq_proto/struct.ShouldTransmit.html create mode 100644 pr/509/docs/noq_proto/struct.StdSystemTime.html create mode 100644 pr/509/docs/noq_proto/struct.StreamId.html create mode 100644 pr/509/docs/noq_proto/struct.StreamInfo.html create mode 100644 pr/509/docs/noq_proto/struct.Streams.html create mode 100644 pr/509/docs/noq_proto/struct.TokenMemoryCache.html create mode 100644 pr/509/docs/noq_proto/struct.TokenReuseError.html create mode 100644 pr/509/docs/noq_proto/struct.Transmit.html create mode 100644 pr/509/docs/noq_proto/struct.TransportConfig.html create mode 100644 pr/509/docs/noq_proto/struct.TransportError.html create mode 100644 pr/509/docs/noq_proto/struct.TransportErrorCode.html create mode 100644 pr/509/docs/noq_proto/struct.UdpStats.html create mode 100644 pr/509/docs/noq_proto/struct.ValidationTokenConfig.html create mode 100644 pr/509/docs/noq_proto/struct.VarInt.html create mode 100644 pr/509/docs/noq_proto/struct.VarIntBoundsExceeded.html create mode 100644 pr/509/docs/noq_proto/struct.Written.html create mode 100644 pr/509/docs/noq_proto/token/struct.NoneTokenLog.html create mode 100644 pr/509/docs/noq_proto/token/struct.NoneTokenStore.html create mode 100644 pr/509/docs/noq_proto/token/struct.TokenReuseError.html create mode 100644 pr/509/docs/noq_proto/token/trait.TokenLog.html create mode 100644 pr/509/docs/noq_proto/token/trait.TokenStore.html create mode 100644 pr/509/docs/noq_proto/token_memory_cache/struct.TokenMemoryCache.html create mode 100644 pr/509/docs/noq_proto/trait.ConnectionIdGenerator.html create mode 100644 pr/509/docs/noq_proto/trait.ConnectionIdParser.html create mode 100644 pr/509/docs/noq_proto/trait.NetworkChangeHint.html create mode 100644 pr/509/docs/noq_proto/trait.QlogFactory.html create mode 100644 pr/509/docs/noq_proto/trait.TimeSource.html create mode 100644 pr/509/docs/noq_proto/trait.TokenLog.html create mode 100644 pr/509/docs/noq_proto/trait.TokenStore.html create mode 100644 pr/509/docs/noq_proto/transport_error/struct.Code.html create mode 100644 pr/509/docs/noq_proto/transport_error/struct.Error.html create mode 100644 pr/509/docs/noq_proto/transport_parameters/enum.Error.html create mode 100644 pr/509/docs/noq_proto/transport_parameters/index.html create mode 100644 pr/509/docs/noq_proto/transport_parameters/sidebar-items.js create mode 100644 pr/509/docs/noq_proto/transport_parameters/struct.TransportParameters.html create mode 100644 pr/509/docs/noq_proto/varint/struct.VarInt.html create mode 100644 pr/509/docs/noq_proto/varint/struct.VarIntBoundsExceeded.html create mode 100644 pr/509/docs/noq_udp/all.html create mode 100644 pr/509/docs/noq_udp/constant.BATCH_SIZE.html create mode 100644 pr/509/docs/noq_udp/enum.EcnCodepoint.html create mode 100644 pr/509/docs/noq_udp/imp/struct.UdpSocketState.html create mode 100644 pr/509/docs/noq_udp/index.html create mode 100644 pr/509/docs/noq_udp/sidebar-items.js create mode 100644 pr/509/docs/noq_udp/struct.RecvMeta.html create mode 100644 pr/509/docs/noq_udp/struct.Transmit.html create mode 100644 pr/509/docs/noq_udp/struct.UdpSockRef.html create mode 100644 pr/509/docs/noq_udp/struct.UdpSocketState.html create mode 100644 pr/509/docs/perf/all.html create mode 100644 pr/509/docs/perf/client/fn.run.html create mode 100644 pr/509/docs/perf/client/index.html create mode 100644 pr/509/docs/perf/client/sidebar-items.js create mode 100644 pr/509/docs/perf/client/struct.Opt.html create mode 100644 pr/509/docs/perf/enum.CongestionAlgorithm.html create mode 100644 pr/509/docs/perf/fn.parse_byte_size.html create mode 100644 pr/509/docs/perf/index.html create mode 100644 pr/509/docs/perf/noprotection/index.html create mode 100644 pr/509/docs/perf/noprotection/sidebar-items.js create mode 100644 pr/509/docs/perf/noprotection/struct.NoProtectionClientConfig.html create mode 100644 pr/509/docs/perf/noprotection/struct.NoProtectionServerConfig.html create mode 100644 pr/509/docs/perf/server/fn.run.html create mode 100644 pr/509/docs/perf/server/index.html create mode 100644 pr/509/docs/perf/server/sidebar-items.js create mode 100644 pr/509/docs/perf/server/struct.Opt.html create mode 100644 pr/509/docs/perf/sidebar-items.js create mode 100644 pr/509/docs/perf/static.PERF_CIPHER_SUITES.html create mode 100644 pr/509/docs/perf/stats/index.html create mode 100644 pr/509/docs/perf/stats/sidebar-items.js create mode 100644 pr/509/docs/perf/stats/struct.OpenStreamStats.html create mode 100644 pr/509/docs/perf/stats/struct.Stats.html create mode 100644 pr/509/docs/perf/stats/struct.StreamStats.html create mode 100644 pr/509/docs/perf/struct.CommonOpt.html create mode 100644 pr/509/docs/search.index/021c1e79bdd9.js create mode 100644 pr/509/docs/search.index/02804be949b0.js create mode 100644 pr/509/docs/search.index/0452ffab0bec.js create mode 100644 pr/509/docs/search.index/04e28bec524a.js create mode 100644 pr/509/docs/search.index/0504377ac740.js create mode 100644 pr/509/docs/search.index/062704961ddf.js create mode 100644 pr/509/docs/search.index/066d6a9a769d.js create mode 100644 pr/509/docs/search.index/06c602595384.js create mode 100644 pr/509/docs/search.index/06cb80e90e12.js create mode 100644 pr/509/docs/search.index/0808e3817873.js create mode 100644 pr/509/docs/search.index/0920e65a8fea.js create mode 100644 pr/509/docs/search.index/094d93788e8b.js create mode 100644 pr/509/docs/search.index/0b1f340b7ad1.js create mode 100644 pr/509/docs/search.index/0b471b6e9f68.js create mode 100644 pr/509/docs/search.index/0b9e3bf94d8e.js create mode 100644 pr/509/docs/search.index/0ba637addba8.js create mode 100644 pr/509/docs/search.index/0bb8ecd0000d.js create mode 100644 pr/509/docs/search.index/0d3c240b6746.js create mode 100644 pr/509/docs/search.index/0d40bbfaf512.js create mode 100644 pr/509/docs/search.index/0e7685c20a2a.js create mode 100644 pr/509/docs/search.index/0ee8581ceb4b.js create mode 100644 pr/509/docs/search.index/0f8ffae0cedd.js create mode 100644 pr/509/docs/search.index/0fc7936ce68d.js create mode 100644 pr/509/docs/search.index/10020ef0a247.js create mode 100644 pr/509/docs/search.index/10071b17a468.js create mode 100644 pr/509/docs/search.index/1062f3057d85.js create mode 100644 pr/509/docs/search.index/10722fb15319.js create mode 100644 pr/509/docs/search.index/116b929a1e0d.js create mode 100644 pr/509/docs/search.index/11770572d7de.js create mode 100644 pr/509/docs/search.index/11d9d1c24f11.js create mode 100644 pr/509/docs/search.index/12e2aa203dff.js create mode 100644 pr/509/docs/search.index/136ec6b58ef2.js create mode 100644 pr/509/docs/search.index/13bc5713853d.js create mode 100644 pr/509/docs/search.index/14600ff5b785.js create mode 100644 pr/509/docs/search.index/1471da9f23be.js create mode 100644 pr/509/docs/search.index/1473e4141b70.js create mode 100644 pr/509/docs/search.index/14e2eb248d40.js create mode 100644 pr/509/docs/search.index/14e8961c1d4b.js create mode 100644 pr/509/docs/search.index/14f8d441cdf1.js create mode 100644 pr/509/docs/search.index/15aa8d41a495.js create mode 100644 pr/509/docs/search.index/15d86d685190.js create mode 100644 pr/509/docs/search.index/166712222acb.js create mode 100644 pr/509/docs/search.index/16760629a188.js create mode 100644 pr/509/docs/search.index/16ac7dbe5c38.js create mode 100644 pr/509/docs/search.index/174deafe44ae.js create mode 100644 pr/509/docs/search.index/17adc63e271c.js create mode 100644 pr/509/docs/search.index/1940d69372f3.js create mode 100644 pr/509/docs/search.index/19935bf303f2.js create mode 100644 pr/509/docs/search.index/1b0ddf9ba1d4.js create mode 100644 pr/509/docs/search.index/1b58e8099167.js create mode 100644 pr/509/docs/search.index/1b8da10f40ca.js create mode 100644 pr/509/docs/search.index/1bce199876db.js create mode 100644 pr/509/docs/search.index/1c551ca2e064.js create mode 100644 pr/509/docs/search.index/1cc87b30a9de.js create mode 100644 pr/509/docs/search.index/1fad72632495.js create mode 100644 pr/509/docs/search.index/1fb07216cf6d.js create mode 100644 pr/509/docs/search.index/2045e5eacc73.js create mode 100644 pr/509/docs/search.index/2096ecc2779b.js create mode 100644 pr/509/docs/search.index/210dbcbbd7f7.js create mode 100644 pr/509/docs/search.index/218355186cfc.js create mode 100644 pr/509/docs/search.index/2209e95f2e0d.js create mode 100644 pr/509/docs/search.index/221f869c6ba4.js create mode 100644 pr/509/docs/search.index/22f4aa7b7735.js create mode 100644 pr/509/docs/search.index/23f856af9139.js create mode 100644 pr/509/docs/search.index/2415f9b5fd49.js create mode 100644 pr/509/docs/search.index/24b191264c4c.js create mode 100644 pr/509/docs/search.index/250587bf9bab.js create mode 100644 pr/509/docs/search.index/253763ab1d88.js create mode 100644 pr/509/docs/search.index/267754446ed8.js create mode 100644 pr/509/docs/search.index/26bbc9b847a5.js create mode 100644 pr/509/docs/search.index/27c9a562bd0e.js create mode 100644 pr/509/docs/search.index/28367a06fb2b.js create mode 100644 pr/509/docs/search.index/2929fb67221c.js create mode 100644 pr/509/docs/search.index/296f1368d1fa.js create mode 100644 pr/509/docs/search.index/2a75dabf6bdd.js create mode 100644 pr/509/docs/search.index/2b1d461fecf9.js create mode 100644 pr/509/docs/search.index/2b391fb231c1.js create mode 100644 pr/509/docs/search.index/2b4f77bea34a.js create mode 100644 pr/509/docs/search.index/2c5dcb185a0c.js create mode 100644 pr/509/docs/search.index/2d5000411b5a.js create mode 100644 pr/509/docs/search.index/2e4ab4883d2f.js create mode 100644 pr/509/docs/search.index/2ef7b6b51b41.js create mode 100644 pr/509/docs/search.index/30aa3843eff5.js create mode 100644 pr/509/docs/search.index/30e186f1b9e2.js create mode 100644 pr/509/docs/search.index/315e1edbed3e.js create mode 100644 pr/509/docs/search.index/323e46e00169.js create mode 100644 pr/509/docs/search.index/3289e9ef46be.js create mode 100644 pr/509/docs/search.index/337c243b1b41.js create mode 100644 pr/509/docs/search.index/34116ea5fc40.js create mode 100644 pr/509/docs/search.index/34d132c9cb45.js create mode 100644 pr/509/docs/search.index/352e0d990534.js create mode 100644 pr/509/docs/search.index/358ecde5a5a1.js create mode 100644 pr/509/docs/search.index/36786b3fe579.js create mode 100644 pr/509/docs/search.index/3884b95c3dd3.js create mode 100644 pr/509/docs/search.index/3889638da158.js create mode 100644 pr/509/docs/search.index/38b550452ed6.js create mode 100644 pr/509/docs/search.index/38d210a6c887.js create mode 100644 pr/509/docs/search.index/394aea3c11a3.js create mode 100644 pr/509/docs/search.index/3965b74da6b0.js create mode 100644 pr/509/docs/search.index/39dc67d07722.js create mode 100644 pr/509/docs/search.index/3a250acbb1fb.js create mode 100644 pr/509/docs/search.index/3b41ad6096cb.js create mode 100644 pr/509/docs/search.index/3b6343e13631.js create mode 100644 pr/509/docs/search.index/3bc9fac5887b.js create mode 100644 pr/509/docs/search.index/3c467f5cbf13.js create mode 100644 pr/509/docs/search.index/3c6fb78251b2.js create mode 100644 pr/509/docs/search.index/3e97732e8a32.js create mode 100644 pr/509/docs/search.index/3ee5422cd9bc.js create mode 100644 pr/509/docs/search.index/3eea839e1662.js create mode 100644 pr/509/docs/search.index/3fe3c85d7012.js create mode 100644 pr/509/docs/search.index/40de1d99c33a.js create mode 100644 pr/509/docs/search.index/4137d3501001.js create mode 100644 pr/509/docs/search.index/41a15bcea3f9.js create mode 100644 pr/509/docs/search.index/41dc73cd19e1.js create mode 100644 pr/509/docs/search.index/4215b1682697.js create mode 100644 pr/509/docs/search.index/424d5a7a4fa1.js create mode 100644 pr/509/docs/search.index/4279670fcfbe.js create mode 100644 pr/509/docs/search.index/427bea082f92.js create mode 100644 pr/509/docs/search.index/43d54762ebae.js create mode 100644 pr/509/docs/search.index/43d7eeba4357.js create mode 100644 pr/509/docs/search.index/4417fc0d17a5.js create mode 100644 pr/509/docs/search.index/444aae938267.js create mode 100644 pr/509/docs/search.index/44cfcd43b029.js create mode 100644 pr/509/docs/search.index/45261c45eedf.js create mode 100644 pr/509/docs/search.index/4656a4fb98bd.js create mode 100644 pr/509/docs/search.index/46b2a1fc2fe3.js create mode 100644 pr/509/docs/search.index/471028459586.js create mode 100644 pr/509/docs/search.index/4763fd511429.js create mode 100644 pr/509/docs/search.index/477aba3c874c.js create mode 100644 pr/509/docs/search.index/47876021cef2.js create mode 100644 pr/509/docs/search.index/48234c118f23.js create mode 100644 pr/509/docs/search.index/48ea2bc1c340.js create mode 100644 pr/509/docs/search.index/49462535504e.js create mode 100644 pr/509/docs/search.index/498baefc1eaa.js create mode 100644 pr/509/docs/search.index/4a2f3dcae3f4.js create mode 100644 pr/509/docs/search.index/4a39f4f617e6.js create mode 100644 pr/509/docs/search.index/4a715899379d.js create mode 100644 pr/509/docs/search.index/4b1b519a3f3d.js create mode 100644 pr/509/docs/search.index/4b1ce8d67124.js create mode 100644 pr/509/docs/search.index/4bc8bd135a43.js create mode 100644 pr/509/docs/search.index/4c7fab31325f.js create mode 100644 pr/509/docs/search.index/4c970f423583.js create mode 100644 pr/509/docs/search.index/4dcbd5c1822e.js create mode 100644 pr/509/docs/search.index/4dd7e14fd0c3.js create mode 100644 pr/509/docs/search.index/4e1e65dd0971.js create mode 100644 pr/509/docs/search.index/4e81395114a0.js create mode 100644 pr/509/docs/search.index/4f1c4cfa01fd.js create mode 100644 pr/509/docs/search.index/4f1dbded47de.js create mode 100644 pr/509/docs/search.index/4f6d4bec726c.js create mode 100644 pr/509/docs/search.index/501107c77886.js create mode 100644 pr/509/docs/search.index/5017d5163d40.js create mode 100644 pr/509/docs/search.index/5041ebb4629e.js create mode 100644 pr/509/docs/search.index/50ff48e6df46.js create mode 100644 pr/509/docs/search.index/51394b8419e6.js create mode 100644 pr/509/docs/search.index/5188e972bd4c.js create mode 100644 pr/509/docs/search.index/51a24df5c4b4.js create mode 100644 pr/509/docs/search.index/5206354980a8.js create mode 100644 pr/509/docs/search.index/5240bbf87b4a.js create mode 100644 pr/509/docs/search.index/5244d6639a96.js create mode 100644 pr/509/docs/search.index/52c94dd0f0aa.js create mode 100644 pr/509/docs/search.index/52d9d2ca2fb3.js create mode 100644 pr/509/docs/search.index/52e3970e8139.js create mode 100644 pr/509/docs/search.index/52f9dda67630.js create mode 100644 pr/509/docs/search.index/550e643162da.js create mode 100644 pr/509/docs/search.index/564d23e1fbb1.js create mode 100644 pr/509/docs/search.index/566273acdbeb.js create mode 100644 pr/509/docs/search.index/56ea93cff34a.js create mode 100644 pr/509/docs/search.index/57e0023cd6b3.js create mode 100644 pr/509/docs/search.index/57f16691bc95.js create mode 100644 pr/509/docs/search.index/59cb3d108e66.js create mode 100644 pr/509/docs/search.index/5a87fbcb1ab9.js create mode 100644 pr/509/docs/search.index/5aa9de2b1ec7.js create mode 100644 pr/509/docs/search.index/5b9b294539ab.js create mode 100644 pr/509/docs/search.index/5c1fe6b3e4a8.js create mode 100644 pr/509/docs/search.index/5efb7b1e3360.js create mode 100644 pr/509/docs/search.index/5f36959d2de3.js create mode 100644 pr/509/docs/search.index/611ff1ea6784.js create mode 100644 pr/509/docs/search.index/61b4c5b2a89e.js create mode 100644 pr/509/docs/search.index/61fe3550b1f8.js create mode 100644 pr/509/docs/search.index/6386aba6a284.js create mode 100644 pr/509/docs/search.index/6410b272eb51.js create mode 100644 pr/509/docs/search.index/64b4fe03993a.js create mode 100644 pr/509/docs/search.index/64d9a6218304.js create mode 100644 pr/509/docs/search.index/665ebf7362b0.js create mode 100644 pr/509/docs/search.index/669a36c47371.js create mode 100644 pr/509/docs/search.index/674f02e77287.js create mode 100644 pr/509/docs/search.index/689d2c6c80c6.js create mode 100644 pr/509/docs/search.index/68b6cd216296.js create mode 100644 pr/509/docs/search.index/69496b573a06.js create mode 100644 pr/509/docs/search.index/69993716acd0.js create mode 100644 pr/509/docs/search.index/6b029a433070.js create mode 100644 pr/509/docs/search.index/6d657b4263d8.js create mode 100644 pr/509/docs/search.index/6fe836e11237.js create mode 100644 pr/509/docs/search.index/711a3a237476.js create mode 100644 pr/509/docs/search.index/714a27f59b30.js create mode 100644 pr/509/docs/search.index/7250c12d601c.js create mode 100644 pr/509/docs/search.index/72d51fa47912.js create mode 100644 pr/509/docs/search.index/74a202ff2fce.js create mode 100644 pr/509/docs/search.index/74ebe484d8fb.js create mode 100644 pr/509/docs/search.index/74fff8bb6461.js create mode 100644 pr/509/docs/search.index/7511c2f31675.js create mode 100644 pr/509/docs/search.index/7545996f0dd7.js create mode 100644 pr/509/docs/search.index/758f270c34be.js create mode 100644 pr/509/docs/search.index/7643889a294b.js create mode 100644 pr/509/docs/search.index/768d33a8e276.js create mode 100644 pr/509/docs/search.index/768fd5752adb.js create mode 100644 pr/509/docs/search.index/77136bfd9216.js create mode 100644 pr/509/docs/search.index/77277d28d69a.js create mode 100644 pr/509/docs/search.index/779f19a4182a.js create mode 100644 pr/509/docs/search.index/791ca440f256.js create mode 100644 pr/509/docs/search.index/7a10444b994a.js create mode 100644 pr/509/docs/search.index/7abae646c037.js create mode 100644 pr/509/docs/search.index/7c04cb118ed6.js create mode 100644 pr/509/docs/search.index/7cb7d80cd09e.js create mode 100644 pr/509/docs/search.index/7cb8f8f69140.js create mode 100644 pr/509/docs/search.index/7da1c8fa437d.js create mode 100644 pr/509/docs/search.index/7df5bd0efad4.js create mode 100644 pr/509/docs/search.index/7f5125453ec9.js create mode 100644 pr/509/docs/search.index/7fb39206b4b2.js create mode 100644 pr/509/docs/search.index/7fc9cfd68b15.js create mode 100644 pr/509/docs/search.index/7fe8c77cc844.js create mode 100644 pr/509/docs/search.index/alias/a762150c532c.js create mode 100644 pr/509/docs/search.index/crateNames/2450c9cb4357.js create mode 100644 pr/509/docs/search.index/desc/0f6717b5c42f.js create mode 100644 pr/509/docs/search.index/entry/3e7a9e6dac3d.js create mode 100644 pr/509/docs/search.index/function/c234980f56b0.js create mode 100644 pr/509/docs/search.index/generic_inverted_index/b518a2effe00.js create mode 100644 pr/509/docs/search.index/name/07d6c6249cec.js create mode 100644 pr/509/docs/search.index/normalizedName/dd897f13bf77.js create mode 100644 pr/509/docs/search.index/path/772450b83c16.js create mode 100644 pr/509/docs/search.index/root.js create mode 100644 pr/509/docs/search.index/type/f7cf3408e9aa.js create mode 100644 pr/509/docs/set_up_connection/all.html create mode 100644 pr/509/docs/set_up_connection/constant.CLIENT_ADDR.html create mode 100644 pr/509/docs/set_up_connection/constant.LOCALHOST_V4.html create mode 100644 pr/509/docs/set_up_connection/constant.SERVER_ADDR.html create mode 100644 pr/509/docs/set_up_connection/constant.SERVER_NAME.html create mode 100644 pr/509/docs/set_up_connection/fn.client.html create mode 100644 pr/509/docs/set_up_connection/fn.main.html create mode 100644 pr/509/docs/set_up_connection/fn.server.html create mode 100644 pr/509/docs/set_up_connection/index.html create mode 100644 pr/509/docs/set_up_connection/sidebar-items.js create mode 100644 pr/509/docs/settings.html create mode 100644 pr/509/docs/src-files.js create mode 100644 pr/509/docs/src/bench/lib.rs.html create mode 100644 pr/509/docs/src/bench/stats.rs.html create mode 100644 pr/509/docs/src/bulk/bulk.rs.html create mode 100644 pr/509/docs/src/certificate/certificate.rs.html create mode 100644 pr/509/docs/src/data_transfer/data-transfer.rs.html create mode 100644 pr/509/docs/src/noq/connection.rs.html create mode 100644 pr/509/docs/src/noq/endpoint.rs.html create mode 100644 pr/509/docs/src/noq/incoming.rs.html create mode 100644 pr/509/docs/src/noq/lib.rs.html create mode 100644 pr/509/docs/src/noq/mutex.rs.html create mode 100644 pr/509/docs/src/noq/path.rs.html create mode 100644 pr/509/docs/src/noq/recv_stream.rs.html create mode 100644 pr/509/docs/src/noq/runtime/mod.rs.html create mode 100644 pr/509/docs/src/noq/runtime/smol.rs.html create mode 100644 pr/509/docs/src/noq/runtime/tokio.rs.html create mode 100644 pr/509/docs/src/noq/send_stream.rs.html create mode 100644 pr/509/docs/src/noq/work_limiter.rs.html create mode 100644 pr/509/docs/src/noq_perf/perf.rs.html create mode 100644 pr/509/docs/src/noq_proto/address_discovery.rs.html create mode 100644 pr/509/docs/src/noq_proto/bloom_token_log.rs.html create mode 100644 pr/509/docs/src/noq_proto/cid_generator.rs.html create mode 100644 pr/509/docs/src/noq_proto/cid_queue.rs.html create mode 100644 pr/509/docs/src/noq_proto/coding.rs.html create mode 100644 pr/509/docs/src/noq_proto/config/mod.rs.html create mode 100644 pr/509/docs/src/noq_proto/config/qlog.rs.html create mode 100644 pr/509/docs/src/noq_proto/config/transport.rs.html create mode 100644 pr/509/docs/src/noq_proto/congestion.rs.html create mode 100644 pr/509/docs/src/noq_proto/congestion/bbr/bw_estimation.rs.html create mode 100644 pr/509/docs/src/noq_proto/congestion/bbr/min_max.rs.html create mode 100644 pr/509/docs/src/noq_proto/congestion/bbr/mod.rs.html create mode 100644 pr/509/docs/src/noq_proto/congestion/cubic.rs.html create mode 100644 pr/509/docs/src/noq_proto/congestion/new_reno.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/ack_frequency.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/assembler.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/cid_state.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/datagrams.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/mod.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/mtud.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/pacing.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/packet_builder.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/packet_crypto.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/paths.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/qlog.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/send_buffer.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/spaces.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/state.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/stats.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/streams/mod.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/streams/recv.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/streams/send.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/streams/state.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/timer.rs.html create mode 100644 pr/509/docs/src/noq_proto/connection/transmit_buf.rs.html create mode 100644 pr/509/docs/src/noq_proto/constant_time.rs.html create mode 100644 pr/509/docs/src/noq_proto/crypto.rs.html create mode 100644 pr/509/docs/src/noq_proto/crypto/ring_like.rs.html create mode 100644 pr/509/docs/src/noq_proto/crypto/rustls.rs.html create mode 100644 pr/509/docs/src/noq_proto/endpoint.rs.html create mode 100644 pr/509/docs/src/noq_proto/frame.rs.html create mode 100644 pr/509/docs/src/noq_proto/lib.rs.html create mode 100644 pr/509/docs/src/noq_proto/n0_nat_traversal.rs.html create mode 100644 pr/509/docs/src/noq_proto/packet.rs.html create mode 100644 pr/509/docs/src/noq_proto/range_set/array_range_set.rs.html create mode 100644 pr/509/docs/src/noq_proto/range_set/mod.rs.html create mode 100644 pr/509/docs/src/noq_proto/shared.rs.html create mode 100644 pr/509/docs/src/noq_proto/token.rs.html create mode 100644 pr/509/docs/src/noq_proto/token_memory_cache.rs.html create mode 100644 pr/509/docs/src/noq_proto/transport_error.rs.html create mode 100644 pr/509/docs/src/noq_proto/transport_parameters.rs.html create mode 100644 pr/509/docs/src/noq_proto/varint.rs.html create mode 100644 pr/509/docs/src/noq_udp/cmsg/mod.rs.html create mode 100644 pr/509/docs/src/noq_udp/cmsg/unix.rs.html create mode 100644 pr/509/docs/src/noq_udp/lib.rs.html create mode 100644 pr/509/docs/src/noq_udp/unix.rs.html create mode 100644 pr/509/docs/src/perf/client.rs.html create mode 100644 pr/509/docs/src/perf/lib.rs.html create mode 100644 pr/509/docs/src/perf/noprotection.rs.html create mode 100644 pr/509/docs/src/perf/server.rs.html create mode 100644 pr/509/docs/src/perf/stats.rs.html create mode 100644 pr/509/docs/src/set_up_connection/set-up-connection.rs.html create mode 100644 pr/509/docs/static.files/COPYRIGHT-7fb11f4e.txt create mode 100644 pr/509/docs/static.files/FiraMono-Medium-86f75c8c.woff2 create mode 100644 pr/509/docs/static.files/FiraMono-Regular-87c26294.woff2 create mode 100644 pr/509/docs/static.files/FiraSans-Italic-81dc35de.woff2 create mode 100644 pr/509/docs/static.files/FiraSans-LICENSE-05ab6dbd.txt create mode 100644 pr/509/docs/static.files/FiraSans-Medium-e1aa3f0a.woff2 create mode 100644 pr/509/docs/static.files/FiraSans-MediumItalic-ccf7e434.woff2 create mode 100644 pr/509/docs/static.files/FiraSans-Regular-0fe48ade.woff2 create mode 100644 pr/509/docs/static.files/LICENSE-APACHE-a60eea81.txt create mode 100644 pr/509/docs/static.files/LICENSE-MIT-23f18e03.txt create mode 100644 pr/509/docs/static.files/NanumBarunGothic-13b3dcba.ttf.woff2 create mode 100644 pr/509/docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt create mode 100644 pr/509/docs/static.files/SourceCodePro-It-fc8b9304.ttf.woff2 create mode 100644 pr/509/docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt create mode 100644 pr/509/docs/static.files/SourceCodePro-Regular-8badfe75.ttf.woff2 create mode 100644 pr/509/docs/static.files/SourceCodePro-Semibold-aa29a496.ttf.woff2 create mode 100644 pr/509/docs/static.files/SourceSerif4-Bold-6d4fd4c0.ttf.woff2 create mode 100644 pr/509/docs/static.files/SourceSerif4-It-ca3b17ed.ttf.woff2 create mode 100644 pr/509/docs/static.files/SourceSerif4-LICENSE-a2cfd9d5.md create mode 100644 pr/509/docs/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 create mode 100644 pr/509/docs/static.files/SourceSerif4-Semibold-457a13ac.ttf.woff2 create mode 100644 pr/509/docs/static.files/favicon-044be391.svg create mode 100644 pr/509/docs/static.files/favicon-32x32-eab170b8.png create mode 100644 pr/509/docs/static.files/main-ce535bd0.js create mode 100644 pr/509/docs/static.files/normalize-9960930a.css create mode 100644 pr/509/docs/static.files/noscript-263c88ec.css create mode 100644 pr/509/docs/static.files/rust-logo-9a9549ea.svg create mode 100644 pr/509/docs/static.files/rustdoc-ca0dd0c4.css create mode 100644 pr/509/docs/static.files/scrape-examples-2bbcccac.js create mode 100644 pr/509/docs/static.files/search-8d3311b9.js create mode 100644 pr/509/docs/static.files/settings-c38705f0.js create mode 100644 pr/509/docs/static.files/src-script-813739b1.js create mode 100644 pr/509/docs/static.files/storage-e2aeef58.js create mode 100644 pr/509/docs/static.files/stringdex-828709d0.js create mode 100644 pr/509/docs/trait.impl/arbitrary/trait.Arbitrary.js create mode 100644 pr/509/docs/trait.impl/clap_builder/derive/trait.Args.js create mode 100644 pr/509/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js create mode 100644 pr/509/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js create mode 100644 pr/509/docs/trait.impl/clap_builder/derive/trait.Parser.js create mode 100644 pr/509/docs/trait.impl/clap_builder/derive/trait.Subcommand.js create mode 100644 pr/509/docs/trait.impl/clap_builder/derive/trait.ValueEnum.js create mode 100644 pr/509/docs/trait.impl/core/clone/trait.Clone.js create mode 100644 pr/509/docs/trait.impl/core/cmp/trait.Eq.js create mode 100644 pr/509/docs/trait.impl/core/cmp/trait.Ord.js create mode 100644 pr/509/docs/trait.impl/core/cmp/trait.PartialEq.js create mode 100644 pr/509/docs/trait.impl/core/cmp/trait.PartialOrd.js create mode 100644 pr/509/docs/trait.impl/core/convert/trait.From.js create mode 100644 pr/509/docs/trait.impl/core/convert/trait.TryFrom.js create mode 100644 pr/509/docs/trait.impl/core/default/trait.Default.js create mode 100644 pr/509/docs/trait.impl/core/error/trait.Error.js create mode 100644 pr/509/docs/trait.impl/core/fmt/trait.Debug.js create mode 100644 pr/509/docs/trait.impl/core/fmt/trait.Display.js create mode 100644 pr/509/docs/trait.impl/core/future/future/trait.Future.js create mode 100644 pr/509/docs/trait.impl/core/future/into_future/trait.IntoFuture.js create mode 100644 pr/509/docs/trait.impl/core/hash/trait.Hash.js create mode 100644 pr/509/docs/trait.impl/core/iter/traits/iterator/trait.Iterator.js create mode 100644 pr/509/docs/trait.impl/core/marker/trait.Copy.js create mode 100644 pr/509/docs/trait.impl/core/marker/trait.Freeze.js create mode 100644 pr/509/docs/trait.impl/core/marker/trait.Send.js create mode 100644 pr/509/docs/trait.impl/core/marker/trait.StructuralPartialEq.js create mode 100644 pr/509/docs/trait.impl/core/marker/trait.Sync.js create mode 100644 pr/509/docs/trait.impl/core/marker/trait.Unpin.js create mode 100644 pr/509/docs/trait.impl/core/marker/trait.UnsafeUnpin.js create mode 100644 pr/509/docs/trait.impl/core/ops/bit/trait.Not.js create mode 100644 pr/509/docs/trait.impl/core/ops/deref/trait.Deref.js create mode 100644 pr/509/docs/trait.impl/core/ops/deref/trait.DerefMut.js create mode 100644 pr/509/docs/trait.impl/core/ops/drop/trait.Drop.js create mode 100644 pr/509/docs/trait.impl/core/ops/index/trait.Index.js create mode 100644 pr/509/docs/trait.impl/core/ops/index/trait.IndexMut.js create mode 100644 pr/509/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js create mode 100644 pr/509/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js create mode 100644 pr/509/docs/trait.impl/core/str/traits/trait.FromStr.js create mode 100644 pr/509/docs/trait.impl/futures_core/stream/trait.Stream.js create mode 100644 pr/509/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js create mode 100644 pr/509/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js create mode 100644 pr/509/docs/trait.impl/identity_hash/trait.IdentityHashable.js create mode 100644 pr/509/docs/trait.impl/noq/runtime/trait.AsyncTimer.js create mode 100644 pr/509/docs/trait.impl/noq/runtime/trait.Runtime.js create mode 100644 pr/509/docs/trait.impl/noq_proto/cid_generator/trait.ConnectionIdGenerator.js create mode 100644 pr/509/docs/trait.impl/noq_proto/coding/trait.Decodable.js create mode 100644 pr/509/docs/trait.impl/noq_proto/coding/trait.Encodable.js create mode 100644 pr/509/docs/trait.impl/noq_proto/config/qlog/trait.QlogFactory.js create mode 100644 pr/509/docs/trait.impl/noq_proto/config/trait.TimeSource.js create mode 100644 pr/509/docs/trait.impl/noq_proto/congestion/trait.Controller.js create mode 100644 pr/509/docs/trait.impl/noq_proto/congestion/trait.ControllerFactory.js create mode 100644 pr/509/docs/trait.impl/noq_proto/crypto/trait.ClientConfig.js create mode 100644 pr/509/docs/trait.impl/noq_proto/crypto/trait.HeaderKey.js create mode 100644 pr/509/docs/trait.impl/noq_proto/crypto/trait.HmacKey.js create mode 100644 pr/509/docs/trait.impl/noq_proto/crypto/trait.PacketKey.js create mode 100644 pr/509/docs/trait.impl/noq_proto/crypto/trait.ServerConfig.js create mode 100644 pr/509/docs/trait.impl/noq_proto/crypto/trait.Session.js create mode 100644 pr/509/docs/trait.impl/noq_proto/packet/trait.ConnectionIdParser.js create mode 100644 pr/509/docs/trait.impl/noq_proto/token/trait.TokenLog.js create mode 100644 pr/509/docs/trait.impl/noq_proto/token/trait.TokenStore.js create mode 100644 pr/509/docs/trait.impl/rustls/verify/trait.ServerCertVerifier.js create mode 100644 pr/509/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js create mode 100644 pr/509/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js create mode 100644 pr/509/docs/type.impl/core/result/enum.Result.js diff --git a/pr/509/docs/.lock b/pr/509/docs/.lock new file mode 100644 index 000000000..e69de29bb diff --git a/pr/509/docs/bench/all.html b/pr/509/docs/bench/all.html new file mode 100644 index 000000000..717d4518a --- /dev/null +++ b/pr/509/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/509/docs/bench/enum.CipherSuite.html b/pr/509/docs/bench/enum.CipherSuite.html new file mode 100644 index 000000000..64f89d912 --- /dev/null +++ b/pr/509/docs/bench/enum.CipherSuite.html @@ -0,0 +1,35 @@ +CipherSuite in bench - Rust

CipherSuite

Enum CipherSuite 

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

Variants§

§

Aes128

§

Aes256

§

Chacha20

Implementations§

Source§

impl CipherSuite

Source

pub fn as_rustls(self) -> SupportedCipherSuite

Trait Implementations§

Source§

impl Clone for CipherSuite

Source§

fn clone(&self) -> CipherSuite

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for CipherSuite

Source§

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

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

impl FromStr for CipherSuite

Source§

type Err = Error

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

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

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

impl PartialEq for CipherSuite

Source§

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

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

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

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

impl Copy for CipherSuite

Source§

impl Eq for CipherSuite

Source§

impl StructuralPartialEq for CipherSuite

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

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

§

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

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

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

§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/bench/fn.configure_tracing_subscriber.html b/pr/509/docs/bench/fn.configure_tracing_subscriber.html new file mode 100644 index 000000000..b07f8c28a --- /dev/null +++ b/pr/509/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/509/docs/bench/fn.connect_client.html b/pr/509/docs/bench/fn.connect_client.html new file mode 100644 index 000000000..1f4edbfc9 --- /dev/null +++ b/pr/509/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/509/docs/bench/fn.drain_stream.html b/pr/509/docs/bench/fn.drain_stream.html new file mode 100644 index 000000000..1156135bd --- /dev/null +++ b/pr/509/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/509/docs/bench/fn.rt.html b/pr/509/docs/bench/fn.rt.html new file mode 100644 index 000000000..eb4265483 --- /dev/null +++ b/pr/509/docs/bench/fn.rt.html @@ -0,0 +1 @@ +rt in bench - Rust

rt

Function rt 

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

Crate bench

Crate bench 

Source

Modules§

stats

Structs§

Opt

Enums§

CipherSuite

Functions§

configure_tracing_subscriber
connect_client
Create a client endpoint and client connection
drain_stream
rt
send_data_on_stream
server_endpoint
Creates a server endpoint which runs on the given runtime
transport_config
\ No newline at end of file diff --git a/pr/509/docs/bench/sidebar-items.js b/pr/509/docs/bench/sidebar-items.js new file mode 100644 index 000000000..f56525d6b --- /dev/null +++ b/pr/509/docs/bench/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["CipherSuite"],"fn":["configure_tracing_subscriber","connect_client","drain_stream","rt","send_data_on_stream","server_endpoint","transport_config"],"mod":["stats"],"struct":["Opt"]}; \ No newline at end of file diff --git a/pr/509/docs/bench/stats/fn.throughput_bps.html b/pr/509/docs/bench/stats/fn.throughput_bps.html new file mode 100644 index 000000000..8f9cac152 --- /dev/null +++ b/pr/509/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/509/docs/bench/stats/index.html b/pr/509/docs/bench/stats/index.html new file mode 100644 index 000000000..61b71b975 --- /dev/null +++ b/pr/509/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/509/docs/bench/stats/sidebar-items.js b/pr/509/docs/bench/stats/sidebar-items.js new file mode 100644 index 000000000..6272a13ae --- /dev/null +++ b/pr/509/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/509/docs/bench/stats/struct.Stats.html b/pr/509/docs/bench/stats/struct.Stats.html new file mode 100644 index 000000000..6af36025e --- /dev/null +++ b/pr/509/docs/bench/stats/struct.Stats.html @@ -0,0 +1,29 @@ +Stats in bench::stats - Rust

Stats

Struct Stats 

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

Fields§

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

Implementations§

Source§

impl Stats

Source

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

Source

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

Trait Implementations§

Source§

impl Default for Stats

Source§

fn default() -> Stats

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

Auto Trait Implementations§

§

impl Freeze for Stats

§

impl RefUnwindSafe for Stats

§

impl Send for Stats

§

impl Sync for Stats

§

impl Unpin for Stats

§

impl UnwindSafe for Stats

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

StreamStats

Struct StreamStats 

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

Fields§

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

Trait Implementations§

Source§

impl Default for StreamStats

Source§

fn default() -> Self

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

TransferResult

Struct TransferResult 

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

Fields§

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

Implementations§

Source§

impl TransferResult

Source

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

Trait Implementations§

Source§

impl Debug for TransferResult

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

Opt

Struct Opt 

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

Fields§

§clients: usize

The total number of clients which should be created

+
§streams: usize

The total number of streams which should be created

+
§max_streams: usize

The amount of concurrent streams which should be used

+
§download_size: u64

Number of bytes to transmit from server to client

+

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

+
§upload_size: u64

Number of bytes to transmit from client to server

+

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

+
§stats: bool

Show connection stats the at the end of the benchmark

+
§read_unordered: bool

Whether to use the unordered read API

+
§cipher: CipherSuite

Allows to configure the desired cipher suite

+

Valid options are: aes128, aes256, chacha20

+
§initial_mtu: u16

Starting guess for maximum UDP payload size

+

Trait Implementations§

Source§

impl Args for Opt

Source§

fn group_id() -> Option<Id>

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

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

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

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

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

impl Clone for Opt

Source§

fn clone(&self) -> Opt

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

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

Performs copy-assignment from source. Read more
Source§

impl CommandFactory for Opt

Source§

fn command<'b>() -> Command

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

fn command_for_update<'b>() -> Command

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

impl Debug for Opt

Source§

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

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

impl FromArgMatches for Opt

Source§

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

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

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

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

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

Assign values from ArgMatches to self.
Source§

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

Assign values from ArgMatches to self.
Source§

impl Parser for Opt

§

fn parse() -> Self

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

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

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

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

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

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

Parse from iterator, return Err on error.
§

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

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

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

Update from iterator, return Err on error.
Source§

impl Copy for Opt

Auto Trait Implementations§

§

impl Freeze for Opt

§

impl RefUnwindSafe for Opt

§

impl Send for Opt

§

impl Sync for Opt

§

impl Unpin for Opt

§

impl UnwindSafe for Opt

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/bulk/all.html b/pr/509/docs/bulk/all.html new file mode 100644 index 000000000..ec127a04e --- /dev/null +++ b/pr/509/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/509/docs/bulk/fn.client.html b/pr/509/docs/bulk/fn.client.html new file mode 100644 index 000000000..11e16fbf0 --- /dev/null +++ b/pr/509/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/509/docs/bulk/fn.handle_client_stream.html b/pr/509/docs/bulk/fn.handle_client_stream.html new file mode 100644 index 000000000..057b0d3ce --- /dev/null +++ b/pr/509/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/509/docs/bulk/fn.main.html b/pr/509/docs/bulk/fn.main.html new file mode 100644 index 000000000..92a2fc225 --- /dev/null +++ b/pr/509/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/509/docs/bulk/fn.server.html b/pr/509/docs/bulk/fn.server.html new file mode 100644 index 000000000..c1722630f --- /dev/null +++ b/pr/509/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/509/docs/bulk/index.html b/pr/509/docs/bulk/index.html new file mode 100644 index 000000000..717e893f9 --- /dev/null +++ b/pr/509/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/509/docs/bulk/sidebar-items.js b/pr/509/docs/bulk/sidebar-items.js new file mode 100644 index 000000000..4b4dceb17 --- /dev/null +++ b/pr/509/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/509/docs/bulk/struct.ClientStats.html b/pr/509/docs/bulk/struct.ClientStats.html new file mode 100644 index 000000000..8aa06c196 --- /dev/null +++ b/pr/509/docs/bulk/struct.ClientStats.html @@ -0,0 +1,27 @@ +ClientStats in bulk - Rust

ClientStats

Struct ClientStats 

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

Fields§

§upload_stats: Stats§download_stats: Stats

Implementations§

Source§

impl ClientStats

Source

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

Trait Implementations§

Source§

impl Default for ClientStats

Source§

fn default() -> ClientStats

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/certificate/all.html b/pr/509/docs/certificate/all.html new file mode 100644 index 000000000..d6e1b79d5 --- /dev/null +++ b/pr/509/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/509/docs/certificate/fn.configure_client.html b/pr/509/docs/certificate/fn.configure_client.html new file mode 100644 index 000000000..e0c8b6fdb --- /dev/null +++ b/pr/509/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/509/docs/certificate/fn.generate_self_signed_cert.html b/pr/509/docs/certificate/fn.generate_self_signed_cert.html new file mode 100644 index 000000000..e91269156 --- /dev/null +++ b/pr/509/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/509/docs/certificate/fn.main.html b/pr/509/docs/certificate/fn.main.html new file mode 100644 index 000000000..461b95b57 --- /dev/null +++ b/pr/509/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/509/docs/certificate/fn.read_certs_from_file.html b/pr/509/docs/certificate/fn.read_certs_from_file.html new file mode 100644 index 000000000..bd63cf355 --- /dev/null +++ b/pr/509/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/509/docs/certificate/index.html b/pr/509/docs/certificate/index.html new file mode 100644 index 000000000..eb429342b --- /dev/null +++ b/pr/509/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/509/docs/certificate/sidebar-items.js b/pr/509/docs/certificate/sidebar-items.js new file mode 100644 index 000000000..0f8f69b2b --- /dev/null +++ b/pr/509/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/509/docs/certificate/struct.SkipServerVerification.html b/pr/509/docs/certificate/struct.SkipServerVerification.html new file mode 100644 index 000000000..d22aeda68 --- /dev/null +++ b/pr/509/docs/certificate/struct.SkipServerVerification.html @@ -0,0 +1,44 @@ +SkipServerVerification in certificate - Rust

SkipServerVerification

Struct SkipServerVerification 

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

Tuple Fields§

§0: Arc<CryptoProvider>

Implementations§

Source§

impl SkipServerVerification

Source

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

Trait Implementations§

Source§

impl Debug for SkipServerVerification

Source§

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

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

impl ServerCertVerifier for SkipServerVerification

Source§

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

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

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

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

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

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

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

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

fn requires_raw_public_keys(&self) -> bool

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

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

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/crates.js b/pr/509/docs/crates.js new file mode 100644 index 000000000..5bec0807a --- /dev/null +++ b/pr/509/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/509/docs/data_transfer/all.html b/pr/509/docs/data_transfer/all.html new file mode 100644 index 000000000..fd64bbb8f --- /dev/null +++ b/pr/509/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/509/docs/data_transfer/fn.main.html b/pr/509/docs/data_transfer/fn.main.html new file mode 100644 index 000000000..8bcfacdf5 --- /dev/null +++ b/pr/509/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/509/docs/data_transfer/fn.open_bidirectional_stream.html b/pr/509/docs/data_transfer/fn.open_bidirectional_stream.html new file mode 100644 index 000000000..2655892df --- /dev/null +++ b/pr/509/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/509/docs/data_transfer/fn.open_unidirectional_stream.html b/pr/509/docs/data_transfer/fn.open_unidirectional_stream.html new file mode 100644 index 000000000..dabbc95a8 --- /dev/null +++ b/pr/509/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/509/docs/data_transfer/fn.receive_bidirectional_stream.html b/pr/509/docs/data_transfer/fn.receive_bidirectional_stream.html new file mode 100644 index 000000000..7622451c8 --- /dev/null +++ b/pr/509/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/509/docs/data_transfer/fn.receive_datagram.html b/pr/509/docs/data_transfer/fn.receive_datagram.html new file mode 100644 index 000000000..53214940b --- /dev/null +++ b/pr/509/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/509/docs/data_transfer/fn.receive_unidirectional_stream.html b/pr/509/docs/data_transfer/fn.receive_unidirectional_stream.html new file mode 100644 index 000000000..3cb11052a --- /dev/null +++ b/pr/509/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/509/docs/data_transfer/fn.send_unreliable.html b/pr/509/docs/data_transfer/fn.send_unreliable.html new file mode 100644 index 000000000..eb16f5a71 --- /dev/null +++ b/pr/509/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/509/docs/data_transfer/index.html b/pr/509/docs/data_transfer/index.html new file mode 100644 index 000000000..bc41bd1c4 --- /dev/null +++ b/pr/509/docs/data_transfer/index.html @@ -0,0 +1 @@ +data_transfer - Rust

Crate data_transfer

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

All

Rustdoc help

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

Module congestion

Module congestion 

Source
Expand description

Logic for controlling the rate at which data is sent

+

Structs§

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

Traits§

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

Bbr

Struct Bbr 

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

Experimental! Use at your own risk.

+

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

+

Implementations§

Source§

impl Bbr

Source

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

Construct a state using the given config and current time now

+

Trait Implementations§

Source§

impl Clone for Bbr

Source§

fn clone(&self) -> Bbr

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

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

Performs copy-assignment from source. Read more
Source§

impl Controller for Bbr

Source§

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

One or more packets were just sent
Source§

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

Packet deliveries were confirmed Read more
Source§

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

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

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

Packets were deemed lost or marked congested Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

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

fn window(&self) -> u64

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

fn metrics(&self) -> ControllerMetrics

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

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

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

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

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

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

impl Debug for Bbr

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Bbr

§

impl RefUnwindSafe for Bbr

§

impl Send for Bbr

§

impl Sync for Bbr

§

impl Unpin for Bbr

§

impl UnwindSafe for Bbr

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

BbrConfig

Struct BbrConfig 

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

Configuration for the Bbr congestion controller

+

Implementations§

Source§

impl BbrConfig

Source

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

Default limit on the amount of outstanding data in bytes.

+

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

+

Trait Implementations§

Source§

impl Clone for BbrConfig

Source§

fn clone(&self) -> BbrConfig

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

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

Performs copy-assignment from source. Read more
Source§

impl ControllerFactory for BbrConfig

Source§

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

Construct a fresh Controller
Source§

impl Debug for BbrConfig

Source§

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

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

impl Default for BbrConfig

Source§

fn default() -> BbrConfig

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

Common congestion controller metrics

+

Fields (Non-exhaustive)§

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

Congestion window (bytes)

+
§ssthresh: Option<u64>

Slow start threshold (bytes)

+
§pacing_rate: Option<u64>

Pacing rate (bits/s)

+

Trait Implementations§

Source§

impl Debug for ControllerMetrics

Source§

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

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

impl Default for ControllerMetrics

Source§

fn default() -> ControllerMetrics

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/congestion/struct.Cubic.html b/pr/509/docs/noq/congestion/struct.Cubic.html new file mode 100644 index 000000000..860fb3b18 --- /dev/null +++ b/pr/509/docs/noq/congestion/struct.Cubic.html @@ -0,0 +1,48 @@ +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, + app_limited: bool, + rtt: &RttEstimator, +)

Packet deliveries were confirmed Read more
Source§

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

Packets were deemed lost or marked congested Read more
Source§

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

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

fn window(&self) -> u64

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

fn metrics(&self) -> ControllerMetrics

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

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

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

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

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

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

One or more packets were just sent
Source§

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

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

impl Debug for Cubic

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Cubic

§

impl RefUnwindSafe for Cubic

§

impl Send for Cubic

§

impl Sync for Cubic

§

impl Unpin for Cubic

§

impl UnwindSafe for Cubic

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/congestion/struct.CubicConfig.html b/pr/509/docs/noq/congestion/struct.CubicConfig.html new file mode 100644 index 000000000..a80cb6bc5 --- /dev/null +++ b/pr/509/docs/noq/congestion/struct.CubicConfig.html @@ -0,0 +1,33 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/congestion/struct.NewReno.html b/pr/509/docs/noq/congestion/struct.NewReno.html new file mode 100644 index 000000000..93b5448e6 --- /dev/null +++ b/pr/509/docs/noq/congestion/struct.NewReno.html @@ -0,0 +1,52 @@ +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, + app_limited: bool, + _rtt: &RttEstimator, +)

Packet deliveries were confirmed Read more
Source§

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

Packets were deemed lost or marked congested Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

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

fn window(&self) -> u64

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

fn metrics(&self) -> ControllerMetrics

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

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

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

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

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

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

One or more packets were just sent
Source§

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

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

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

impl Debug for NewReno

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/congestion/struct.NewRenoConfig.html b/pr/509/docs/noq/congestion/struct.NewRenoConfig.html new file mode 100644 index 000000000..dba590836 --- /dev/null +++ b/pr/509/docs/noq/congestion/struct.NewRenoConfig.html @@ -0,0 +1,34 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

Controller

Trait Controller 

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

Common interface for different congestion controllers

+

Required Methods§

Source

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

Packets were deemed lost or marked congested

+

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

+
Source

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated

+
Source

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight

+
Source

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

Duplicate the controller’s state

+
Source

fn initial_window(&self) -> u64

Initial congestion window

+
Source

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

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

+

Provided Methods§

Source

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

One or more packets were just sent

+
Source

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

Packet deliveries were confirmed

+

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

+
Source

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

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

+
Source

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost

+

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

+
Source

fn metrics(&self) -> ControllerMetrics

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

+

Implementors§

\ No newline at end of file diff --git a/pr/509/docs/noq/congestion/trait.ControllerFactory.html b/pr/509/docs/noq/congestion/trait.ControllerFactory.html new file mode 100644 index 000000000..e7464de35 --- /dev/null +++ b/pr/509/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/509/docs/noq/connection/enum.SendDatagramError.html b/pr/509/docs/noq/connection/enum.SendDatagramError.html new file mode 100644 index 000000000..c4cce6778 --- /dev/null +++ b/pr/509/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/509/docs/noq/connection/struct.AcceptBi.html b/pr/509/docs/noq/connection/struct.AcceptBi.html new file mode 100644 index 000000000..52de2cf57 --- /dev/null +++ b/pr/509/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/509/docs/noq/connection/struct.AcceptUni.html b/pr/509/docs/noq/connection/struct.AcceptUni.html new file mode 100644 index 000000000..f00a84c0c --- /dev/null +++ b/pr/509/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/509/docs/noq/connection/struct.Connecting.html b/pr/509/docs/noq/connection/struct.Connecting.html new file mode 100644 index 000000000..6414473aa --- /dev/null +++ b/pr/509/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/509/docs/noq/connection/struct.Connection.html b/pr/509/docs/noq/connection/struct.Connection.html new file mode 100644 index 000000000..bb7b05d42 --- /dev/null +++ b/pr/509/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/509/docs/noq/connection/struct.OnClosed.html b/pr/509/docs/noq/connection/struct.OnClosed.html new file mode 100644 index 000000000..0636d054a --- /dev/null +++ b/pr/509/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/509/docs/noq/connection/struct.OpenBi.html b/pr/509/docs/noq/connection/struct.OpenBi.html new file mode 100644 index 000000000..55c0bf806 --- /dev/null +++ b/pr/509/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/509/docs/noq/connection/struct.OpenUni.html b/pr/509/docs/noq/connection/struct.OpenUni.html new file mode 100644 index 000000000..8f6b44e8b --- /dev/null +++ b/pr/509/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/509/docs/noq/connection/struct.ReadDatagram.html b/pr/509/docs/noq/connection/struct.ReadDatagram.html new file mode 100644 index 000000000..e81baf121 --- /dev/null +++ b/pr/509/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/509/docs/noq/connection/struct.SendDatagram.html b/pr/509/docs/noq/connection/struct.SendDatagram.html new file mode 100644 index 000000000..0abedb619 --- /dev/null +++ b/pr/509/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/509/docs/noq/connection/struct.WeakConnectionHandle.html b/pr/509/docs/noq/connection/struct.WeakConnectionHandle.html new file mode 100644 index 000000000..9bad232d2 --- /dev/null +++ b/pr/509/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/509/docs/noq/connection/struct.ZeroRttAccepted.html b/pr/509/docs/noq/connection/struct.ZeroRttAccepted.html new file mode 100644 index 000000000..1965aca64 --- /dev/null +++ b/pr/509/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/509/docs/noq/crypto/index.html b/pr/509/docs/noq/crypto/index.html new file mode 100644 index 000000000..3266d8e96 --- /dev/null +++ b/pr/509/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/509/docs/noq/crypto/rustls/enum.Error.html b/pr/509/docs/noq/crypto/rustls/enum.Error.html new file mode 100644 index 000000000..c9a039a88 --- /dev/null +++ b/pr/509/docs/noq/crypto/rustls/enum.Error.html @@ -0,0 +1,101 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

HandshakeData

Struct HandshakeData 

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

Authentication data for (rustls) TLS session

+

Fields§

§protocol: Option<Vec<u8>>

The negotiated application protocol, if ALPN is in use

+

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

+
§server_name: Option<String>

The server name specified by the client, if any

+

Always None for outgoing connections

+
§negotiated_key_exchange_group: NamedGroup

The key exchange group negotiated with the peer

+

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/crypto/rustls/struct.NoInitialCipherSuite.html b/pr/509/docs/noq/crypto/rustls/struct.NoInitialCipherSuite.html new file mode 100644 index 000000000..0a68f632f --- /dev/null +++ b/pr/509/docs/noq/crypto/rustls/struct.NoInitialCipherSuite.html @@ -0,0 +1,31 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/crypto/rustls/struct.QuicClientConfig.html b/pr/509/docs/noq/crypto/rustls/struct.QuicClientConfig.html new file mode 100644 index 000000000..e3b9c6f62 --- /dev/null +++ b/pr/509/docs/noq/crypto/rustls/struct.QuicClientConfig.html @@ -0,0 +1,55 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/crypto/rustls/struct.QuicServerConfig.html b/pr/509/docs/noq/crypto/rustls/struct.QuicServerConfig.html new file mode 100644 index 000000000..0233a8dbd --- /dev/null +++ b/pr/509/docs/noq/crypto/rustls/struct.QuicServerConfig.html @@ -0,0 +1,59 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/crypto/rustls/struct.TlsSession.html b/pr/509/docs/noq/crypto/rustls/struct.TlsSession.html new file mode 100644 index 000000000..a093d41d6 --- /dev/null +++ b/pr/509/docs/noq/crypto/rustls/struct.TlsSession.html @@ -0,0 +1,38 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/crypto/sidebar-items.js b/pr/509/docs/noq/crypto/sidebar-items.js new file mode 100644 index 000000000..710b27671 --- /dev/null +++ b/pr/509/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/509/docs/noq/crypto/struct.CryptoError.html b/pr/509/docs/noq/crypto/struct.CryptoError.html new file mode 100644 index 000000000..5d6945444 --- /dev/null +++ b/pr/509/docs/noq/crypto/struct.CryptoError.html @@ -0,0 +1,25 @@ +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
Source§

impl From<Unspecified> for CryptoError

Source§

fn from(_: Unspecified) -> CryptoError

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/crypto/struct.ExportKeyingMaterialError.html b/pr/509/docs/noq/crypto/struct.ExportKeyingMaterialError.html new file mode 100644 index 000000000..95635cc39 --- /dev/null +++ b/pr/509/docs/noq/crypto/struct.ExportKeyingMaterialError.html @@ -0,0 +1,31 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/crypto/struct.KeyPair.html b/pr/509/docs/noq/crypto/struct.KeyPair.html new file mode 100644 index 000000000..8206dd365 --- /dev/null +++ b/pr/509/docs/noq/crypto/struct.KeyPair.html @@ -0,0 +1,36 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/crypto/struct.Keys.html b/pr/509/docs/noq/crypto/struct.Keys.html new file mode 100644 index 000000000..e39f252a5 --- /dev/null +++ b/pr/509/docs/noq/crypto/struct.Keys.html @@ -0,0 +1,30 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/crypto/struct.UnsupportedVersion.html b/pr/509/docs/noq/crypto/struct.UnsupportedVersion.html new file mode 100644 index 000000000..5686c151a --- /dev/null +++ b/pr/509/docs/noq/crypto/struct.UnsupportedVersion.html @@ -0,0 +1,25 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/crypto/trait.ClientConfig.html b/pr/509/docs/noq/crypto/trait.ClientConfig.html new file mode 100644 index 000000000..613ecf973 --- /dev/null +++ b/pr/509/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/509/docs/noq/crypto/trait.HandshakeTokenKey.html b/pr/509/docs/noq/crypto/trait.HandshakeTokenKey.html new file mode 100644 index 000000000..d1afef56b --- /dev/null +++ b/pr/509/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/509/docs/noq/crypto/trait.HeaderKey.html b/pr/509/docs/noq/crypto/trait.HeaderKey.html new file mode 100644 index 000000000..bd43de09f --- /dev/null +++ b/pr/509/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/509/docs/noq/crypto/trait.HmacKey.html b/pr/509/docs/noq/crypto/trait.HmacKey.html new file mode 100644 index 000000000..9b9937114 --- /dev/null +++ b/pr/509/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/509/docs/noq/crypto/trait.PacketKey.html b/pr/509/docs/noq/crypto/trait.PacketKey.html new file mode 100644 index 000000000..c670d4f6b --- /dev/null +++ b/pr/509/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/509/docs/noq/crypto/trait.ServerConfig.html b/pr/509/docs/noq/crypto/trait.ServerConfig.html new file mode 100644 index 000000000..1bab34898 --- /dev/null +++ b/pr/509/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/509/docs/noq/crypto/trait.Session.html b/pr/509/docs/noq/crypto/trait.Session.html new file mode 100644 index 000000000..b85c826d9 --- /dev/null +++ b/pr/509/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/509/docs/noq/endpoint/struct.Accept.html b/pr/509/docs/noq/endpoint/struct.Accept.html new file mode 100644 index 000000000..9cdddf024 --- /dev/null +++ b/pr/509/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/509/docs/noq/endpoint/struct.Endpoint.html b/pr/509/docs/noq/endpoint/struct.Endpoint.html new file mode 100644 index 000000000..7dcda303e --- /dev/null +++ b/pr/509/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/509/docs/noq/endpoint/struct.EndpointStats.html b/pr/509/docs/noq/endpoint/struct.EndpointStats.html new file mode 100644 index 000000000..83378cfb3 --- /dev/null +++ b/pr/509/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/509/docs/noq/enum.ConfigError.html b/pr/509/docs/noq/enum.ConfigError.html new file mode 100644 index 000000000..b92126b21 --- /dev/null +++ b/pr/509/docs/noq/enum.ConfigError.html @@ -0,0 +1,36 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/enum.ConnectError.html b/pr/509/docs/noq/enum.ConnectError.html new file mode 100644 index 000000000..734cd8e4a --- /dev/null +++ b/pr/509/docs/noq/enum.ConnectError.html @@ -0,0 +1,51 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/enum.FrameType.html b/pr/509/docs/noq/enum.FrameType.html new file mode 100644 index 000000000..31597fc3a --- /dev/null +++ b/pr/509/docs/noq/enum.FrameType.html @@ -0,0 +1,79 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/enum.ReadError.html b/pr/509/docs/noq/enum.ReadError.html new file mode 100644 index 000000000..155291a47 --- /dev/null +++ b/pr/509/docs/noq/enum.ReadError.html @@ -0,0 +1,45 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/enum.SendDatagramError.html b/pr/509/docs/noq/enum.SendDatagramError.html new file mode 100644 index 000000000..5340f7c76 --- /dev/null +++ b/pr/509/docs/noq/enum.SendDatagramError.html @@ -0,0 +1,44 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/fn.default_runtime.html b/pr/509/docs/noq/fn.default_runtime.html new file mode 100644 index 000000000..849241019 --- /dev/null +++ b/pr/509/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/509/docs/noq/incoming/struct.Incoming.html b/pr/509/docs/noq/incoming/struct.Incoming.html new file mode 100644 index 000000000..50fb0ce69 --- /dev/null +++ b/pr/509/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/509/docs/noq/incoming/struct.IncomingFuture.html b/pr/509/docs/noq/incoming/struct.IncomingFuture.html new file mode 100644 index 000000000..2f7f4e245 --- /dev/null +++ b/pr/509/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/509/docs/noq/incoming/struct.RetryError.html b/pr/509/docs/noq/incoming/struct.RetryError.html new file mode 100644 index 000000000..b813e4f34 --- /dev/null +++ b/pr/509/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/509/docs/noq/index.html b/pr/509/docs/noq/index.html new file mode 100644 index 000000000..99d773105 --- /dev/null +++ b/pr/509/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
ClosedStream
Error indicating that a stream has not been opened or has already been finished or reset
Connecting
In-progress connection attempt future
Connection
A QUIC connection.
ConnectionClose
Reason given by the transport for closing the connection
ConnectionId
Protocol-level identifier for a connection.
ConnectionStats
Connection statistics
Endpoint
A QUIC endpoint.
EndpointConfig
Global configuration for the endpoint, affecting all connections
EndpointStats
Statistics on Endpoint activity
FrameStats
Number of frames transmitted or received of each frame type
IdleTimeout
Maximum duration of inactivity to accept before timing out the connection
Incoming
An incoming connection for which the server has not yet begun its part of the handshake
IncomingFuture
Basic adapter to let Incoming be await-ed like a Connecting
InvalidCid
The connection ID was not recognized by the ConnectionIdGenerator
MtuDiscoveryConfig
Parameters governing MTU discovery.
NoneTokenLog
Null implementation of TokenLog, which never accepts tokens
NoneTokenStore
Null implementation of TokenStore, which does not store any tokens
OnClosed
Future returned by Connection::on_closed
OpenBi
Future produced by Connection::open_bi
OpenPath
Future produced by crate::Connection::open_path
OpenUni
Future produced by Connection::open_uni
Path
An open network transmission within a multipath-enabled 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.
Written
Indicates how many bytes and chunks had been transferred in a write operation
ZeroRttAccepted
Future that completes when a connection is fully established

Enums§

ConfigError
Errors in the configuration of an endpoint
ConnectError
Errors in the parameters being used to create a new connection
ConnectionError
Reasons why a connection might be lost
Dir
Whether a stream communicates data in both directions or only from the initiator
EcnCodepoint
Explicit congestion notification codepoint
FrameType
ReadError
Errors that arise from reading from a stream.
ReadExactError
Errors that arise from reading from a stream.
ReadToEndError
Errors from RecvStream::read_to_end
ResetError
Errors that arise while waiting for a stream to be reset
SendDatagramError
Errors that can arise when sending a datagram
Side
Whether an endpoint was the initiator of a connection
StoppedError
Errors that arise while monitoring for a send stream stop from the peer
WriteError
Errors that arise from writing to a stream

Traits§

AsyncTimer
Abstract implementation of an async timer for runtime independence
AsyncUdpSocket
Abstract implementation of a UDP socket for runtime independence
ConnectionIdGenerator
Generates connection IDs for incoming connections
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/509/docs/noq/path/struct.AddressDiscovery.html b/pr/509/docs/noq/path/struct.AddressDiscovery.html new file mode 100644 index 000000000..34e9370e2 --- /dev/null +++ b/pr/509/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/509/docs/noq/path/struct.OpenPath.html b/pr/509/docs/noq/path/struct.OpenPath.html new file mode 100644 index 000000000..e67f2e217 --- /dev/null +++ b/pr/509/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/509/docs/noq/path/struct.Path.html b/pr/509/docs/noq/path/struct.Path.html new file mode 100644 index 000000000..43ba55501 --- /dev/null +++ b/pr/509/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/509/docs/noq/path/struct.WeakPathHandle.html b/pr/509/docs/noq/path/struct.WeakPathHandle.html new file mode 100644 index 000000000..098ef640e --- /dev/null +++ b/pr/509/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/509/docs/noq/recv_stream/enum.ReadError.html b/pr/509/docs/noq/recv_stream/enum.ReadError.html new file mode 100644 index 000000000..2cd0f4dc9 --- /dev/null +++ b/pr/509/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/509/docs/noq/recv_stream/enum.ReadExactError.html b/pr/509/docs/noq/recv_stream/enum.ReadExactError.html new file mode 100644 index 000000000..6e058f920 --- /dev/null +++ b/pr/509/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/509/docs/noq/recv_stream/enum.ReadToEndError.html b/pr/509/docs/noq/recv_stream/enum.ReadToEndError.html new file mode 100644 index 000000000..1844a14af --- /dev/null +++ b/pr/509/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/509/docs/noq/recv_stream/enum.ResetError.html b/pr/509/docs/noq/recv_stream/enum.ResetError.html new file mode 100644 index 000000000..9f6e90992 --- /dev/null +++ b/pr/509/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/509/docs/noq/recv_stream/struct.RecvStream.html b/pr/509/docs/noq/recv_stream/struct.RecvStream.html new file mode 100644 index 000000000..a27b4685e --- /dev/null +++ b/pr/509/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/509/docs/noq/recv_stream/struct.UnorderedRecvStream.html b/pr/509/docs/noq/recv_stream/struct.UnorderedRecvStream.html new file mode 100644 index 000000000..d6f27060a --- /dev/null +++ b/pr/509/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/509/docs/noq/runtime/fn.default_runtime.html b/pr/509/docs/noq/runtime/fn.default_runtime.html new file mode 100644 index 000000000..67f4c870b --- /dev/null +++ b/pr/509/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/509/docs/noq/runtime/smol/struct.SmolRuntime.html b/pr/509/docs/noq/runtime/smol/struct.SmolRuntime.html new file mode 100644 index 000000000..e64385bd3 --- /dev/null +++ b/pr/509/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/509/docs/noq/runtime/tokio/struct.TokioRuntime.html b/pr/509/docs/noq/runtime/tokio/struct.TokioRuntime.html new file mode 100644 index 000000000..a2146491d --- /dev/null +++ b/pr/509/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/509/docs/noq/runtime/trait.AsyncTimer.html b/pr/509/docs/noq/runtime/trait.AsyncTimer.html new file mode 100644 index 000000000..9c7431b09 --- /dev/null +++ b/pr/509/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/509/docs/noq/runtime/trait.AsyncUdpSocket.html b/pr/509/docs/noq/runtime/trait.AsyncUdpSocket.html new file mode 100644 index 000000000..5f29e23be --- /dev/null +++ b/pr/509/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/509/docs/noq/runtime/trait.Runtime.html b/pr/509/docs/noq/runtime/trait.Runtime.html new file mode 100644 index 000000000..65709d454 --- /dev/null +++ b/pr/509/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/509/docs/noq/runtime/trait.UdpSender.html b/pr/509/docs/noq/runtime/trait.UdpSender.html new file mode 100644 index 000000000..c332897c7 --- /dev/null +++ b/pr/509/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/509/docs/noq/send_stream/enum.StoppedError.html b/pr/509/docs/noq/send_stream/enum.StoppedError.html new file mode 100644 index 000000000..35e9cb2cf --- /dev/null +++ b/pr/509/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/509/docs/noq/send_stream/enum.WriteError.html b/pr/509/docs/noq/send_stream/enum.WriteError.html new file mode 100644 index 000000000..8a8d44bef --- /dev/null +++ b/pr/509/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/509/docs/noq/send_stream/struct.SendStream.html b/pr/509/docs/noq/send_stream/struct.SendStream.html new file mode 100644 index 000000000..5e6ba28e0 --- /dev/null +++ b/pr/509/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/509/docs/noq/send_stream/struct.Stopped.html b/pr/509/docs/noq/send_stream/struct.Stopped.html new file mode 100644 index 000000000..c32498254 --- /dev/null +++ b/pr/509/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/509/docs/noq/sidebar-items.js b/pr/509/docs/noq/sidebar-items.js new file mode 100644 index 000000000..9f506426c --- /dev/null +++ b/pr/509/docs/noq/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["ConfigError","ConnectError","ConnectionError","Dir","EcnCodepoint","FrameType","ReadError","ReadExactError","ReadToEndError","ResetError","SendDatagramError","Side","StoppedError","WriteError"],"fn":["default_runtime"],"mod":["congestion","crypto"],"struct":["Accept","AcceptBi","AcceptUni","AckFrequencyConfig","AddressDiscovery","ApplicationClose","BloomTokenLog","Chunk","ClientConfig","ClosedStream","Connecting","Connection","ConnectionClose","ConnectionId","ConnectionStats","Endpoint","EndpointConfig","EndpointStats","FrameStats","IdleTimeout","Incoming","IncomingFuture","InvalidCid","MtuDiscoveryConfig","NoneTokenLog","NoneTokenStore","OnClosed","OpenBi","OpenPath","OpenUni","Path","PathId","PathStats","QlogConfig","QlogFileFactory","ReadDatagram","RecvStream","RetryError","SendDatagram","SendStream","ServerConfig","SmolRuntime","StdSystemTime","Stopped","StreamId","TokenMemoryCache","TokenReuseError","TokioRuntime","Transmit","TransportConfig","TransportErrorCode","UdpStats","UnorderedRecvStream","ValidationTokenConfig","VarInt","VarIntBoundsExceeded","WeakConnectionHandle","WeakPathHandle","Written","ZeroRttAccepted"],"trait":["AsyncTimer","AsyncUdpSocket","ConnectionIdGenerator","NetworkChangeHint","QlogFactory","Runtime","TimeSource","TokenLog","TokenStore","UdpSender"]}; \ No newline at end of file diff --git a/pr/509/docs/noq/struct.Accept.html b/pr/509/docs/noq/struct.Accept.html new file mode 100644 index 000000000..7434c4d7f --- /dev/null +++ b/pr/509/docs/noq/struct.Accept.html @@ -0,0 +1,50 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.AcceptBi.html b/pr/509/docs/noq/struct.AcceptBi.html new file mode 100644 index 000000000..4ffb7915f --- /dev/null +++ b/pr/509/docs/noq/struct.AcceptBi.html @@ -0,0 +1,54 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.AckFrequencyConfig.html b/pr/509/docs/noq/struct.AckFrequencyConfig.html new file mode 100644 index 000000000..6635b5e54 --- /dev/null +++ b/pr/509/docs/noq/struct.AckFrequencyConfig.html @@ -0,0 +1,65 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.AddressDiscovery.html b/pr/509/docs/noq/struct.AddressDiscovery.html new file mode 100644 index 000000000..2ccf38d49 --- /dev/null +++ b/pr/509/docs/noq/struct.AddressDiscovery.html @@ -0,0 +1,173 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.Chunk.html b/pr/509/docs/noq/struct.Chunk.html new file mode 100644 index 000000000..7b3456024 --- /dev/null +++ b/pr/509/docs/noq/struct.Chunk.html @@ -0,0 +1,35 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.ClientConfig.html b/pr/509/docs/noq/struct.ClientConfig.html new file mode 100644 index 000000000..980a6be01 --- /dev/null +++ b/pr/509/docs/noq/struct.ClientConfig.html @@ -0,0 +1,49 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.Connecting.html b/pr/509/docs/noq/struct.Connecting.html new file mode 100644 index 000000000..f80cf4635 --- /dev/null +++ b/pr/509/docs/noq/struct.Connecting.html @@ -0,0 +1,101 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.Connection.html b/pr/509/docs/noq/struct.Connection.html new file mode 100644 index 000000000..74b5b30ca --- /dev/null +++ b/pr/509/docs/noq/struct.Connection.html @@ -0,0 +1,216 @@ +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, + addr: SocketAddr, + initial_status: PathStatus, +) -> OpenPath

Opens a new path if no path exists yet for the remote address.

+

Otherwise behaves exactly as open_path.

+
Source

pub fn open_path( + &self, + addr: SocketAddr, + initial_status: PathStatus, +) -> OpenPath

Opens an additional path if the multipath extension is negotiated.

+

The returned future completes once the path is either fully opened and ready to +carry application data, or if there was an error.

+

Dropping the returned future does not cancel the opening of the path, the +PathEvent::Opened event will still be emitted from Self::path_events if the +path opens. The PathId for the events can be extracted from +OpenPath::path_id.

+

Failure to open a path can either occur immediately, before polling the returned +future, or at a later time. If the failure is immediate OpenPath::path_id will +return None and the future will be ready immediately. If the failure happens +later, a PathEvent will be emitted.

+
Source

pub fn path(&self, id: PathId) -> Option<Path>

Returns the Path structure of an open path

+
Source

pub fn path_events(&self) -> Receiver<PathEvent>

A broadcast receiver of PathEvents for all paths in this connection

+
Source

pub fn nat_traversal_updates(&self) -> Receiver<Event>

A broadcast receiver of n0_nat_traversal::Events for updates about server addresses

+
Source

pub async fn closed(&self) -> ConnectionError

Wait for the connection to be closed for any reason

+

Despite the return type’s name, closed connections are often not an error condition at the +application layer. Cases that might be routine include ConnectionError::LocallyClosed +and ConnectionError::ApplicationClosed.

+
Source

pub fn on_closed(&self) -> OnClosed

Wait for the connection to be closed without keeping a strong reference to the connection

+

Returns a future that resolves, once the connection is closed, to a tuple of +(ConnectionError, ConnectionStats).

+

Calling Self::closed keeps the connection alive until it is either closed locally via Connection::close +or closed by the remote peer. This function instead does not keep the connection itself alive, +so if all other clones of the connection are dropped, the connection will be closed implicitly even +if there are futures returned from this function still being awaited.

+
Source

pub fn close_reason(&self) -> Option<ConnectionError>

If the connection is closed, the reason why.

+

Returns None if the connection is still open.

+
Source

pub fn close(&self, error_code: VarInt, reason: &[u8])

Close the connection immediately.

+

Pending operations will fail immediately with ConnectionError::LocallyClosed. No +more data is sent to the peer and the peer may drop buffered data upon receiving +the CONNECTION_CLOSE frame.

+

error_code and reason are not interpreted, and are provided directly to the peer.

+

reason will be truncated to fit in a single packet with overhead; to improve odds that it +is preserved in full, it should be kept under 1KiB.

+
§Gracefully closing a connection
+

Only the peer last receiving application data can be certain that all data is +delivered. The only reliable action it can then take is to close the connection, +potentially with a custom error code. The delivery of the final CONNECTION_CLOSE +frame is very likely if both endpoints stay online long enough, and +Endpoint::wait_idle() can be used to provide sufficient time. Otherwise, the +remote peer will time out the connection, provided that the idle timeout is not +disabled.

+

The sending side can not guarantee all stream data is delivered to the remote +application. It only knows the data is delivered to the QUIC stack of the remote +endpoint. Once the local side sends a CONNECTION_CLOSE frame in response to calling +close() the remote endpoint may drop any data it received but is as yet +undelivered to the application, including data that was acknowledged as received to +the local endpoint.

+
Source

pub async fn handshake_confirmed(&self) -> Result<(), ConnectionError>

Wait for the handshake to be confirmed.

+

As a server, who must be authenticated by clients, +this happens when the handshake completes +upon receiving a TLS Finished message from the client. +In return, the server send a HANDSHAKE_DONE frame.

+

As a client, this happens when receiving a HANDSHAKE_DONE frame. +At this point, the server has either accepted our authentication, +or, if client authentication is not required, accepted our lack of authentication.

+
Source

pub fn send_datagram(&self, data: Bytes) -> Result<(), SendDatagramError>

Transmit data as an unreliable, unordered application datagram

+

Application datagrams are a low-level primitive. They may be lost or delivered out of order, +and data must both fit inside a single QUIC packet and be smaller than the maximum +dictated by the peer.

+

Previously queued datagrams which are still unsent may be discarded to make space for this +datagram, in order of oldest to newest.

+
Source

pub fn send_datagram_wait(&self, data: Bytes) -> SendDatagram<'_>

Transmit data as an unreliable, unordered application datagram

+

Unlike send_datagram(), this method will wait for buffer space during congestion +conditions, which effectively prioritizes old datagrams over new datagrams.

+

See send_datagram() for details.

+
Source

pub fn max_datagram_size(&self) -> Option<usize>

Compute the maximum size of datagrams that may be passed to send_datagram().

+

Returns None if datagrams are unsupported by the peer or disabled locally.

+

This may change over the lifetime of a connection according to variation in the path MTU +estimate. The peer can also enforce an arbitrarily small fixed limit, but if the peer’s +limit is large this is guaranteed to be a little over a kilobyte at minimum.

+

Not necessarily the maximum size of received datagrams.

+
Source

pub fn datagram_send_buffer_space(&self) -> usize

Bytes available in the outgoing datagram buffer

+

When greater than zero, calling send_datagram() with a datagram of +at most this size is guaranteed not to cause older datagrams to be dropped.

+
Source

pub fn side(&self) -> Side

The side of the connection (client or server)

+
Source

pub fn remote_address(&self) -> SocketAddr

The peer’s UDP address

+

If ServerConfig::migration is true, clients may change addresses at will, +e.g. when switching to a cellular internet connection.

+

If multipath is enabled this will return the address of any +path, and may not be consistent. Prefer Path::remote_address instead.

+
Source

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

The local IP address which was used when the peer established +the connection

+

This can be different from the address the endpoint is bound to, in case +the endpoint is bound to a wildcard address like 0.0.0.0 or ::.

+

This will return None for clients, or when the platform does not expose this +information. See noq_udp::RecvMeta::dst_ip for a list of +supported platforms when using noq_udp for I/O, which is the default.

+
Source

pub fn rtt(&self, path_id: PathId) -> Option<Duration>

Current best estimate of this connection’s latency (round-trip-time)

+
Source

pub fn stats(&self) -> ConnectionStats

Returns connection statistics

+
Source

pub fn path_stats(&self, path_id: PathId) -> Option<PathStats>

Returns path statistics

+
Source

pub fn congestion_state(&self, path_id: PathId) -> Option<Box<dyn Controller>>

Current state of the congestion control algorithm, for debugging purposes

+
Source

pub fn handshake_data(&self) -> Option<Box<dyn Any>>

Parameters negotiated during the handshake

+

Guaranteed to return Some on fully established connections or after +Connecting::handshake_data() succeeds. See that method’s documentations for details on +the returned value.

+
Source

pub fn peer_identity(&self) -> Option<Box<dyn Any>>

Cryptographic identity of the peer

+

The dynamic type returned is determined by the configured +Session. For the default rustls session, the return value can +be downcast to a Vec<[rustls::pki_types::CertificateDer]>

+
Source

pub fn stable_id(&self) -> usize

A stable identifier for this connection

+

Peer addresses and connection IDs can change, but this value will remain +fixed for the lifetime of the connection.

+
Source

pub fn force_key_update(&self)

Update traffic keys spontaneously

+

This primarily exists for testing purposes.

+
Source

pub fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: &[u8], +) -> Result<(), ExportKeyingMaterialError>

Derive keying material from this connection’s TLS session secrets.

+

When both peers call this method with the same label and context +arguments and output buffers of equal length, they will get the +same sequence of bytes in output. These bytes are cryptographically +strong and pseudorandom, and are suitable for use as keying material.

+

See RFC5705 for more information.

+
Source

pub fn set_max_concurrent_uni_streams(&self, count: VarInt)

Modify the number of remotely initiated unidirectional streams that may be concurrently open

+

No streams may be opened by the peer unless fewer than count are already open. Large +counts increase both minimum and worst-case memory consumption.

+
Source

pub fn set_send_window(&self, send_window: u64)

Source

pub fn set_receive_window(&self, receive_window: VarInt)

Source

pub fn set_max_concurrent_bi_streams(&self, count: VarInt)

Modify the number of remotely initiated bidirectional streams that may be concurrently open

+

No streams may be opened by the peer unless fewer than count are already open. Large +counts increase both minimum and worst-case memory consumption.

+
Source

pub fn observed_external_addr(&self) -> Receiver<Option<SocketAddr>>

Track changed on our external address as reported by the peer.

+
Source

pub fn is_multipath_enabled(&self) -> bool

Is multipath enabled?

+
Source

pub fn add_nat_traversal_address( + &self, + address: SocketAddr, +) -> Result<(), Error>

Registers one address at which this endpoint might be reachable

+

When the NAT traversal extension is negotiated, servers send these addresses to clients in +ADD_ADDRESS frames. This allows clients to obtain server address candidates to initiate +NAT traversal attempts. Clients provide their own reachable addresses in REACH_OUT frames +when Self::initiate_nat_traversal_round is called.

+
Source

pub fn remove_nat_traversal_address( + &self, + address: SocketAddr, +) -> Result<(), Error>

Removes one or more addresses from the set of addresses at which this endpoint is reachable

+

When the NAT traversal extension is negotiated, servers send address removals to +clients in REMOVE_ADDRESS frames. This allows clients to stop using outdated +server address candidates that are no longer valid for NAT traversal.

+

For clients, removed addresses will no longer be advertised in REACH_OUT frames.

+

Addresses not present in the set will be silently ignored.

+
Source

pub fn get_local_nat_traversal_addresses( + &self, +) -> Result<Vec<SocketAddr>, Error>

Get the current local nat traversal addresses

+
Source

pub fn get_remote_nat_traversal_addresses( + &self, +) -> Result<Vec<SocketAddr>, Error>

Get the currently advertised nat traversal addresses by the server

+
Source

pub fn initiate_nat_traversal_round(&self) -> Result<Vec<SocketAddr>, Error>

Initiates a new nat traversal round

+

A nat traversal round involves advertising the client’s local addresses in REACH_OUT +frames, and initiating probing of the known remote addresses. When a new round is +initiated, the previous one is cancelled, and paths that have not been opened are closed.

+

Returns the server addresses that are now being probed.

+

Trait Implementations§

Source§

impl Clone for Connection

Source§

fn clone(&self) -> Connection

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for Connection

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.ConnectionClose.html b/pr/509/docs/noq/struct.ConnectionClose.html new file mode 100644 index 000000000..cab42546e --- /dev/null +++ b/pr/509/docs/noq/struct.ConnectionClose.html @@ -0,0 +1,40 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

Connection statistics

+

Fields (Non-exhaustive)§

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

Statistics about UDP datagrams transmitted on a connection

+
§udp_rx: UdpStats

Statistics about UDP datagrams received on a connection

+
§frame_tx: FrameStats

Statistics about frames transmitted on a connection

+
§frame_rx: FrameStats

Statistics about frames received on a connection

+

Trait Implementations§

Source§

impl Clone for ConnectionStats

Source§

fn clone(&self) -> ConnectionStats

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionStats

Source§

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

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

impl Default for ConnectionStats

Source§

fn default() -> ConnectionStats

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

+

Platform defaults for dual-stack sockets vary. For example, any socket bound to a wildcard +IPv6 address on Windows will not by default be able to communicate with IPv4 +addresses. Portable applications should bind an address that matches the family they wish to +communicate within.

+
Source

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

Construct an endpoint with arbitrary configuration and socket

+
Source

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

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

+

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

+
Source

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

Get the next incoming connection attempt from a client

+

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

+
Source

pub fn set_default_client_config(&self, config: ClientConfig)

Set the client configuration used by connect

+
Source

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

Connect to a remote endpoint

+

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

+

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

+
Source

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

Connect to a remote endpoint using a custom configuration.

+

See connect() for details.

+
Source

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

Switch to a new UDP socket

+

See Endpoint::rebind_abstract() for details.

+
Source

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

Switch to a new UDP socket

+

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

+

On error, the old UDP socket is retained.

+
Source

pub fn 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)

Wait for all connections on the endpoint to be cleanly shut down

+

Waiting for this condition before exiting ensures that a good-faith effort is made to notify +peers of recent connection closes, whereas exiting immediately could force them to wait out +the idle timeout period.

+

Does not proactively close existing connections or cause incoming connections to be +rejected. Consider calling close() if that is desired.

+

Trait Implementations§

Source§

impl Clone for Endpoint

Source§

fn clone(&self) -> Endpoint

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for Endpoint

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.EndpointConfig.html b/pr/509/docs/noq/struct.EndpointConfig.html new file mode 100644 index 000000000..442c4b384 --- /dev/null +++ b/pr/509/docs/noq/struct.EndpointConfig.html @@ -0,0 +1,67 @@ +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<F>(&mut self, factory: F) -> &mut EndpointConfig
where + F: Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync + 'static,

Supply a custom connection ID generator factory

+

Called once by each Endpoint constructed from this configuration to obtain the CID +generator which will be used to generate the CIDs used for incoming packets on all +connections involving that Endpoint. A custom CID generator allows applications to embed +information in local connection IDs, e.g. to support stateless packet-level load balancers.

+

Defaults to HashedConnectionIdGenerator.

+
Source

pub fn reset_key(&mut self, key: Arc<dyn HmacKey>) -> &mut EndpointConfig

Private key used to send authenticated connection resets to peers who were +communicating with a previous instance of this endpoint.

+
Source

pub fn max_udp_payload_size( + &mut self, + value: u16, +) -> Result<&mut EndpointConfig, ConfigError>

Maximum UDP payload size accepted from peers (excluding UDP and IP overhead).

+

Must be greater or equal than 1200.

+

Defaults to 1472, which is the largest UDP payload that can be transmitted in the typical +1500 byte Ethernet MTU. Deployments on links with larger MTUs (e.g. loopback or Ethernet +with jumbo frames) can raise this to improve performance at the cost of a linear increase in +datagram receive buffer size.

+
Source

pub fn get_max_udp_payload_size(&self) -> u64

Get the current value of max_udp_payload_size

+
Source

pub fn supported_versions( + &mut self, + supported_versions: Vec<u32>, +) -> &mut EndpointConfig

Override supported QUIC versions

+
Source

pub fn grease_quic_bit(&mut self, value: bool) -> &mut EndpointConfig

Whether to accept QUIC packets containing any value for the fixed bit

+

Enabled by default. Helps protect against protocol ossification and makes traffic less +identifiable to observers. Disable if helping observers identify this traffic as QUIC is +desired.

+
Source

pub fn min_reset_interval(&mut self, value: Duration) -> &mut EndpointConfig

Minimum interval between outgoing stateless reset packets

+

Defaults to 20ms. Limits the impact of attacks which flood an endpoint with garbage packets, +e.g. ISAKMP/IKE amplification. Larger values provide a stronger defense, but may delay +detection of some error conditions by clients. Using a ConnectionIdGenerator with a low +rate of false positives in validate reduces the risk +incurred by a small minimum reset interval.

+
Source

pub fn rng_seed(&mut self, seed: Option<[u8; 32]>) -> &mut EndpointConfig

Optional seed to be used internally for random number generation

+

By default, 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.EndpointStats.html b/pr/509/docs/noq/struct.EndpointStats.html new file mode 100644 index 000000000..e8cc59d24 --- /dev/null +++ b/pr/509/docs/noq/struct.EndpointStats.html @@ -0,0 +1,36 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

Source§

fn clone(&self) -> FrameStats

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for FrameStats

Source§

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

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

impl Default for FrameStats

Source§

fn default() -> FrameStats

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

impl Copy for FrameStats

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.IdleTimeout.html b/pr/509/docs/noq/struct.IdleTimeout.html new file mode 100644 index 000000000..58371431f --- /dev/null +++ b/pr/509/docs/noq/struct.IdleTimeout.html @@ -0,0 +1,52 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.Incoming.html b/pr/509/docs/noq/struct.Incoming.html new file mode 100644 index 000000000..963e84c21 --- /dev/null +++ b/pr/509/docs/noq/struct.Incoming.html @@ -0,0 +1,49 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.InvalidCid.html b/pr/509/docs/noq/struct.InvalidCid.html new file mode 100644 index 000000000..17724bc95 --- /dev/null +++ b/pr/509/docs/noq/struct.InvalidCid.html @@ -0,0 +1,27 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.MtuDiscoveryConfig.html b/pr/509/docs/noq/struct.MtuDiscoveryConfig.html new file mode 100644 index 000000000..566acb383 --- /dev/null +++ b/pr/509/docs/noq/struct.MtuDiscoveryConfig.html @@ -0,0 +1,88 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.NoneTokenLog.html b/pr/509/docs/noq/struct.NoneTokenLog.html new file mode 100644 index 000000000..79b68143d --- /dev/null +++ b/pr/509/docs/noq/struct.NoneTokenLog.html @@ -0,0 +1,31 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.NoneTokenStore.html b/pr/509/docs/noq/struct.NoneTokenStore.html new file mode 100644 index 000000000..1cefcf1c5 --- /dev/null +++ b/pr/509/docs/noq/struct.NoneTokenStore.html @@ -0,0 +1,25 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

OnClosed

Struct OnClosed 

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

Future returned by Connection::on_closed

+

Resolves to a tuple of (ConnectionError, ConnectionStats).

+

Trait Implementations§

Source§

impl Drop for OnClosed

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl Future for OnClosed

Source§

type Output = (ConnectionError, ConnectionStats)

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

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

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

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

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

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

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.OpenBi.html b/pr/509/docs/noq/struct.OpenBi.html new file mode 100644 index 000000000..ab4071d1d --- /dev/null +++ b/pr/509/docs/noq/struct.OpenBi.html @@ -0,0 +1,54 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.OpenPath.html b/pr/509/docs/noq/struct.OpenPath.html new file mode 100644 index 000000000..77a9de14e --- /dev/null +++ b/pr/509/docs/noq/struct.OpenPath.html @@ -0,0 +1,57 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.OpenUni.html b/pr/509/docs/noq/struct.OpenUni.html new file mode 100644 index 000000000..63f458f35 --- /dev/null +++ b/pr/509/docs/noq/struct.OpenUni.html @@ -0,0 +1,54 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

Sets the PathStatus of this 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 keep_alive_interval for a specific path

+

See TransportConfig::default_path_keep_alive_interval for details.

+

Returns the previous value of the setting.

+
Source

pub fn set_keep_alive_interval( + &self, + interval: Option<Duration>, +) -> Result<Option<Duration>, ClosedPath>

Sets the keep_alive_interval for a specific path

+

See TransportConfig::default_path_keep_alive_interval for details.

+

Returns the previous value of the setting.

+
Source

pub fn observed_external_addr(&self) -> Result<AddressDiscovery, ClosedPath>

Track changes on our external address as reported by the peer.

+

If the address-discovery extension is not negotiated, the stream will never return.

+
Source

pub fn remote_address(&self) -> Result<SocketAddr, ClosedPath>

The peer’s UDP address for this path.

+
Source

pub fn ping(&self) -> Result<(), ClosedPath>

Ping the remote endpoint over this path.

+

Trait Implementations§

Source§

impl Clone for Path

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

Source§

impl StructuralPartialEq for PathId

Auto Trait Implementations§

§

impl Freeze for PathId

§

impl RefUnwindSafe for PathId

§

impl Send for PathId

§

impl Sync for PathId

§

impl Unpin for PathId

§

impl UnwindSafe for PathId

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

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

§

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

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

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

§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

PathStats

Struct PathStats 

Source
#[non_exhaustive]
pub struct PathStats { + pub rtt: Duration, + pub udp_tx: UdpStats, + pub udp_rx: UdpStats, + pub cwnd: u64, + pub congestion_events: u64, + pub lost_packets: u64, + pub lost_bytes: u64, + pub sent_plpmtud_probes: u64, + pub lost_plpmtud_probes: u64, + pub black_holes_detected: u64, + pub current_mtu: u16, +}
Expand description

Statistics related to a transmission path

+

Fields (Non-exhaustive)§

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

Current best estimate of this connection’s latency (round-trip-time)

+
§udp_tx: UdpStats

Statistics about datagrams and bytes sent on this path

+
§udp_rx: UdpStats

Statistics about datagrams and bytes received on this path

+
§cwnd: u64

Current congestion window of the connection

+
§congestion_events: u64

Congestion events on the connection

+
§lost_packets: u64

The amount of packets lost on this path

+
§lost_bytes: u64

The amount of bytes lost on this path

+
§sent_plpmtud_probes: u64

The amount of PLPMTUD probe packets sent on this path (also counted by udp_tx.datagrams)

+
§lost_plpmtud_probes: u64

The amount of PLPMTUD probe packets lost on this path (ignored by lost_packets and +lost_bytes)

+
§black_holes_detected: u64

The number of times a black hole was detected in the path

+
§current_mtu: u16

Largest UDP payload size the path currently supports

+

Trait Implementations§

Source§

impl Clone for PathStats

Source§

fn clone(&self) -> PathStats

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for PathStats

Source§

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

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

impl Default for PathStats

Source§

fn default() -> PathStats

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

impl PartialEq for PathStats

Source§

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

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

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

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

impl Copy for PathStats

Source§

impl Eq for PathStats

Source§

impl StructuralPartialEq for PathStats

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

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

§

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

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

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

§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.QlogConfig.html b/pr/509/docs/noq/struct.QlogConfig.html new file mode 100644 index 000000000..f272c84a0 --- /dev/null +++ b/pr/509/docs/noq/struct.QlogConfig.html @@ -0,0 +1,34 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.QlogFileFactory.html b/pr/509/docs/noq/struct.QlogFileFactory.html new file mode 100644 index 000000000..d9d45dd88 --- /dev/null +++ b/pr/509/docs/noq/struct.QlogFileFactory.html @@ -0,0 +1,39 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.ReadDatagram.html b/pr/509/docs/noq/struct.ReadDatagram.html new file mode 100644 index 000000000..44ad4a599 --- /dev/null +++ b/pr/509/docs/noq/struct.ReadDatagram.html @@ -0,0 +1,54 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.RecvStream.html b/pr/509/docs/noq/struct.RecvStream.html new file mode 100644 index 000000000..b82aa92dc --- /dev/null +++ b/pr/509/docs/noq/struct.RecvStream.html @@ -0,0 +1,210 @@ +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 fn poll_read_buf( + &mut self, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, +) -> Poll<Result<(), ReadError>>

Attempts to read from the stream into the provided buffer, which may be uninitialized

+

On success, returns Poll::Ready(Ok(())) and places data into the unfilled portion of +buf. If this does not write any bytes to buf (and buf.remaining() is non-zero), that +indicates that the remote side has finished the stream and the local side has already +read all bytes.

+

If no data is available for reading, this returns Poll::Pending and arranges for the +current task (via cx.waker()) to be notified when the stream becomes readable or is +closed.

+
Source

pub async fn read_chunk( + &mut self, + max_length: usize, +) -> Result<Option<Chunk>, ReadError>

Read the next segment of data

+

Yields None if the stream was finished. Otherwise, yields a segment of data and its +offset in the stream. The chunk’s offset will be immediately after +the last data yielded by RecvStream::read or RecvStream::read_chunk.

+

For unordered reads, convert the stream into an unordered stream using Self::into_unordered.

+

Slightly more efficient than RecvStream::read due to not copying. Chunk boundaries do not correspond +to peer writes, and hence cannot be used as framing.

+

This operation is cancel-safe.

+
Source

pub async fn read_chunks( + &mut self, + bufs: &mut [Bytes], +) -> Result<Option<usize>, ReadError>

Read the next segments of data

+

Fills bufs with the segments of data beginning immediately after the +last data yielded by read or read_chunk, or None if the stream was +finished.

+

Slightly more efficient than read due to not copying. Chunk boundaries +do not correspond to peer writes, and hence cannot be used as framing.

+

This operation is cancel-safe.

+
Source

pub async fn read_to_end( + &mut self, + size_limit: usize, +) -> Result<Vec<u8>, ReadToEndError>

Convenience method to read all remaining data into a buffer

+

Fails with ReadToEndError::TooLong on reading more than size_limit bytes, discarding +all data read. Uses unordered reads to be more efficient than using AsyncRead would +allow. size_limit should be set to limit worst-case memory use.

+

If unordered reads have already been made, the resulting buffer may have gaps containing +arbitrary data.

+

This operation is not cancel-safe.

+
Source

pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream>

Stop accepting data

+

Discards unread data and notifies the peer to stop transmitting. Once stopped, further +attempts to operate on a stream will yield ClosedStream errors.

+
Source

pub fn is_0rtt(&self) -> bool

Check if this stream has been opened during 0-RTT.

+

In which case any non-idempotent request should be considered dangerous at the application +level. Because read data is subject to replay attacks.

+
Source

pub fn id(&self) -> StreamId

Get the identity of this stream

+
Source

pub async fn received_reset(&mut self) -> Result<Option<VarInt>, ResetError>

Completes when the stream has been reset by the peer or otherwise closed

+

Yields Some with the reset error code when the stream is reset by the peer. Yields None +when the stream was previously stop()ed, or when the stream was +finish()ed by the peer and all data has been received, after +which it is no longer meaningful for the stream to be reset.

+

This operation is cancel-safe.

+
Source

pub fn into_unordered(self) -> UnorderedRecvStream

Converts this stream into an unordered stream.

+

Trait Implementations§

Source§

impl AsyncRead for RecvStream

Available on crate feature futures-io only.
Source§

fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], +) -> Poll<Result<usize>>

Attempt to read from the AsyncRead into buf. Read more
§

fn poll_read_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], +) -> Poll<Result<usize, Error>>

Attempt to read from the AsyncRead into bufs using vectored +IO operations. Read more
Source§

impl AsyncRead for RecvStream

Source§

fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, +) -> Poll<Result<()>>

Attempts to read from the AsyncRead into buf. Read more
Source§

impl Debug for RecvStream

Source§

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

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

impl Drop for RecvStream

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<R> AsyncReadExt for R
where + R: AsyncRead + ?Sized,

§

fn chain<R>(self, next: R) -> Chain<Self, R>
where + Self: Sized, + R: AsyncRead,

Creates a new AsyncRead instance that chains this stream with +next. Read more
§

fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self>
where + Self: Unpin,

Pulls some bytes from this source into the specified buffer, +returning how many bytes were read. Read more
§

fn read_buf<'a, B>(&'a mut self, buf: &'a mut B) -> ReadBuf<'a, Self, B>
where + Self: Unpin, + B: BufMut + ?Sized,

Pulls some bytes from this source into the specified buffer, +advancing the buffer’s internal cursor. Read more
§

fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self>
where + Self: Unpin,

Reads the exact number of bytes required to fill buf. Read more
§

fn read_u8(&mut self) -> ReadU8<&mut Self>
where + Self: Unpin,

Reads an unsigned 8 bit integer from the underlying reader. Read more
§

fn read_i8(&mut self) -> ReadI8<&mut Self>
where + Self: Unpin,

Reads a signed 8 bit integer from the underlying reader. Read more
§

fn read_u16(&mut self) -> ReadU16<&mut Self>
where + Self: Unpin,

Reads an unsigned 16-bit integer in big-endian order from the +underlying reader. Read more
§

fn read_i16(&mut self) -> ReadI16<&mut Self>
where + Self: Unpin,

Reads a signed 16-bit integer in big-endian order from the +underlying reader. Read more
§

fn read_u32(&mut self) -> ReadU32<&mut Self>
where + Self: Unpin,

Reads an unsigned 32-bit integer in big-endian order from the +underlying reader. Read more
§

fn read_i32(&mut self) -> ReadI32<&mut Self>
where + Self: Unpin,

Reads a signed 32-bit integer in big-endian order from the +underlying reader. Read more
§

fn read_u64(&mut self) -> ReadU64<&mut Self>
where + Self: Unpin,

Reads an unsigned 64-bit integer in big-endian order from the +underlying reader. Read more
§

fn read_i64(&mut self) -> ReadI64<&mut Self>
where + Self: Unpin,

Reads an signed 64-bit integer in big-endian order from the +underlying reader. Read more
§

fn read_u128(&mut self) -> ReadU128<&mut Self>
where + Self: Unpin,

Reads an unsigned 128-bit integer in big-endian order from the +underlying reader. Read more
§

fn read_i128(&mut self) -> ReadI128<&mut Self>
where + Self: Unpin,

Reads an signed 128-bit integer in big-endian order from the +underlying reader. Read more
§

fn read_f32(&mut self) -> ReadF32<&mut Self>
where + Self: Unpin,

Reads an 32-bit floating point type in big-endian order from the +underlying reader. Read more
§

fn read_f64(&mut self) -> ReadF64<&mut Self>
where + Self: Unpin,

Reads an 64-bit floating point type in big-endian order from the +underlying reader. Read more
§

fn read_u16_le(&mut self) -> ReadU16Le<&mut Self>
where + Self: Unpin,

Reads an unsigned 16-bit integer in little-endian order from the +underlying reader. Read more
§

fn read_i16_le(&mut self) -> ReadI16Le<&mut Self>
where + Self: Unpin,

Reads a signed 16-bit integer in little-endian order from the +underlying reader. Read more
§

fn read_u32_le(&mut self) -> ReadU32Le<&mut Self>
where + Self: Unpin,

Reads an unsigned 32-bit integer in little-endian order from the +underlying reader. Read more
§

fn read_i32_le(&mut self) -> ReadI32Le<&mut Self>
where + Self: Unpin,

Reads a signed 32-bit integer in little-endian order from the +underlying reader. Read more
§

fn read_u64_le(&mut self) -> ReadU64Le<&mut Self>
where + Self: Unpin,

Reads an unsigned 64-bit integer in little-endian order from the +underlying reader. Read more
§

fn read_i64_le(&mut self) -> ReadI64Le<&mut Self>
where + Self: Unpin,

Reads an signed 64-bit integer in little-endian order from the +underlying reader. Read more
§

fn read_u128_le(&mut self) -> ReadU128Le<&mut Self>
where + Self: Unpin,

Reads an unsigned 128-bit integer in little-endian order from the +underlying reader. Read more
§

fn read_i128_le(&mut self) -> ReadI128Le<&mut Self>
where + Self: Unpin,

Reads an signed 128-bit integer in little-endian order from the +underlying reader. Read more
§

fn read_f32_le(&mut self) -> ReadF32Le<&mut Self>
where + Self: Unpin,

Reads an 32-bit floating point type in little-endian order from the +underlying reader. Read more
§

fn read_f64_le(&mut self) -> ReadF64Le<&mut Self>
where + Self: Unpin,

Reads an 64-bit floating point type in little-endian order from the +underlying reader. Read more
§

fn read_to_end<'a>(&'a mut self, buf: &'a mut Vec<u8>) -> ReadToEnd<'a, Self>
where + Self: Unpin,

Reads all bytes until EOF in this source, placing them into buf. Read more
§

fn read_to_string<'a>( + &'a mut self, + dst: &'a mut String, +) -> ReadToString<'a, Self>
where + Self: Unpin,

Reads all bytes until EOF in this source, appending them to buf. Read more
§

fn take(self, limit: u64) -> Take<Self>
where + Self: Sized,

Creates an adaptor which reads at most limit bytes from it. Read more
§

impl<R> AsyncReadExt for R
where + R: AsyncRead + ?Sized,

§

fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadFuture<'a, Self>
where + Self: Unpin,

Reads some bytes from the byte stream. Read more
§

fn read_vectored<'a>( + &'a mut self, + bufs: &'a mut [IoSliceMut<'a>], +) -> ReadVectoredFuture<'a, Self>
where + Self: Unpin,

Like [read()][AsyncReadExt::read()], except it reads into a slice of buffers. Read more
§

fn read_to_end<'a>( + &'a mut self, + buf: &'a mut Vec<u8>, +) -> ReadToEndFuture<'a, Self>
where + Self: Unpin,

Reads the entire contents and appends them to a Vec. Read more
§

fn read_to_string<'a>( + &'a mut self, + buf: &'a mut String, +) -> ReadToStringFuture<'a, Self>
where + Self: Unpin,

Reads the entire contents and appends them to a String. Read more
§

fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExactFuture<'a, Self>
where + Self: Unpin,

Reads the exact number of bytes required to fill buf. Read more
§

fn take(self, limit: u64) -> Take<Self>
where + Self: Sized,

Creates an adapter which will read at most limit bytes from it. Read more
§

fn bytes(self) -> Bytes<Self>
where + Self: Sized,

Converts this [AsyncRead] into a [Stream] of bytes. Read more
§

fn chain<R>(self, next: R) -> Chain<Self, R>
where + R: AsyncRead, + Self: Sized,

Creates an adapter which will chain this stream with another. Read more
§

fn boxed_reader<'a>(self) -> Pin<Box<dyn AsyncRead + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the reader and changes its type to dyn AsyncRead + Send + 'a. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.RetryError.html b/pr/509/docs/noq/struct.RetryError.html new file mode 100644 index 000000000..6177cd666 --- /dev/null +++ b/pr/509/docs/noq/struct.RetryError.html @@ -0,0 +1,27 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.SendDatagram.html b/pr/509/docs/noq/struct.SendDatagram.html new file mode 100644 index 000000000..b37c2ffa0 --- /dev/null +++ b/pr/509/docs/noq/struct.SendDatagram.html @@ -0,0 +1,54 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.SendStream.html b/pr/509/docs/noq/struct.SendStream.html new file mode 100644 index 000000000..e51d014df --- /dev/null +++ b/pr/509/docs/noq/struct.SendStream.html @@ -0,0 +1,220 @@ +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_chunks( + &mut self, + bufs: &mut [Bytes], +) -> Result<Written, WriteError>

Write a slice of [Bytes] into this stream, returning how much was written

+

Bytes to try to write are provided to this method as an array of cheaply cloneable chunks. +Unless this method errors, it waits until some amount of those bytes can be written into +this stream, and then writes as much as it can without waiting again. Due to congestion and +flow control, this may be less than the total number of bytes.

+

On success, this method both mutates bufs and yields an informative Written struct +indicating how much was written:

+
    +
  • [Bytes] chunks that were fully written are mutated to be empty.
  • +
  • If a [Bytes] chunk was partially written, it is split to contain +only the suffix of bytes that were not written.
  • +
  • The yielded Written struct indicates how many chunks were fully written as well as +how many bytes were written.
  • +
+
§Cancel safety
+

This method is cancellation safe. If this does not resolve, no bytes were written.

+
Source

pub async fn write_chunk(&mut self, buf: Bytes) -> Result<(), WriteError>

Write a single [Bytes] into this stream in its entirety

+

Bytes to write are provided to this method as an single cheaply cloneable chunk. This +method repeatedly calls write_chunks until all bytes are written, +or an error occurs.

+
§Cancel safety
+

This method is not cancellation safe. Even if this does not resolve, some bytes may have +been written when previously polled.

+
Source

pub async fn write_all_chunks( + &mut self, + bufs: &mut [Bytes], +) -> Result<(), WriteError>

Write a slice of [Bytes] into this stream in its entirety

+

Bytes to write are provided to this method as an array of cheaply cloneable chunks. This +method repeatedly calls write_chunks until all bytes are written, +or an error occurs. This method mutates bufs by mutating all chunks to be +empty.

+
§Cancel safety
+

This method is not cancellation safe. Even if this does not resolve, some bytes may have +been written when previously polled.

+
Source

pub fn finish(&mut self) -> Result<(), ClosedStream>

Notify the peer that no more data will ever be written to this stream

+

It is an error to write to a SendStream after finish()ing it. reset() +may still be called after finish to abandon transmission of any stream data that might +still be buffered.

+

To wait for the peer to receive all buffered stream data, see stopped().

+

May fail if finish() or reset() was previously +called. This error is harmless and serves only to indicate that the caller may have +incorrect assumptions about the stream’s state.

+
Source

pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream>

Close the send stream immediately.

+

No new data can be written after calling this method. Locally buffered data is dropped, and +previously transmitted data will no longer be retransmitted if lost. If an attempt has +already been made to finish the stream, the peer may still receive all written data.

+

May fail if finish() or reset() was previously +called. This error is harmless and serves only to indicate that the caller may have +incorrect assumptions about the stream’s state.

+
Source

pub fn set_priority(&self, priority: i32) -> Result<(), ClosedStream>

Set the priority of the send stream

+

Every send stream has an initial priority of 0. Locally buffered data from streams with +higher priority will be transmitted before data from streams with lower priority. Changing +the priority of a stream with pending data may only take effect after that data has been +transmitted. Using many different priority levels per connection may have a negative +impact on performance.

+
Source

pub fn priority(&self) -> Result<i32, ClosedStream>

Get the priority of the send stream

+
Source

pub fn stopped(&self) -> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.ServerConfig.html b/pr/509/docs/noq/struct.ServerConfig.html new file mode 100644 index 000000000..fe219c25e --- /dev/null +++ b/pr/509/docs/noq/struct.ServerConfig.html @@ -0,0 +1,114 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.SmolRuntime.html b/pr/509/docs/noq/struct.SmolRuntime.html new file mode 100644 index 000000000..8e96b38ac --- /dev/null +++ b/pr/509/docs/noq/struct.SmolRuntime.html @@ -0,0 +1,25 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.StdSystemTime.html b/pr/509/docs/noq/struct.StdSystemTime.html new file mode 100644 index 000000000..6e1294bd6 --- /dev/null +++ b/pr/509/docs/noq/struct.StdSystemTime.html @@ -0,0 +1,26 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.Stopped.html b/pr/509/docs/noq/struct.Stopped.html new file mode 100644 index 000000000..2ed0ed96d --- /dev/null +++ b/pr/509/docs/noq/struct.Stopped.html @@ -0,0 +1,54 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.StreamId.html b/pr/509/docs/noq/struct.StreamId.html new file mode 100644 index 000000000..f7789344a --- /dev/null +++ b/pr/509/docs/noq/struct.StreamId.html @@ -0,0 +1,55 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.TokenMemoryCache.html b/pr/509/docs/noq/struct.TokenMemoryCache.html new file mode 100644 index 000000000..dbcf2abe4 --- /dev/null +++ b/pr/509/docs/noq/struct.TokenMemoryCache.html @@ -0,0 +1,31 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.TokenReuseError.html b/pr/509/docs/noq/struct.TokenReuseError.html new file mode 100644 index 000000000..74a06a1d6 --- /dev/null +++ b/pr/509/docs/noq/struct.TokenReuseError.html @@ -0,0 +1,25 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.TokioRuntime.html b/pr/509/docs/noq/struct.TokioRuntime.html new file mode 100644 index 000000000..5c3d5c6ca --- /dev/null +++ b/pr/509/docs/noq/struct.TokioRuntime.html @@ -0,0 +1,25 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.TransportConfig.html b/pr/509/docs/noq/struct.TransportConfig.html new file mode 100644 index 000000000..f47c309f7 --- /dev/null +++ b/pr/509/docs/noq/struct.TransportConfig.html @@ -0,0 +1,252 @@ +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 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 set_max_remote_nat_traversal_addresses( + &mut self, + max_addresses: u8, +) -> &mut TransportConfig

Sets the maximum number of nat traversal addresses this endpoint allows the remote to +advertise

+

Setting this to any nonzero value will enable 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, a default value of +[DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED] will be used.

+
Source

pub fn qlog_factory( + &mut self, + factory: Arc<dyn QlogFactory>, +) -> &mut TransportConfig

Configures qlog capturing by setting a QlogFactory.

+

This assigns a QlogFactory that produces qlog capture configurations for +individual connections.

+
Source

pub fn qlog_from_env(&mut self, prefix: &str) -> &mut TransportConfig

Configures qlog capturing through the QLOGDIR environment variable.

+

This uses QlogFileFactory::from_env to create a factory to write qlog traces +into the directory set through the QLOGDIR environment variable.

+

If QLOGDIR is not set, no traces will be written. If QLOGDIR is set to a path +that does not exist, it will be created.

+

The files will be prefixed with prefix.

+
Source

pub fn qlog_from_path( + &mut self, + path: impl AsRef<Path>, + prefix: &str, +) -> &mut TransportConfig

Configures qlog capturing into a directory.

+

This uses QlogFileFactory to create a factory to write qlog traces into +the specified directory. The files will be prefixed with prefix.

+

Trait Implementations§

Source§

impl Clone for TransportConfig

Source§

fn clone(&self) -> TransportConfig

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for TransportConfig

Source§

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

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

impl Default for TransportConfig

Source§

fn default() -> TransportConfig

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.TransportErrorCode.html b/pr/509/docs/noq/struct.TransportErrorCode.html new file mode 100644 index 000000000..1ce88917a --- /dev/null +++ b/pr/509/docs/noq/struct.TransportErrorCode.html @@ -0,0 +1,57 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.UdpStats.html b/pr/509/docs/noq/struct.UdpStats.html new file mode 100644 index 000000000..af791cc52 --- /dev/null +++ b/pr/509/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 amount of UDP datagrams observed

+
§bytes: u64

The total amount of bytes which have been transferred inside UDP datagrams

+
§ios: u64

The amount of I/O operations executed

+

Can be less than datagrams when GSO, GRO, and/or batched system calls are in use.

+

Trait Implementations§

Source§

impl Clone for UdpStats

Source§

fn clone(&self) -> UdpStats

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for UdpStats

Source§

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

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

impl Default for UdpStats

Source§

fn default() -> UdpStats

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

impl PartialEq for UdpStats

Source§

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

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

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

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

impl Copy for UdpStats

Source§

impl Eq for UdpStats

Source§

impl StructuralPartialEq for UdpStats

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

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

§

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

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

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

§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

impl<T> IntoEither for T

Source§

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

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

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

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

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.UnorderedRecvStream.html b/pr/509/docs/noq/struct.UnorderedRecvStream.html new file mode 100644 index 000000000..cf15965ad --- /dev/null +++ b/pr/509/docs/noq/struct.UnorderedRecvStream.html @@ -0,0 +1,54 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.ValidationTokenConfig.html b/pr/509/docs/noq/struct.ValidationTokenConfig.html new file mode 100644 index 000000000..8494d616b --- /dev/null +++ b/pr/509/docs/noq/struct.ValidationTokenConfig.html @@ -0,0 +1,74 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.VarInt.html b/pr/509/docs/noq/struct.VarInt.html new file mode 100644 index 000000000..b7dbbdbdb --- /dev/null +++ b/pr/509/docs/noq/struct.VarInt.html @@ -0,0 +1,65 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.VarIntBoundsExceeded.html b/pr/509/docs/noq/struct.VarIntBoundsExceeded.html new file mode 100644 index 000000000..53242ac86 --- /dev/null +++ b/pr/509/docs/noq/struct.VarIntBoundsExceeded.html @@ -0,0 +1,33 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.WeakConnectionHandle.html b/pr/509/docs/noq/struct.WeakConnectionHandle.html new file mode 100644 index 000000000..534196ef0 --- /dev/null +++ b/pr/509/docs/noq/struct.WeakConnectionHandle.html @@ -0,0 +1,32 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.WeakPathHandle.html b/pr/509/docs/noq/struct.WeakPathHandle.html new file mode 100644 index 000000000..f2c56b182 --- /dev/null +++ b/pr/509/docs/noq/struct.WeakPathHandle.html @@ -0,0 +1,38 @@ +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) -> Self

Returns a duplicate of the value. 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.Written.html b/pr/509/docs/noq/struct.Written.html new file mode 100644 index 000000000..e771c2f57 --- /dev/null +++ b/pr/509/docs/noq/struct.Written.html @@ -0,0 +1,38 @@ +Written in noq - Rust

Written

Struct Written 

Source
pub struct Written {
+    pub bytes: usize,
+    pub chunks: usize,
+}
Expand description

Indicates how many bytes and chunks had been transferred in a write operation

+

Fields§

§bytes: usize

The amount of bytes which had been written

+
§chunks: usize

The amount of full chunks which had been written

+

If a chunk was only partially written, it will not be counted by this field.

+

Trait Implementations§

Source§

impl Clone for Written

Source§

fn clone(&self) -> Written

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Written

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for Written

Source§

fn default() -> Written

Returns the “default value” for a type. Read more
Source§

impl PartialEq for Written

Source§

fn eq(&self, other: &Written) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for Written

Source§

impl Eq for Written

Source§

impl StructuralPartialEq for Written

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/struct.ZeroRttAccepted.html b/pr/509/docs/noq/struct.ZeroRttAccepted.html new file mode 100644 index 000000000..5e26e04ce --- /dev/null +++ b/pr/509/docs/noq/struct.ZeroRttAccepted.html @@ -0,0 +1,51 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq/trait.AsyncTimer.html b/pr/509/docs/noq/trait.AsyncTimer.html new file mode 100644 index 000000000..18ac49179 --- /dev/null +++ b/pr/509/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/509/docs/noq/trait.AsyncUdpSocket.html b/pr/509/docs/noq/trait.AsyncUdpSocket.html new file mode 100644 index 000000000..0e161a88a --- /dev/null +++ b/pr/509/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/509/docs/noq/trait.ConnectionIdGenerator.html b/pr/509/docs/noq/trait.ConnectionIdGenerator.html new file mode 100644 index 000000000..d6b3b1892 --- /dev/null +++ b/pr/509/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/509/docs/noq/trait.NetworkChangeHint.html b/pr/509/docs/noq/trait.NetworkChangeHint.html new file mode 100644 index 000000000..be757fb0e --- /dev/null +++ b/pr/509/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/509/docs/noq/trait.QlogFactory.html b/pr/509/docs/noq/trait.QlogFactory.html new file mode 100644 index 000000000..ac57dec70 --- /dev/null +++ b/pr/509/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/509/docs/noq/trait.Runtime.html b/pr/509/docs/noq/trait.Runtime.html new file mode 100644 index 000000000..6452974d5 --- /dev/null +++ b/pr/509/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/509/docs/noq/trait.TimeSource.html b/pr/509/docs/noq/trait.TimeSource.html new file mode 100644 index 000000000..2869c06bb --- /dev/null +++ b/pr/509/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/509/docs/noq/trait.TokenLog.html b/pr/509/docs/noq/trait.TokenLog.html new file mode 100644 index 000000000..60db43f8c --- /dev/null +++ b/pr/509/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/509/docs/noq/trait.TokenStore.html b/pr/509/docs/noq/trait.TokenStore.html new file mode 100644 index 000000000..aff114b1b --- /dev/null +++ b/pr/509/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/509/docs/noq/trait.UdpSender.html b/pr/509/docs/noq/trait.UdpSender.html new file mode 100644 index 000000000..21a10c856 --- /dev/null +++ b/pr/509/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/509/docs/noq_perf/all.html b/pr/509/docs/noq_perf/all.html new file mode 100644 index 000000000..3d9934367 --- /dev/null +++ b/pr/509/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/509/docs/noq_perf/enum.Commands.html b/pr/509/docs/noq_perf/enum.Commands.html new file mode 100644 index 000000000..0f193a771 --- /dev/null +++ b/pr/509/docs/noq_perf/enum.Commands.html @@ -0,0 +1,40 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_perf/fn.main.html b/pr/509/docs/noq_perf/fn.main.html new file mode 100644 index 000000000..8c4d9baeb --- /dev/null +++ b/pr/509/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/509/docs/noq_perf/index.html b/pr/509/docs/noq_perf/index.html new file mode 100644 index 000000000..93b6e0650 --- /dev/null +++ b/pr/509/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/509/docs/noq_perf/sidebar-items.js b/pr/509/docs/noq_perf/sidebar-items.js new file mode 100644 index 000000000..9a5c272f9 --- /dev/null +++ b/pr/509/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/509/docs/noq_perf/struct.Cli.html b/pr/509/docs/noq_perf/struct.Cli.html new file mode 100644 index 000000000..9ef444b73 --- /dev/null +++ b/pr/509/docs/noq_perf/struct.Cli.html @@ -0,0 +1,45 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/all.html b/pr/509/docs/noq_proto/all.html new file mode 100644 index 000000000..f6ff6a81e --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/bench_exports/index.html b/pr/509/docs/noq_proto/bench_exports/index.html new file mode 100644 index 000000000..ab3e699ae --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/bench_exports/send_buffer_benches/fn.get_into_many_segments.html b/pr/509/docs/noq_proto/bench_exports/send_buffer_benches/fn.get_into_many_segments.html new file mode 100644 index 000000000..3e097348b --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/bench_exports/send_buffer_benches/fn.get_loop_many_segments.html b/pr/509/docs/noq_proto/bench_exports/send_buffer_benches/fn.get_loop_many_segments.html new file mode 100644 index 000000000..7d944d5cf --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/bench_exports/send_buffer_benches/index.html b/pr/509/docs/noq_proto/bench_exports/send_buffer_benches/index.html new file mode 100644 index 000000000..11caeae58 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/bench_exports/send_buffer_benches/sidebar-items.js b/pr/509/docs/noq_proto/bench_exports/send_buffer_benches/sidebar-items.js new file mode 100644 index 000000000..24edbdcf5 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/bench_exports/sidebar-items.js b/pr/509/docs/noq_proto/bench_exports/sidebar-items.js new file mode 100644 index 000000000..cb9cfb5fa --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/bloom_token_log/struct.BloomTokenLog.html b/pr/509/docs/noq_proto/bloom_token_log/struct.BloomTokenLog.html new file mode 100644 index 000000000..8df1022f9 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/cid_generator/struct.HashedConnectionIdGenerator.html b/pr/509/docs/noq_proto/cid_generator/struct.HashedConnectionIdGenerator.html new file mode 100644 index 000000000..f4b4f8626 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/cid_generator/struct.InvalidCid.html b/pr/509/docs/noq_proto/cid_generator/struct.InvalidCid.html new file mode 100644 index 000000000..eefbbc3b5 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/cid_generator/struct.RandomConnectionIdGenerator.html b/pr/509/docs/noq_proto/cid_generator/struct.RandomConnectionIdGenerator.html new file mode 100644 index 000000000..44b8435b1 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/cid_generator/trait.ConnectionIdGenerator.html b/pr/509/docs/noq_proto/cid_generator/trait.ConnectionIdGenerator.html new file mode 100644 index 000000000..4fb6ecf98 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/coding/index.html b/pr/509/docs/noq_proto/coding/index.html new file mode 100644 index 000000000..45eb32cde --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/coding/sidebar-items.js b/pr/509/docs/noq_proto/coding/sidebar-items.js new file mode 100644 index 000000000..e6d58e381 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/coding/struct.UnexpectedEnd.html b/pr/509/docs/noq_proto/coding/struct.UnexpectedEnd.html new file mode 100644 index 000000000..cd57dc21d --- /dev/null +++ b/pr/509/docs/noq_proto/coding/struct.UnexpectedEnd.html @@ -0,0 +1,33 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/coding/trait.Decodable.html b/pr/509/docs/noq_proto/coding/trait.Decodable.html new file mode 100644 index 000000000..a91146d9c --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/coding/trait.Encodable.html b/pr/509/docs/noq_proto/coding/trait.Encodable.html new file mode 100644 index 000000000..eb5f65510 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/coding/type.Result.html b/pr/509/docs/noq_proto/coding/type.Result.html new file mode 100644 index 000000000..8c628b2be --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/config/enum.ConfigError.html b/pr/509/docs/noq_proto/config/enum.ConfigError.html new file mode 100644 index 000000000..924393a0f --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/config/qlog/struct.QlogConfig.html b/pr/509/docs/noq_proto/config/qlog/struct.QlogConfig.html new file mode 100644 index 000000000..309ba43ab --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/config/qlog/struct.QlogFileFactory.html b/pr/509/docs/noq_proto/config/qlog/struct.QlogFileFactory.html new file mode 100644 index 000000000..b41c84750 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/config/qlog/trait.QlogFactory.html b/pr/509/docs/noq_proto/config/qlog/trait.QlogFactory.html new file mode 100644 index 000000000..2ce472d56 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/config/struct.ClientConfig.html b/pr/509/docs/noq_proto/config/struct.ClientConfig.html new file mode 100644 index 000000000..0eaf6aef3 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/config/struct.EndpointConfig.html b/pr/509/docs/noq_proto/config/struct.EndpointConfig.html new file mode 100644 index 000000000..e6dc880ba --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/config/struct.ServerConfig.html b/pr/509/docs/noq_proto/config/struct.ServerConfig.html new file mode 100644 index 000000000..8c4dd7c4d --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/config/struct.StdSystemTime.html b/pr/509/docs/noq_proto/config/struct.StdSystemTime.html new file mode 100644 index 000000000..3977e551f --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/config/struct.ValidationTokenConfig.html b/pr/509/docs/noq_proto/config/struct.ValidationTokenConfig.html new file mode 100644 index 000000000..0fc5aa26a --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/config/trait.TimeSource.html b/pr/509/docs/noq_proto/config/trait.TimeSource.html new file mode 100644 index 000000000..316bd2e7f --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/config/transport/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html b/pr/509/docs/noq_proto/config/transport/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html new file mode 100644 index 000000000..50d5e1b71 --- /dev/null +++ b/pr/509/docs/noq_proto/config/transport/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html...

+ + + \ No newline at end of file diff --git a/pr/509/docs/noq_proto/config/transport/struct.AckFrequencyConfig.html b/pr/509/docs/noq_proto/config/transport/struct.AckFrequencyConfig.html new file mode 100644 index 000000000..befe5e73d --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/config/transport/struct.IdleTimeout.html b/pr/509/docs/noq_proto/config/transport/struct.IdleTimeout.html new file mode 100644 index 000000000..9839679ac --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/config/transport/struct.MtuDiscoveryConfig.html b/pr/509/docs/noq_proto/config/transport/struct.MtuDiscoveryConfig.html new file mode 100644 index 000000000..7cfee6b1b --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/config/transport/struct.TransportConfig.html b/pr/509/docs/noq_proto/config/transport/struct.TransportConfig.html new file mode 100644 index 000000000..763547791 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/congestion/bbr/struct.Bbr.html b/pr/509/docs/noq_proto/congestion/bbr/struct.Bbr.html new file mode 100644 index 000000000..64b7a24b3 --- /dev/null +++ b/pr/509/docs/noq_proto/congestion/bbr/struct.Bbr.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/congestion/struct.Bbr.html...

+ + + \ No newline at end of file diff --git a/pr/509/docs/noq_proto/congestion/bbr/struct.BbrConfig.html b/pr/509/docs/noq_proto/congestion/bbr/struct.BbrConfig.html new file mode 100644 index 000000000..a2722d201 --- /dev/null +++ b/pr/509/docs/noq_proto/congestion/bbr/struct.BbrConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../noq_proto/congestion/struct.BbrConfig.html...

+ + + \ No newline at end of file diff --git a/pr/509/docs/noq_proto/congestion/cubic/struct.Cubic.html b/pr/509/docs/noq_proto/congestion/cubic/struct.Cubic.html new file mode 100644 index 000000000..62ea263f2 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/congestion/cubic/struct.CubicConfig.html b/pr/509/docs/noq_proto/congestion/cubic/struct.CubicConfig.html new file mode 100644 index 000000000..42b5afadb --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/congestion/index.html b/pr/509/docs/noq_proto/congestion/index.html new file mode 100644 index 000000000..690781691 --- /dev/null +++ b/pr/509/docs/noq_proto/congestion/index.html @@ -0,0 +1,2 @@ +noq_proto::congestion - Rust

Module congestion

Module congestion 

Source
Expand description

Logic for controlling the rate at which data is sent

+

Structs§

Bbr
Experimental! Use at your own risk.
BbrConfig
Configuration for the Bbr congestion controller
ControllerMetrics
Common congestion controller metrics
Cubic
The RFC8312 congestion controller, as widely used for TCP
CubicConfig
Configuration for the Cubic congestion controller
NewReno
A simple, standard congestion controller
NewRenoConfig
Configuration for the NewReno congestion controller

Traits§

Controller
Common interface for different congestion controllers
ControllerFactory
Constructs controllers on demand
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/congestion/new_reno/struct.NewReno.html b/pr/509/docs/noq_proto/congestion/new_reno/struct.NewReno.html new file mode 100644 index 000000000..6a4145388 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/congestion/new_reno/struct.NewRenoConfig.html b/pr/509/docs/noq_proto/congestion/new_reno/struct.NewRenoConfig.html new file mode 100644 index 000000000..d97995371 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/congestion/sidebar-items.js b/pr/509/docs/noq_proto/congestion/sidebar-items.js new file mode 100644 index 000000000..888f550d4 --- /dev/null +++ b/pr/509/docs/noq_proto/congestion/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["Bbr","BbrConfig","ControllerMetrics","Cubic","CubicConfig","NewReno","NewRenoConfig"],"trait":["Controller","ControllerFactory"]}; \ No newline at end of file diff --git a/pr/509/docs/noq_proto/congestion/struct.Bbr.html b/pr/509/docs/noq_proto/congestion/struct.Bbr.html new file mode 100644 index 000000000..1382373f0 --- /dev/null +++ b/pr/509/docs/noq_proto/congestion/struct.Bbr.html @@ -0,0 +1,52 @@ +Bbr in noq_proto::congestion - Rust

Bbr

Struct Bbr 

Source
pub struct Bbr { /* private fields */ }
Expand description

Experimental! Use at your own risk.

+

Aims for reduced buffer bloat and improved performance over high bandwidth-delay product networks. +Based on google’s quiche implementation https://source.chromium.org/chromium/chromium/src/+/master:net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc +of BBR https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control. +More discussion and links at https://groups.google.com/g/bbr-dev.

+

Implementations§

Source§

impl Bbr

Source

pub fn new(config: Arc<BbrConfig>, current_mtu: u16) -> Self

Construct a state using the given config and current time now

+

Trait Implementations§

Source§

impl Clone for Bbr

Source§

fn clone(&self) -> Bbr

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Controller for Bbr

Source§

fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64)

One or more packets were just sent
Source§

fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + app_limited: bool, + rtt: &RttEstimator, +)

Packet deliveries were confirmed Read more
Source§

fn on_end_acks( + &mut self, + now: Instant, + in_flight: u64, + app_limited: bool, + largest_packet_num_acked: Option<u64>, +)

Packets are acked in batches, all with the same now argument. This indicates one of those batches has completed.
Source§

fn on_congestion_event( + &mut self, + _now: Instant, + _sent: Instant, + _is_persistent_congestion: bool, + _is_ecn: bool, + lost_bytes: u64, +)

Packets were deemed lost or marked congested Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated
Source§

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight
Source§

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled
Source§

fn clone_box(&self) -> Box<dyn Controller>

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

fn into_any(self: Box<Self>) -> Box<dyn Any>

Returns Self for use in down-casting to extract implementation details
Source§

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

impl Debug for Bbr

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Bbr

§

impl RefUnwindSafe for Bbr

§

impl Send for Bbr

§

impl Sync for Bbr

§

impl Unpin for Bbr

§

impl UnwindSafe for Bbr

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/congestion/struct.BbrConfig.html b/pr/509/docs/noq_proto/congestion/struct.BbrConfig.html new file mode 100644 index 000000000..f9ca59f8a --- /dev/null +++ b/pr/509/docs/noq_proto/congestion/struct.BbrConfig.html @@ -0,0 +1,33 @@ +BbrConfig in noq_proto::congestion - Rust

BbrConfig

Struct BbrConfig 

Source
pub struct BbrConfig { /* private fields */ }
Expand description

Configuration for the Bbr congestion controller

+

Implementations§

Source§

impl BbrConfig

Source

pub fn initial_window(&mut self, value: u64) -> &mut Self

Default limit on the amount of outstanding data in bytes.

+

Recommended value: min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))

+

Trait Implementations§

Source§

impl Clone for BbrConfig

Source§

fn clone(&self) -> BbrConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl ControllerFactory for BbrConfig

Source§

fn build( + self: Arc<Self>, + _now: Instant, + current_mtu: u16, +) -> Box<dyn Controller>

Construct a fresh Controller
Source§

impl Debug for BbrConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for BbrConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/congestion/struct.ControllerMetrics.html b/pr/509/docs/noq_proto/congestion/struct.ControllerMetrics.html new file mode 100644 index 000000000..f8fbbdb1d --- /dev/null +++ b/pr/509/docs/noq_proto/congestion/struct.ControllerMetrics.html @@ -0,0 +1,32 @@ +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>, +}
Expand description

Common congestion controller metrics

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§congestion_window: u64

Congestion window (bytes)

+
§ssthresh: Option<u64>

Slow start threshold (bytes)

+
§pacing_rate: Option<u64>

Pacing rate (bits/s)

+

Trait Implementations§

Source§

impl Debug for ControllerMetrics

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ControllerMetrics

Source§

fn default() -> ControllerMetrics

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/congestion/struct.Cubic.html b/pr/509/docs/noq_proto/congestion/struct.Cubic.html new file mode 100644 index 000000000..73ae083a6 --- /dev/null +++ b/pr/509/docs/noq_proto/congestion/struct.Cubic.html @@ -0,0 +1,48 @@ +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, + app_limited: bool, + rtt: &RttEstimator, +)

Packet deliveries were confirmed Read more
Source§

fn on_congestion_event( + &mut self, + now: Instant, + sent: Instant, + is_persistent_congestion: bool, + is_ecn: bool, + _lost_bytes: u64, +)

Packets were deemed lost or marked congested Read more
Source§

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated
Source§

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight
Source§

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled
Source§

fn clone_box(&self) -> Box<dyn Controller>

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

fn into_any(self: Box<Self>) -> Box<dyn Any>

Returns Self for use in down-casting to extract implementation details
Source§

fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64)

One or more packets were just sent
Source§

fn on_end_acks( + &mut self, + now: Instant, + in_flight: u64, + app_limited: bool, + largest_packet_num_acked: Option<u64>, +)

Packets are acked in batches, all with the same now argument. This indicates one of those batches has completed.
Source§

impl Debug for Cubic

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Cubic

§

impl RefUnwindSafe for Cubic

§

impl Send for Cubic

§

impl Sync for Cubic

§

impl Unpin for Cubic

§

impl UnwindSafe for Cubic

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/congestion/struct.CubicConfig.html b/pr/509/docs/noq_proto/congestion/struct.CubicConfig.html new file mode 100644 index 000000000..577d5caa2 --- /dev/null +++ b/pr/509/docs/noq_proto/congestion/struct.CubicConfig.html @@ -0,0 +1,29 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/congestion/struct.NewReno.html b/pr/509/docs/noq_proto/congestion/struct.NewReno.html new file mode 100644 index 000000000..8d0fd172f --- /dev/null +++ b/pr/509/docs/noq_proto/congestion/struct.NewReno.html @@ -0,0 +1,48 @@ +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, + app_limited: bool, + _rtt: &RttEstimator, +)

Packet deliveries were confirmed Read more
Source§

fn on_congestion_event( + &mut self, + now: Instant, + sent: Instant, + is_persistent_congestion: bool, + _is_ecn: bool, + _lost_bytes: u64, +)

Packets were deemed lost or marked congested Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated
Source§

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight
Source§

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled
Source§

fn clone_box(&self) -> Box<dyn Controller>

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

fn into_any(self: Box<Self>) -> Box<dyn Any>

Returns Self for use in down-casting to extract implementation details
Source§

fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64)

One or more packets were just sent
Source§

fn on_end_acks( + &mut self, + now: Instant, + in_flight: u64, + app_limited: bool, + largest_packet_num_acked: Option<u64>, +)

Packets are acked in batches, all with the same now argument. This indicates one of those batches has completed.
Source§

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

impl Debug for NewReno

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/congestion/struct.NewRenoConfig.html b/pr/509/docs/noq_proto/congestion/struct.NewRenoConfig.html new file mode 100644 index 000000000..fd90f87a1 --- /dev/null +++ b/pr/509/docs/noq_proto/congestion/struct.NewRenoConfig.html @@ -0,0 +1,30 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/congestion/trait.Controller.html b/pr/509/docs/noq_proto/congestion/trait.Controller.html new file mode 100644 index 000000000..689446fd9 --- /dev/null +++ b/pr/509/docs/noq_proto/congestion/trait.Controller.html @@ -0,0 +1,79 @@ +Controller in noq_proto::congestion - Rust

Controller

Trait Controller 

Source
pub trait Controller:
+    Send
+    + Sync
+    + Debug {
+    // Required methods
+    fn on_congestion_event(
+        &mut self,
+        now: Instant,
+        sent: Instant,
+        is_persistent_congestion: bool,
+        is_ecn: bool,
+        lost_bytes: u64,
+    );
+    fn on_mtu_update(&mut self, new_mtu: u16);
+    fn window(&self) -> u64;
+    fn clone_box(&self) -> Box<dyn Controller>;
+    fn initial_window(&self) -> u64;
+    fn into_any(self: Box<Self>) -> Box<dyn Any>;
+
+    // Provided methods
+    fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64) { ... }
+    fn on_ack(
+        &mut self,
+        now: Instant,
+        sent: Instant,
+        bytes: u64,
+        app_limited: bool,
+        rtt: &RttEstimator,
+    ) { ... }
+    fn on_end_acks(
+        &mut self,
+        now: Instant,
+        in_flight: u64,
+        app_limited: bool,
+        largest_packet_num_acked: Option<u64>,
+    ) { ... }
+    fn on_spurious_congestion_event(&mut self) { ... }
+    fn metrics(&self) -> ControllerMetrics { ... }
+}
Expand description

Common interface for different congestion controllers

+

Required Methods§

Source

fn on_congestion_event( + &mut self, + now: Instant, + sent: Instant, + is_persistent_congestion: bool, + is_ecn: bool, + lost_bytes: u64, +)

Packets were deemed lost or marked congested

+

in_persistent_congestion indicates whether all packets sent within the persistent +congestion threshold period ending when the most recent packet in this batch was sent were +lost. +lost_bytes indicates how many bytes were lost. This value will be 0 for ECN triggers.

+
Source

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated

+
Source

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight

+
Source

fn clone_box(&self) -> Box<dyn Controller>

Duplicate the controller’s state

+
Source

fn initial_window(&self) -> u64

Initial congestion window

+
Source

fn into_any(self: Box<Self>) -> Box<dyn Any>

Returns Self for use in down-casting to extract implementation details

+

Provided Methods§

Source

fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64)

One or more packets were just sent

+
Source

fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + app_limited: bool, + rtt: &RttEstimator, +)

Packet deliveries were confirmed

+

app_limited indicates whether the connection was blocked on outgoing +application data prior to receiving these acknowledgements.

+
Source

fn on_end_acks( + &mut self, + now: Instant, + in_flight: u64, + app_limited: bool, + largest_packet_num_acked: Option<u64>, +)

Packets are acked in batches, all with the same now argument. This indicates one of those batches has completed.

+
Source

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost

+

This function is called when all packets that were deemed lost (for instance because +of packet reordering) are acknowledged after the congestion event was raised.

+
Source

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled

+

Implementors§

\ No newline at end of file diff --git a/pr/509/docs/noq_proto/congestion/trait.ControllerFactory.html b/pr/509/docs/noq_proto/congestion/trait.ControllerFactory.html new file mode 100644 index 000000000..34bdb0be8 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/assembler/struct.Chunk.html b/pr/509/docs/noq_proto/connection/assembler/struct.Chunk.html new file mode 100644 index 000000000..898446f8d --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/datagrams/enum.SendDatagramError.html b/pr/509/docs/noq_proto/connection/datagrams/enum.SendDatagramError.html new file mode 100644 index 000000000..570c234b4 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/datagrams/struct.Datagrams.html b/pr/509/docs/noq_proto/connection/datagrams/struct.Datagrams.html new file mode 100644 index 000000000..3045cc171 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/enum.ClosePathError.html b/pr/509/docs/noq_proto/connection/enum.ClosePathError.html new file mode 100644 index 000000000..32d2e4223 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/enum.ConnectionError.html b/pr/509/docs/noq_proto/connection/enum.ConnectionError.html new file mode 100644 index 000000000..3f1a9fbfb --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/enum.Event.html b/pr/509/docs/noq_proto/connection/enum.Event.html new file mode 100644 index 000000000..77cd05a9b --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/enum.PathError.html b/pr/509/docs/noq_proto/connection/enum.PathError.html new file mode 100644 index 000000000..95276db00 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/paths/enum.PathAbandonReason.html b/pr/509/docs/noq_proto/connection/paths/enum.PathAbandonReason.html new file mode 100644 index 000000000..8aa633327 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/paths/enum.PathEvent.html b/pr/509/docs/noq_proto/connection/paths/enum.PathEvent.html new file mode 100644 index 000000000..38b2b2c8b --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/paths/enum.PathStatus.html b/pr/509/docs/noq_proto/connection/paths/enum.PathStatus.html new file mode 100644 index 000000000..de11e934a --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/paths/enum.SetPathStatusError.html b/pr/509/docs/noq_proto/connection/paths/enum.SetPathStatusError.html new file mode 100644 index 000000000..5390959b2 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/paths/struct.ClosedPath.html b/pr/509/docs/noq_proto/connection/paths/struct.ClosedPath.html new file mode 100644 index 000000000..f5d8d15d2 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/paths/struct.PathId.html b/pr/509/docs/noq_proto/connection/paths/struct.PathId.html new file mode 100644 index 000000000..ba917e8fb --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/paths/struct.RttEstimator.html b/pr/509/docs/noq_proto/connection/paths/struct.RttEstimator.html new file mode 100644 index 000000000..781ef4877 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/send_buffer/send_buffer_benches/fn.get_into_many_segments.html b/pr/509/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/509/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/509/docs/noq_proto/connection/send_buffer/send_buffer_benches/fn.get_loop_many_segments.html b/pr/509/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/509/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/509/docs/noq_proto/connection/send_buffer/send_buffer_benches/index.html b/pr/509/docs/noq_proto/connection/send_buffer/send_buffer_benches/index.html new file mode 100644 index 000000000..e31ae92ab --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/stats/struct.ConnectionStats.html b/pr/509/docs/noq_proto/connection/stats/struct.ConnectionStats.html new file mode 100644 index 000000000..595a1ff81 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/stats/struct.FrameStats.html b/pr/509/docs/noq_proto/connection/stats/struct.FrameStats.html new file mode 100644 index 000000000..9474dc3bc --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/stats/struct.PathStats.html b/pr/509/docs/noq_proto/connection/stats/struct.PathStats.html new file mode 100644 index 000000000..4739ace0a --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/stats/struct.UdpStats.html b/pr/509/docs/noq_proto/connection/stats/struct.UdpStats.html new file mode 100644 index 000000000..c1f56ed4c --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/streams/enum.StreamEvent.html b/pr/509/docs/noq_proto/connection/streams/enum.StreamEvent.html new file mode 100644 index 000000000..09af0d7ec --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/streams/recv/enum.ReadError.html b/pr/509/docs/noq_proto/connection/streams/recv/enum.ReadError.html new file mode 100644 index 000000000..d00ebffa7 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/streams/recv/enum.ReadableError.html b/pr/509/docs/noq_proto/connection/streams/recv/enum.ReadableError.html new file mode 100644 index 000000000..c4752b3f5 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/streams/recv/struct.Chunks.html b/pr/509/docs/noq_proto/connection/streams/recv/struct.Chunks.html new file mode 100644 index 000000000..d1cd9b72f --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/streams/send/enum.FinishError.html b/pr/509/docs/noq_proto/connection/streams/send/enum.FinishError.html new file mode 100644 index 000000000..99e7b1b9d --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/streams/send/enum.WriteError.html b/pr/509/docs/noq_proto/connection/streams/send/enum.WriteError.html new file mode 100644 index 000000000..7ad6dec0c --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/streams/send/struct.Written.html b/pr/509/docs/noq_proto/connection/streams/send/struct.Written.html new file mode 100644 index 000000000..8e7619f45 --- /dev/null +++ b/pr/509/docs/noq_proto/connection/streams/send/struct.Written.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../noq_proto/struct.Written.html...

+ + + \ No newline at end of file diff --git a/pr/509/docs/noq_proto/connection/streams/struct.ClosedStream.html b/pr/509/docs/noq_proto/connection/streams/struct.ClosedStream.html new file mode 100644 index 000000000..3b52993d9 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/streams/struct.RecvStream.html b/pr/509/docs/noq_proto/connection/streams/struct.RecvStream.html new file mode 100644 index 000000000..dbc5e85a0 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/streams/struct.SendStream.html b/pr/509/docs/noq_proto/connection/streams/struct.SendStream.html new file mode 100644 index 000000000..b223bbbe1 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/streams/struct.ShouldTransmit.html b/pr/509/docs/noq_proto/connection/streams/struct.ShouldTransmit.html new file mode 100644 index 000000000..9c9d31ae9 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/streams/struct.Streams.html b/pr/509/docs/noq_proto/connection/streams/struct.Streams.html new file mode 100644 index 000000000..e1cb86450 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/struct.Connection.html b/pr/509/docs/noq_proto/connection/struct.Connection.html new file mode 100644 index 000000000..6c77e5c29 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/struct.MultipathNotNegotiated.html b/pr/509/docs/noq_proto/connection/struct.MultipathNotNegotiated.html new file mode 100644 index 000000000..64d88689a --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/connection/trait.NetworkChangeHint.html b/pr/509/docs/noq_proto/connection/trait.NetworkChangeHint.html new file mode 100644 index 000000000..55ed376f5 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html b/pr/509/docs/noq_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html new file mode 100644 index 000000000..a1be1f1b1 --- /dev/null +++ b/pr/509/docs/noq_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html @@ -0,0 +1,3 @@ +DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED in noq_proto - Rust

DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED

Constant DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED 

Source
pub const DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED: NonZeroU32;
Expand description

When multipath is required and has not been explicitly enabled, this value will be used for

+

TransportConfig::max_concurrent_multipath_paths.

+
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html b/pr/509/docs/noq_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html new file mode 100644 index 000000000..cc71da09d --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/crypto/index.html b/pr/509/docs/noq_proto/crypto/index.html new file mode 100644 index 000000000..76a182cbf --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/crypto/rustls/enum.Error.html b/pr/509/docs/noq_proto/crypto/rustls/enum.Error.html new file mode 100644 index 000000000..d23ecd59e --- /dev/null +++ b/pr/509/docs/noq_proto/crypto/rustls/enum.Error.html @@ -0,0 +1,101 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/crypto/rustls/index.html b/pr/509/docs/noq_proto/crypto/rustls/index.html new file mode 100644 index 000000000..d655ca7fe --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/crypto/rustls/sidebar-items.js b/pr/509/docs/noq_proto/crypto/rustls/sidebar-items.js new file mode 100644 index 000000000..65f98d4d4 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/crypto/rustls/struct.HandshakeData.html b/pr/509/docs/noq_proto/crypto/rustls/struct.HandshakeData.html new file mode 100644 index 000000000..9365951ef --- /dev/null +++ b/pr/509/docs/noq_proto/crypto/rustls/struct.HandshakeData.html @@ -0,0 +1,34 @@ +HandshakeData in noq_proto::crypto::rustls - Rust

HandshakeData

Struct HandshakeData 

Source
pub struct HandshakeData {
+    pub protocol: Option<Vec<u8>>,
+    pub server_name: Option<String>,
+    pub negotiated_key_exchange_group: NamedGroup,
+}
Expand description

Authentication data for (rustls) TLS session

+

Fields§

§protocol: Option<Vec<u8>>

The negotiated application protocol, if ALPN is in use

+

Guaranteed to be set if a nonempty list of protocols was specified for this connection.

+
§server_name: Option<String>

The server name specified by the client, if any

+

Always None for outgoing connections

+
§negotiated_key_exchange_group: NamedGroup

The key exchange group negotiated with the peer

+

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/crypto/rustls/struct.NoInitialCipherSuite.html b/pr/509/docs/noq_proto/crypto/rustls/struct.NoInitialCipherSuite.html new file mode 100644 index 000000000..690b568e2 --- /dev/null +++ b/pr/509/docs/noq_proto/crypto/rustls/struct.NoInitialCipherSuite.html @@ -0,0 +1,31 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/crypto/rustls/struct.QuicClientConfig.html b/pr/509/docs/noq_proto/crypto/rustls/struct.QuicClientConfig.html new file mode 100644 index 000000000..e052ad79b --- /dev/null +++ b/pr/509/docs/noq_proto/crypto/rustls/struct.QuicClientConfig.html @@ -0,0 +1,51 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/crypto/rustls/struct.QuicServerConfig.html b/pr/509/docs/noq_proto/crypto/rustls/struct.QuicServerConfig.html new file mode 100644 index 000000000..51ceabef5 --- /dev/null +++ b/pr/509/docs/noq_proto/crypto/rustls/struct.QuicServerConfig.html @@ -0,0 +1,55 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/crypto/rustls/struct.TlsSession.html b/pr/509/docs/noq_proto/crypto/rustls/struct.TlsSession.html new file mode 100644 index 000000000..472da1856 --- /dev/null +++ b/pr/509/docs/noq_proto/crypto/rustls/struct.TlsSession.html @@ -0,0 +1,40 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/crypto/sidebar-items.js b/pr/509/docs/noq_proto/crypto/sidebar-items.js new file mode 100644 index 000000000..710b27671 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/crypto/struct.CryptoError.html b/pr/509/docs/noq_proto/crypto/struct.CryptoError.html new file mode 100644 index 000000000..3a891562b --- /dev/null +++ b/pr/509/docs/noq_proto/crypto/struct.CryptoError.html @@ -0,0 +1,25 @@ +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
Source§

impl From<Unspecified> for CryptoError

Source§

fn from(_: Unspecified) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/crypto/struct.ExportKeyingMaterialError.html b/pr/509/docs/noq_proto/crypto/struct.ExportKeyingMaterialError.html new file mode 100644 index 000000000..330cf92e3 --- /dev/null +++ b/pr/509/docs/noq_proto/crypto/struct.ExportKeyingMaterialError.html @@ -0,0 +1,31 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/crypto/struct.KeyPair.html b/pr/509/docs/noq_proto/crypto/struct.KeyPair.html new file mode 100644 index 000000000..5c1d76c51 --- /dev/null +++ b/pr/509/docs/noq_proto/crypto/struct.KeyPair.html @@ -0,0 +1,36 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/crypto/struct.Keys.html b/pr/509/docs/noq_proto/crypto/struct.Keys.html new file mode 100644 index 000000000..031f9dd17 --- /dev/null +++ b/pr/509/docs/noq_proto/crypto/struct.Keys.html @@ -0,0 +1,30 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/crypto/struct.UnsupportedVersion.html b/pr/509/docs/noq_proto/crypto/struct.UnsupportedVersion.html new file mode 100644 index 000000000..a1f6d8979 --- /dev/null +++ b/pr/509/docs/noq_proto/crypto/struct.UnsupportedVersion.html @@ -0,0 +1,25 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/crypto/trait.ClientConfig.html b/pr/509/docs/noq_proto/crypto/trait.ClientConfig.html new file mode 100644 index 000000000..9a0f417b2 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/crypto/trait.HandshakeTokenKey.html b/pr/509/docs/noq_proto/crypto/trait.HandshakeTokenKey.html new file mode 100644 index 000000000..d5c6e9370 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/crypto/trait.HeaderKey.html b/pr/509/docs/noq_proto/crypto/trait.HeaderKey.html new file mode 100644 index 000000000..ccdd53c55 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/crypto/trait.HmacKey.html b/pr/509/docs/noq_proto/crypto/trait.HmacKey.html new file mode 100644 index 000000000..058355ee8 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/crypto/trait.PacketKey.html b/pr/509/docs/noq_proto/crypto/trait.PacketKey.html new file mode 100644 index 000000000..298552117 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/crypto/trait.ServerConfig.html b/pr/509/docs/noq_proto/crypto/trait.ServerConfig.html new file mode 100644 index 000000000..0643156e9 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/crypto/trait.Session.html b/pr/509/docs/noq_proto/crypto/trait.Session.html new file mode 100644 index 000000000..81418100f --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/endpoint/enum.ConnectError.html b/pr/509/docs/noq_proto/endpoint/enum.ConnectError.html new file mode 100644 index 000000000..8ef61a0a8 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/endpoint/enum.DatagramEvent.html b/pr/509/docs/noq_proto/endpoint/enum.DatagramEvent.html new file mode 100644 index 000000000..61330e877 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/endpoint/struct.AcceptError.html b/pr/509/docs/noq_proto/endpoint/struct.AcceptError.html new file mode 100644 index 000000000..6356d3a2b --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/endpoint/struct.ConnectionHandle.html b/pr/509/docs/noq_proto/endpoint/struct.ConnectionHandle.html new file mode 100644 index 000000000..3a4fb506e --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/endpoint/struct.DecryptedInitial.html b/pr/509/docs/noq_proto/endpoint/struct.DecryptedInitial.html new file mode 100644 index 000000000..5f603db81 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/endpoint/struct.Endpoint.html b/pr/509/docs/noq_proto/endpoint/struct.Endpoint.html new file mode 100644 index 000000000..2a38c917a --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/endpoint/struct.Incoming.html b/pr/509/docs/noq_proto/endpoint/struct.Incoming.html new file mode 100644 index 000000000..50681ded7 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/endpoint/struct.IncomingAlpns.html b/pr/509/docs/noq_proto/endpoint/struct.IncomingAlpns.html new file mode 100644 index 000000000..552057caf --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/endpoint/struct.RetryError.html b/pr/509/docs/noq_proto/endpoint/struct.RetryError.html new file mode 100644 index 000000000..1f8680683 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/enum.ClosePathError.html b/pr/509/docs/noq_proto/enum.ClosePathError.html new file mode 100644 index 000000000..cc224f2cb --- /dev/null +++ b/pr/509/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

This is the last path, which can not be abandoned

+

Trait Implementations§

Source§

impl Clone for ClosePathError

Source§

fn clone(&self) -> ClosePathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosePathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ClosePathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ClosePathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosePathError

Source§

fn eq(&self, other: &ClosePathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ClosePathError

Source§

impl StructuralPartialEq for ClosePathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.ConfigError.html b/pr/509/docs/noq_proto/enum.ConfigError.html new file mode 100644 index 000000000..b123e736f --- /dev/null +++ b/pr/509/docs/noq_proto/enum.ConfigError.html @@ -0,0 +1,36 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.ConnectError.html b/pr/509/docs/noq_proto/enum.ConnectError.html new file mode 100644 index 000000000..74f057166 --- /dev/null +++ b/pr/509/docs/noq_proto/enum.ConnectError.html @@ -0,0 +1,51 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.ConnectionError.html b/pr/509/docs/noq_proto/enum.ConnectionError.html new file mode 100644 index 000000000..ec35ca639 --- /dev/null +++ b/pr/509/docs/noq_proto/enum.ConnectionError.html @@ -0,0 +1,54 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.DatagramEvent.html b/pr/509/docs/noq_proto/enum.DatagramEvent.html new file mode 100644 index 000000000..23227550a --- /dev/null +++ b/pr/509/docs/noq_proto/enum.DatagramEvent.html @@ -0,0 +1,32 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.Dir.html b/pr/509/docs/noq_proto/enum.Dir.html new file mode 100644 index 000000000..c86ae9ec1 --- /dev/null +++ b/pr/509/docs/noq_proto/enum.Dir.html @@ -0,0 +1,53 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.EcnCodepoint.html b/pr/509/docs/noq_proto/enum.EcnCodepoint.html new file mode 100644 index 000000000..a0c498e53 --- /dev/null +++ b/pr/509/docs/noq_proto/enum.EcnCodepoint.html @@ -0,0 +1,41 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.Event.html b/pr/509/docs/noq_proto/enum.Event.html new file mode 100644 index 000000000..a2f752b86 --- /dev/null +++ b/pr/509/docs/noq_proto/enum.Event.html @@ -0,0 +1,48 @@ +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 if the peer closes the connection or an error is encountered.

+

Fields

§reason: ConnectionError

Reason that the connection was closed

+
§

Stream(StreamEvent)

Stream events

+
§

DatagramReceived

One or more application datagrams have been received

+
§

DatagramsUnblocked

One or more application datagrams have been sent after blocking

+
§

Path(PathEvent)

(Multi)Path events

+
§

NatTraversal(Event)

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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.FinishError.html b/pr/509/docs/noq_proto/enum.FinishError.html new file mode 100644 index 000000000..935cf4552 --- /dev/null +++ b/pr/509/docs/noq_proto/enum.FinishError.html @@ -0,0 +1,40 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.FrameType.html b/pr/509/docs/noq_proto/enum.FrameType.html new file mode 100644 index 000000000..2b08ee4b7 --- /dev/null +++ b/pr/509/docs/noq_proto/enum.FrameType.html @@ -0,0 +1,77 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.LongType.html b/pr/509/docs/noq_proto/enum.LongType.html new file mode 100644 index 000000000..f8ccc3c3c --- /dev/null +++ b/pr/509/docs/noq_proto/enum.LongType.html @@ -0,0 +1,37 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.MaybeFrame.html b/pr/509/docs/noq_proto/enum.MaybeFrame.html new file mode 100644 index 000000000..4c1133e13 --- /dev/null +++ b/pr/509/docs/noq_proto/enum.MaybeFrame.html @@ -0,0 +1,40 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.PacketDecodeError.html b/pr/509/docs/noq_proto/enum.PacketDecodeError.html new file mode 100644 index 000000000..dfe58d744 --- /dev/null +++ b/pr/509/docs/noq_proto/enum.PacketDecodeError.html @@ -0,0 +1,55 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.PathAbandonReason.html b/pr/509/docs/noq_proto/enum.PathAbandonReason.html new file mode 100644 index 000000000..3f8d7ced0 --- /dev/null +++ b/pr/509/docs/noq_proto/enum.PathAbandonReason.html @@ -0,0 +1,51 @@ +PathAbandonReason in noq_proto - Rust

PathAbandonReason

Enum PathAbandonReason 

Source
pub enum PathAbandonReason {
+    ApplicationClosed {
+        error_code: VarInt,
+    },
+    ValidationFailed,
+    TimedOut,
+    UnusableAfterNetworkChange,
+    NatTraversalRoundEnded,
+    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.

+
§

NatTraversalRoundEnded

The path was opened in a NAT traversal round which was terminated.

+
§

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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.PathError.html b/pr/509/docs/noq_proto/enum.PathError.html new file mode 100644 index 000000000..7b88739a9 --- /dev/null +++ b/pr/509/docs/noq_proto/enum.PathError.html @@ -0,0 +1,46 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.PathEvent.html b/pr/509/docs/noq_proto/enum.PathEvent.html new file mode 100644 index 000000000..78738a241 --- /dev/null +++ b/pr/509/docs/noq_proto/enum.PathEvent.html @@ -0,0 +1,72 @@ +PathEvent in noq_proto - Rust

PathEvent

Enum PathEvent 

Source
pub enum PathEvent {
+    Opened {
+        id: PathId,
+    },
+    Abandoned {
+        id: PathId,
+        reason: PathAbandonReason,
+    },
+    Discarded {
+        id: PathId,
+        path_stats: PathStats,
+    },
+    RemoteStatus {
+        id: PathId,
+        status: PathStatus,
+    },
+    ObservedAddr {
+        id: PathId,
+        addr: SocketAddr,
+    },
+}
Expand description

Application events about paths

+

Variants§

§

Opened

A new path has been opened

+

Fields

§id: PathId

Which path is now open

+
§

Abandoned

A path was abandoned and is no longer usable.

+

This event will always be followed by Self::Discarded after some time.

+

Fields

§id: PathId

With path was abandoned.

+
§reason: PathAbandonReason

Reason why this path was abandoned.

+
§

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

§id: PathId

Which path had its state dropped

+
§path_stats: PathStats

The final path stats, they are no longer available via Connection::stats

+
§

RemoteStatus

The remote changed the status of the path

+

The local status is not changed because of this event. It is up to the application +to update the local status, which is used for packet scheduling, when the remote +changes the status.

+

Fields

§id: PathId

Path which has changed status

+
§status: PathStatus

The new status set by the remote

+
§

ObservedAddr

Received an observation of our external address from the peer.

+

Fields

§id: PathId

Path over which the observed address was reported, PathId::ZERO when multipath is +not negotiated

+
§addr: SocketAddr

The address observed by the remote over this path

+

Trait Implementations§

Source§

impl Clone for PathEvent

Source§

fn clone(&self) -> PathEvent

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<PathEvent> for Event

Source§

fn from(source: PathEvent) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for PathEvent

Source§

fn eq(&self, other: &PathEvent) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for PathEvent

Source§

impl StructuralPartialEq for PathEvent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.PathStatus.html b/pr/509/docs/noq_proto/enum.PathStatus.html new file mode 100644 index 000000000..f6d1c9549 --- /dev/null +++ b/pr/509/docs/noq_proto/enum.PathStatus.html @@ -0,0 +1,43 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.ProtectedHeader.html b/pr/509/docs/noq_proto/enum.ProtectedHeader.html new file mode 100644 index 000000000..3db197cfd --- /dev/null +++ b/pr/509/docs/noq_proto/enum.ProtectedHeader.html @@ -0,0 +1,75 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.ReadError.html b/pr/509/docs/noq_proto/enum.ReadError.html new file mode 100644 index 000000000..471e6456e --- /dev/null +++ b/pr/509/docs/noq_proto/enum.ReadError.html @@ -0,0 +1,51 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.ReadableError.html b/pr/509/docs/noq_proto/enum.ReadableError.html new file mode 100644 index 000000000..764ff4ece --- /dev/null +++ b/pr/509/docs/noq_proto/enum.ReadableError.html @@ -0,0 +1,50 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.SendDatagramError.html b/pr/509/docs/noq_proto/enum.SendDatagramError.html new file mode 100644 index 000000000..0ea41f1ba --- /dev/null +++ b/pr/509/docs/noq_proto/enum.SendDatagramError.html @@ -0,0 +1,54 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.SetPathStatusError.html b/pr/509/docs/noq_proto/enum.SetPathStatusError.html new file mode 100644 index 000000000..b54666810 --- /dev/null +++ b/pr/509/docs/noq_proto/enum.SetPathStatusError.html @@ -0,0 +1,38 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.Side.html b/pr/509/docs/noq_proto/enum.Side.html new file mode 100644 index 000000000..136b52e1c --- /dev/null +++ b/pr/509/docs/noq_proto/enum.Side.html @@ -0,0 +1,54 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.StreamEvent.html b/pr/509/docs/noq_proto/enum.StreamEvent.html new file mode 100644 index 000000000..a20aff7e1 --- /dev/null +++ b/pr/509/docs/noq_proto/enum.StreamEvent.html @@ -0,0 +1,64 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/enum.WriteError.html b/pr/509/docs/noq_proto/enum.WriteError.html new file mode 100644 index 000000000..a604bd763 --- /dev/null +++ b/pr/509/docs/noq_proto/enum.WriteError.html @@ -0,0 +1,54 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/frame/enum.FrameType.html b/pr/509/docs/noq_proto/frame/enum.FrameType.html new file mode 100644 index 000000000..488123b8f --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/frame/enum.MaybeFrame.html b/pr/509/docs/noq_proto/frame/enum.MaybeFrame.html new file mode 100644 index 000000000..d93dce96c --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/frame/struct.ApplicationClose.html b/pr/509/docs/noq_proto/frame/struct.ApplicationClose.html new file mode 100644 index 000000000..b065b5302 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/frame/struct.ConnectionClose.html b/pr/509/docs/noq_proto/frame/struct.ConnectionClose.html new file mode 100644 index 000000000..040a8dc2a --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/frame/struct.Datagram.html b/pr/509/docs/noq_proto/frame/struct.Datagram.html new file mode 100644 index 000000000..8fdb7d9f7 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/frame/struct.DatagramInfo.html b/pr/509/docs/noq_proto/frame/struct.DatagramInfo.html new file mode 100644 index 000000000..8bc2fc4fb --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/frame/struct.InvalidFrameId.html b/pr/509/docs/noq_proto/frame/struct.InvalidFrameId.html new file mode 100644 index 000000000..384bde4f9 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/frame/struct.StreamInfo.html b/pr/509/docs/noq_proto/frame/struct.StreamInfo.html new file mode 100644 index 000000000..032d16b41 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/index.html b/pr/509/docs/noq_proto/index.html new file mode 100644 index 000000000..36d11ed52 --- /dev/null +++ b/pr/509/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
Written
Indicates how many bytes and chunks had been transferred in a write operation

Enums§

ClosePathError
Errors triggered when abandoning a path
ConfigError
Errors in the configuration of an endpoint
ConnectError
Errors in the parameters being used to create a new connection
ConnectionError
Reasons why a connection might be lost
DatagramEvent
Event resulting from processing a single datagram
Dir
Whether a stream communicates data in both directions or only from the initiator
EcnCodepoint
Explicit congestion notification codepoint
Event
Events of interest to the application
FinishError
Reasons why attempting to finish a stream might fail
FrameType
LongType
Long packet types with uniform header structure
MaybeFrame
The type used to refer to FrameTypes in closing and transport errors.
PacketDecodeError
Packet decode error
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_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED
When multipath is required and has not been explicitly enabled, this value will be used for
DEFAULT_SUPPORTED_VERSIONS
The QUIC protocol version implemented.

Traits§

ConnectionIdGenerator
Generates connection IDs for incoming connections
ConnectionIdParser
Parse connection id in short header packet
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/509/docs/noq_proto/n0_nat_traversal/enum.Error.html b/pr/509/docs/noq_proto/n0_nat_traversal/enum.Error.html new file mode 100644 index 000000000..2d32fa783 --- /dev/null +++ b/pr/509/docs/noq_proto/n0_nat_traversal/enum.Error.html @@ -0,0 +1,39 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/n0_nat_traversal/enum.Event.html b/pr/509/docs/noq_proto/n0_nat_traversal/enum.Event.html new file mode 100644 index 000000000..b09256cd0 --- /dev/null +++ b/pr/509/docs/noq_proto/n0_nat_traversal/enum.Event.html @@ -0,0 +1,32 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/n0_nat_traversal/index.html b/pr/509/docs/noq_proto/n0_nat_traversal/index.html new file mode 100644 index 000000000..741d131a5 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/n0_nat_traversal/sidebar-items.js b/pr/509/docs/noq_proto/n0_nat_traversal/sidebar-items.js new file mode 100644 index 000000000..e03b07ea1 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/packet/enum.LongType.html b/pr/509/docs/noq_proto/packet/enum.LongType.html new file mode 100644 index 000000000..4cc962748 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/packet/enum.PacketDecodeError.html b/pr/509/docs/noq_proto/packet/enum.PacketDecodeError.html new file mode 100644 index 000000000..a1853fef7 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/packet/enum.ProtectedHeader.html b/pr/509/docs/noq_proto/packet/enum.ProtectedHeader.html new file mode 100644 index 000000000..20b1faa75 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/packet/struct.FixedLengthConnectionIdParser.html b/pr/509/docs/noq_proto/packet/struct.FixedLengthConnectionIdParser.html new file mode 100644 index 000000000..ae49b212b --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/packet/struct.PartialDecode.html b/pr/509/docs/noq_proto/packet/struct.PartialDecode.html new file mode 100644 index 000000000..c588f9314 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/packet/struct.ProtectedInitialHeader.html b/pr/509/docs/noq_proto/packet/struct.ProtectedInitialHeader.html new file mode 100644 index 000000000..f93b4575c --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/packet/trait.ConnectionIdParser.html b/pr/509/docs/noq_proto/packet/trait.ConnectionIdParser.html new file mode 100644 index 000000000..dac6135b4 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/shared/enum.EcnCodepoint.html b/pr/509/docs/noq_proto/shared/enum.EcnCodepoint.html new file mode 100644 index 000000000..dd57eb734 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/shared/struct.ConnectionEvent.html b/pr/509/docs/noq_proto/shared/struct.ConnectionEvent.html new file mode 100644 index 000000000..bd2aa06f1 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/shared/struct.ConnectionId.html b/pr/509/docs/noq_proto/shared/struct.ConnectionId.html new file mode 100644 index 000000000..07078baea --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/shared/struct.EndpointEvent.html b/pr/509/docs/noq_proto/shared/struct.EndpointEvent.html new file mode 100644 index 000000000..b86119665 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/sidebar-items.js b/pr/509/docs/noq_proto/sidebar-items.js new file mode 100644 index 000000000..e9a741d67 --- /dev/null +++ b/pr/509/docs/noq_proto/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"constant":["DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED","DEFAULT_SUPPORTED_VERSIONS"],"enum":["ClosePathError","ConfigError","ConnectError","ConnectionError","DatagramEvent","Dir","EcnCodepoint","Event","FinishError","FrameType","LongType","MaybeFrame","PacketDecodeError","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","Written"],"trait":["ConnectionIdGenerator","ConnectionIdParser","NetworkChangeHint","QlogFactory","TimeSource","TokenLog","TokenStore"]}; \ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.AcceptError.html b/pr/509/docs/noq_proto/struct.AcceptError.html new file mode 100644 index 000000000..01b431c0e --- /dev/null +++ b/pr/509/docs/noq_proto/struct.AcceptError.html @@ -0,0 +1,30 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.AckFrequencyConfig.html b/pr/509/docs/noq_proto/struct.AckFrequencyConfig.html new file mode 100644 index 000000000..3ccdbf03e --- /dev/null +++ b/pr/509/docs/noq_proto/struct.AckFrequencyConfig.html @@ -0,0 +1,59 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.ApplicationClose.html b/pr/509/docs/noq_proto/struct.ApplicationClose.html new file mode 100644 index 000000000..a12370d88 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.ApplicationClose.html @@ -0,0 +1,38 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.BloomTokenLog.html b/pr/509/docs/noq_proto/struct.BloomTokenLog.html new file mode 100644 index 000000000..0a93c4e86 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.BloomTokenLog.html @@ -0,0 +1,48 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.Chunk.html b/pr/509/docs/noq_proto/struct.Chunk.html new file mode 100644 index 000000000..38b7dcdaf --- /dev/null +++ b/pr/509/docs/noq_proto/struct.Chunk.html @@ -0,0 +1,35 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.Chunks.html b/pr/509/docs/noq_proto/struct.Chunks.html new file mode 100644 index 000000000..1b4c4287a --- /dev/null +++ b/pr/509/docs/noq_proto/struct.Chunks.html @@ -0,0 +1,41 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.ClientConfig.html b/pr/509/docs/noq_proto/struct.ClientConfig.html new file mode 100644 index 000000000..867fb8970 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.ClientConfig.html @@ -0,0 +1,46 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.ClosedPath.html b/pr/509/docs/noq_proto/struct.ClosedPath.html new file mode 100644 index 000000000..d61471098 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.ClosedPath.html @@ -0,0 +1,33 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.ClosedStream.html b/pr/509/docs/noq_proto/struct.ClosedStream.html new file mode 100644 index 000000000..5754c45db --- /dev/null +++ b/pr/509/docs/noq_proto/struct.ClosedStream.html @@ -0,0 +1,33 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.Connection.html b/pr/509/docs/noq_proto/struct.Connection.html new file mode 100644 index 000000000..8aee2496b --- /dev/null +++ b/pr/509/docs/noq_proto/struct.Connection.html @@ -0,0 +1,264 @@ +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.

+

This comparison will use FourTuple::is_probably_same_path on the given network_path +and pass it existing path’s network paths.

+

This means that you can pass local_ip: None to make the comparison only compare +remote addresses.

+

This avoids having to guess which local interface will be used to communicate with the +remote, should it not be known yet. We assume that if we already have a path to the remote, +the OS is likely to use the same interface to talk to said remote.

+

See also open_path. Returns (path_id, true) if the path already existed. (path_id, false) if was opened.

+
Source

pub fn open_path( + &mut self, + network_path: FourTuple, + initial_status: PathStatus, + now: Instant, +) -> Result<PathId, PathError>

Opens a new path

+

Further errors might occur and they will be emitted in PathEvent::Abandoned events with this path id. +When the path is opened it will be reported as an PathEvent::Opened.

+
Source

pub fn close_path( + &mut self, + now: Instant, + path_id: PathId, + error_code: VarInt, +) -> Result<(), ClosePathError>

Closes a path and sends a PATH_ABANDON frame with the passed error code.

+

This will not allow closing the last path. It does allow closing paths which have +not yet been opened, as e.g. is the case when receiving a PATH_ABANDON from the peer +for a path that was never opened locally.

+
Source

pub fn paths(&self) -> Vec<PathId>

Returns all known paths.

+

There is no guarantee any of these paths are open or usable.

+
Source

pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath>

Gets the local PathStatus for a known PathId

+
Source

pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath>

Returns the path’s network path represented as a 4-tuple.

+
Source

pub fn set_path_status( + &mut self, + path_id: PathId, + status: PathStatus, +) -> Result<PathStatus, SetPathStatusError>

Sets the PathStatus for a known PathId

+

Returns the previous path status on success.

+
Source

pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus>

Returns the remote path status

+
Source

pub fn set_path_max_idle_timeout( + &mut self, + path_id: PathId, + timeout: Option<Duration>, +) -> Result<Option<Duration>, ClosedPath>

Sets the max_idle_timeout for a specific path

+

See TransportConfig::default_path_max_idle_timeout for details.

+

Returns the previous value of the setting.

+
Source

pub fn set_path_keep_alive_interval( + &mut self, + path_id: PathId, + interval: Option<Duration>, +) -> Result<Option<Duration>, ClosedPath>

Sets the keep_alive_interval for a specific path

+

See TransportConfig::default_path_keep_alive_interval for details.

+

Returns the previous value of the setting.

+
Source

pub fn poll_transmit( + &mut self, + now: Instant, + max_datagrams: NonZeroUsize, + buf: &mut Vec<u8>, +) -> Option<Transmit>

Returns packets to transmit

+

Connections should be polled for transmit after:

+
    +
  • the application performed some I/O on the connection
  • +
  • a call was made to handle_event
  • +
  • a call was made to handle_timeout
  • +
+

max_datagrams specifies how many datagrams can be returned inside a +single Transmit using GSO. This must be at least 1.

+
Source

pub fn handle_event(&mut self, event: ConnectionEvent)

Process ConnectionEvents generated by the associated Endpoint

+

Will execute protocol logic upon receipt of a connection event, in turn preparing signals +(including application Events, EndpointEvents and outgoing datagrams) that should be +extracted through the relevant methods.

+
Source

pub fn handle_timeout(&mut self, now: Instant)

Process timer expirations

+

Executes protocol logic, potentially preparing signals (including application Events, +EndpointEvents and outgoing datagrams) that should be extracted through the relevant +methods.

+

It is most efficient to call this immediately after the system clock reaches the latest +Instant that was output by poll_timeout; however spurious extra calls will simply +no-op and therefore are safe.

+
Source

pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes)

Close a connection immediately

+

This does not ensure delivery of outstanding data. It is the application’s responsibility to +call this only when all important communications have been completed, e.g. by calling +SendStream::finish on outstanding streams and waiting for the corresponding +StreamEvent::Finished event.

+

If Streams::send_streams returns 0, all outstanding stream data has been +delivered. There may still be data from the peer that has not been received.

+
Source

pub fn datagrams(&mut self) -> Datagrams<'_>

Control datagrams

+
Source

pub fn stats(&mut self) -> ConnectionStats

Returns connection statistics

+
Source

pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats>

Returns path statistics

+
Source

pub fn ping(&mut self)

Ping the remote endpoint

+

Causes an ACK-eliciting packet to be transmitted on the connection.

+
Source

pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath>

Ping the remote endpoint over a specific path

+

Causes an ACK-eliciting packet to be transmitted on the path.

+
Source

pub fn force_key_update(&mut self)

Update traffic keys spontaneously

+

This can be useful for testing key updates, as they otherwise only happen infrequently.

+
Source

pub fn crypto_session(&self) -> &dyn Session

Get a session reference

+
Source

pub fn is_handshaking(&self) -> bool

Whether the connection is in the process of being established

+

If this returns false, the connection may be either established or closed, signaled by the +emission of a Connected or ConnectionLost message respectively.

+
Source

pub fn is_closed(&self) -> bool

Whether the connection is closed

+

Closed connections cannot transport any further data. A connection becomes closed when +either peer application intentionally closes it, or when either transport layer detects an +error such as a time-out or certificate validation failure.

+

A ConnectionLost event is emitted with details when the connection becomes closed.

+
Source

pub fn is_drained(&self) -> bool

Whether there is no longer any need to keep the connection around

+

Closed connections become drained after a brief timeout to absorb any remaining in-flight +packets from the peer. All drained connections have been closed.

+
Source

pub fn accepted_0rtt(&self) -> bool

For clients, if the peer accepted the 0-RTT data packets

+

The value is meaningless until after the handshake completes.

+
Source

pub fn has_0rtt(&self) -> bool

Whether 0-RTT is/was possible during the handshake

+
Source

pub fn has_pending_retransmits(&self) -> bool

Whether there are any pending retransmits

+
Source

pub fn side(&self) -> Side

Look up whether we’re the client or server of this Connection

+
Source

pub fn path_observed_address( + &self, + path_id: PathId, +) -> Result<Option<SocketAddr>, ClosedPath>

Get the address observed by the remote over the given path

+
Source

pub fn rtt(&self, path_id: PathId) -> Option<Duration>

Current best estimate of this connection’s latency (round-trip-time)

+
Source

pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller>

Current state of this connection’s congestion controller, for debugging purposes

+
Source

pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt)

Modify the number of remotely initiated streams that may be concurrently open

+

No streams may be opened by the peer unless fewer than count are already open. Large +counts increase both minimum and worst-case memory consumption.

+
Source

pub fn set_max_concurrent_paths( + &mut self, + now: Instant, + count: NonZeroU32, +) -> Result<(), MultipathNotNegotiated>

Modify the number of open paths allowed when multipath is enabled

+

When reducing the number of concurrent paths this will only affect delaying sending +new MAX_PATH_ID frames until fewer than this number of paths are possible. To +actively reduce paths they must be closed using Connection::close_path, which +can also be used to close not-yet-opened paths.

+

If multipath is not negotiated (see the TransportConfig) this can not enable +multipath and will fail.

+
Source

pub fn max_concurrent_streams(&self, dir: Dir) -> u64

Current number of remotely initiated streams that may be concurrently open

+

If the target for this limit is reduced using set_max_concurrent_streams, +it will not change immediately, even if fewer streams are open. Instead, it will +decrement by one for each time a remotely initiated stream of matching directionality is closed.

+
Source

pub fn set_send_window(&mut self, send_window: u64)

Source

pub fn set_receive_window(&mut self, receive_window: VarInt)

Source

pub fn is_multipath_negotiated(&self) -> bool

Whether the Multipath for QUIC extension is enabled.

+

Multipath is only enabled after the handshake is completed and if it was enabled by both +peers.

+
Source

pub fn 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, and paths that have not been opened are closed.

+

Returns the server addresses that are now being probed. +If addresses fail due to spurious errors, these might succeed later and not be returned in +this set.

+

Trait Implementations§

Source§

impl Debug for Connection

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.ConnectionClose.html b/pr/509/docs/noq_proto/struct.ConnectionClose.html new file mode 100644 index 000000000..83906d6bc --- /dev/null +++ b/pr/509/docs/noq_proto/struct.ConnectionClose.html @@ -0,0 +1,40 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.ConnectionEvent.html b/pr/509/docs/noq_proto/struct.ConnectionEvent.html new file mode 100644 index 000000000..7d8eec5ca --- /dev/null +++ b/pr/509/docs/noq_proto/struct.ConnectionEvent.html @@ -0,0 +1,25 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.ConnectionHandle.html b/pr/509/docs/noq_proto/struct.ConnectionHandle.html new file mode 100644 index 000000000..742f46c38 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.ConnectionHandle.html @@ -0,0 +1,42 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.ConnectionId.html b/pr/509/docs/noq_proto/struct.ConnectionId.html new file mode 100644 index 000000000..dd04ec886 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.ConnectionId.html @@ -0,0 +1,2468 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.ConnectionStats.html b/pr/509/docs/noq_proto/struct.ConnectionStats.html new file mode 100644 index 000000000..2e0c956f1 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.ConnectionStats.html @@ -0,0 +1,36 @@ +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, +}
Expand description

Connection statistics

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§udp_tx: UdpStats

Statistics about UDP datagrams transmitted on a connection

+
§udp_rx: UdpStats

Statistics about UDP datagrams received on a connection

+
§frame_tx: FrameStats

Statistics about frames transmitted on a connection

+
§frame_rx: FrameStats

Statistics about frames received on a connection

+

Trait Implementations§

Source§

impl Clone for ConnectionStats

Source§

fn clone(&self) -> ConnectionStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ConnectionStats

Source§

fn default() -> ConnectionStats

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.Datagram.html b/pr/509/docs/noq_proto/struct.Datagram.html new file mode 100644 index 000000000..dc270d371 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.Datagram.html @@ -0,0 +1,31 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.DatagramInfo.html b/pr/509/docs/noq_proto/struct.DatagramInfo.html new file mode 100644 index 000000000..a00cb04c0 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.DatagramInfo.html @@ -0,0 +1,32 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.Datagrams.html b/pr/509/docs/noq_proto/struct.Datagrams.html new file mode 100644 index 000000000..0fef69684 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.Datagrams.html @@ -0,0 +1,44 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.DecryptedInitial.html b/pr/509/docs/noq_proto/struct.DecryptedInitial.html new file mode 100644 index 000000000..96d39b725 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.DecryptedInitial.html @@ -0,0 +1,31 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.Endpoint.html b/pr/509/docs/noq_proto/struct.Endpoint.html new file mode 100644 index 000000000..2f5f238de --- /dev/null +++ b/pr/509/docs/noq_proto/struct.Endpoint.html @@ -0,0 +1,78 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.EndpointConfig.html b/pr/509/docs/noq_proto/struct.EndpointConfig.html new file mode 100644 index 000000000..91b47c868 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.EndpointConfig.html @@ -0,0 +1,66 @@ +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<F: Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync + 'static>( + &mut self, + factory: F, +) -> &mut Self

Supply a custom connection ID generator factory

+

Called once by each Endpoint constructed from this configuration to obtain the CID +generator which will be used to generate the CIDs used for incoming packets on all +connections involving that Endpoint. A custom CID generator allows applications to embed +information in local connection IDs, e.g. to support stateless packet-level load balancers.

+

Defaults to HashedConnectionIdGenerator.

+
Source

pub fn reset_key(&mut self, key: Arc<dyn HmacKey>) -> &mut Self

Private key used to send authenticated connection resets to peers who were +communicating with a previous instance of this endpoint.

+
Source

pub fn max_udp_payload_size( + &mut self, + value: u16, +) -> Result<&mut Self, ConfigError>

Maximum UDP payload size accepted from peers (excluding UDP and IP overhead).

+

Must be greater or equal than 1200.

+

Defaults to 1472, which is the largest UDP payload that can be transmitted in the typical +1500 byte Ethernet MTU. Deployments on links with larger MTUs (e.g. loopback or Ethernet +with jumbo frames) can raise this to improve performance at the cost of a linear increase in +datagram receive buffer size.

+
Source

pub fn get_max_udp_payload_size(&self) -> u64

Get the current value of max_udp_payload_size

+
Source

pub fn supported_versions(&mut self, supported_versions: Vec<u32>) -> &mut Self

Override supported QUIC versions

+
Source

pub fn grease_quic_bit(&mut self, value: bool) -> &mut Self

Whether to accept QUIC packets containing any value for the fixed bit

+

Enabled by default. Helps protect against protocol ossification and makes traffic less +identifiable to observers. Disable if helping observers identify this traffic as QUIC is +desired.

+
Source

pub fn min_reset_interval(&mut self, value: Duration) -> &mut Self

Minimum interval between outgoing stateless reset packets

+

Defaults to 20ms. Limits the impact of attacks which flood an endpoint with garbage packets, +e.g. ISAKMP/IKE amplification. Larger values provide a stronger defense, but may delay +detection of some error conditions by clients. Using a ConnectionIdGenerator with a low +rate of false positives in validate reduces the risk +incurred by a small minimum reset interval.

+
Source

pub fn rng_seed(&mut self, seed: Option<[u8; 32]>) -> &mut Self

Optional seed to be used internally for random number generation

+

By default, 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.EndpointEvent.html b/pr/509/docs/noq_proto/struct.EndpointEvent.html new file mode 100644 index 000000000..239a0aed2 --- /dev/null +++ b/pr/509/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.

+

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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.FixedLengthConnectionIdParser.html b/pr/509/docs/noq_proto/struct.FixedLengthConnectionIdParser.html new file mode 100644 index 000000000..aa79a72b2 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.FixedLengthConnectionIdParser.html @@ -0,0 +1,26 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.FourTuple.html b/pr/509/docs/noq_proto/struct.FourTuple.html new file mode 100644 index 000000000..4c9a30a00 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.FourTuple.html @@ -0,0 +1,63 @@ +FourTuple in noq_proto - Rust

FourTuple

Struct FourTuple 

Source
pub struct FourTuple {
+    pub remote: SocketAddr,
+    pub local_ip: Option<IpAddr>,
+}
Expand description

Identifies a network path by the combination of remote and local addresses

+

Including the local ensures good behavior when the host has multiple IP addresses on the same +subnet and zero-length connection IDs are in use or when multipath is enabled and multiple +paths exist with the same remote, but different local IP interfaces.

+

Fields§

§remote: SocketAddr

The remote side of this tuple

+
§local_ip: Option<IpAddr>

The local side of this tuple.

+

The socket is irrelevant for our intents and purposes: +When we send, we can only specify the src_ip, not the source port. +So even if we track the port, we won’t be able to make use of it.

+

Implementations§

Source§

impl FourTuple

Source

pub fn is_probably_same_path(&self, other: &Self) -> bool

Returns whether we think the other address probably represents the same path +as ours.

+

If we have a local IP set, then we’re exact and only match if the 4-tuples are +exactly equal. +If we don’t have a local IP set, then we only check the remote addresses for equality.

+

Note that because of this, the following calls might differ:

+
    +
  • a.is_probably_same_path(b)
  • +
  • b.is_probably_same_path(a)
  • +
+
Source

pub fn update_local_if_same_remote(&mut self, other: &Self) -> bool

Updates this tuple’s local address iff

+
    +
  • it was unset before,
  • +
  • the other tuple has the same remote, and
  • +
  • the other tuple has a local address set.
  • +
+

Returns whether this and the other remote are now fully equal.

+

Trait Implementations§

Source§

impl Clone for FourTuple

Source§

fn clone(&self) -> FourTuple

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FourTuple

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for FourTuple

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Hash for FourTuple

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for FourTuple

Source§

fn eq(&self, other: &FourTuple) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for FourTuple

Source§

impl Eq for FourTuple

Source§

impl StructuralPartialEq for FourTuple

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.FrameStats.html b/pr/509/docs/noq_proto/struct.FrameStats.html new file mode 100644 index 000000000..be8c85d63 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.FrameStats.html @@ -0,0 +1,65 @@ +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 Clone for FrameStats

Source§

fn clone(&self) -> FrameStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FrameStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for FrameStats

Source§

fn default() -> FrameStats

Returns the “default value” for a type. Read more
Source§

impl Copy for FrameStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.HashedConnectionIdGenerator.html b/pr/509/docs/noq_proto/struct.HashedConnectionIdGenerator.html new file mode 100644 index 000000000..6401cd882 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.HashedConnectionIdGenerator.html @@ -0,0 +1,33 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.IdleTimeout.html b/pr/509/docs/noq_proto/struct.IdleTimeout.html new file mode 100644 index 000000000..155ecccae --- /dev/null +++ b/pr/509/docs/noq_proto/struct.IdleTimeout.html @@ -0,0 +1,49 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.Incoming.html b/pr/509/docs/noq_proto/struct.Incoming.html new file mode 100644 index 000000000..e24dd0d46 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.Incoming.html @@ -0,0 +1,39 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.IncomingAlpns.html b/pr/509/docs/noq_proto/struct.IncomingAlpns.html new file mode 100644 index 000000000..6c857c1c9 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.IncomingAlpns.html @@ -0,0 +1,638 @@ +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
Source§

impl<I> IteratorRandom for I
where + I: Iterator,

Source§

fn choose<R>(self, rng: &mut R) -> Option<Self::Item>
where + R: Rng + ?Sized,

Uniformly sample one element Read more
Source§

fn choose_stable<R>(self, rng: &mut R) -> Option<Self::Item>
where + R: Rng + ?Sized,

Uniformly sample one element (stable) Read more
Source§

fn choose_multiple_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
Source§

fn choose_multiple<R>(self, rng: &mut R, amount: usize) -> Vec<Self::Item>
where + R: Rng + ?Sized,

Uniformly sample amount distinct elements into a Vec Read more
§

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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.InvalidCid.html b/pr/509/docs/noq_proto/struct.InvalidCid.html new file mode 100644 index 000000000..44963b885 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.InvalidCid.html @@ -0,0 +1,27 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.InvalidFrameId.html b/pr/509/docs/noq_proto/struct.InvalidFrameId.html new file mode 100644 index 000000000..d15c6f3e6 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.InvalidFrameId.html @@ -0,0 +1,31 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.MtuDiscoveryConfig.html b/pr/509/docs/noq_proto/struct.MtuDiscoveryConfig.html new file mode 100644 index 000000000..2afe4b3ad --- /dev/null +++ b/pr/509/docs/noq_proto/struct.MtuDiscoveryConfig.html @@ -0,0 +1,85 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.MultipathNotNegotiated.html b/pr/509/docs/noq_proto/struct.MultipathNotNegotiated.html new file mode 100644 index 000000000..16050460b --- /dev/null +++ b/pr/509/docs/noq_proto/struct.MultipathNotNegotiated.html @@ -0,0 +1,28 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.NoneTokenLog.html b/pr/509/docs/noq_proto/struct.NoneTokenLog.html new file mode 100644 index 000000000..233cd3f74 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.NoneTokenLog.html @@ -0,0 +1,31 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.NoneTokenStore.html b/pr/509/docs/noq_proto/struct.NoneTokenStore.html new file mode 100644 index 000000000..85865f4a2 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.NoneTokenStore.html @@ -0,0 +1,25 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.PartialDecode.html b/pr/509/docs/noq_proto/struct.PartialDecode.html new file mode 100644 index 000000000..c90552a0c --- /dev/null +++ b/pr/509/docs/noq_proto/struct.PartialDecode.html @@ -0,0 +1,42 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.PathId.html b/pr/509/docs/noq_proto/struct.PathId.html new file mode 100644 index 000000000..4c016ad9c --- /dev/null +++ b/pr/509/docs/noq_proto/struct.PathId.html @@ -0,0 +1,49 @@ +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 IdentityHashable for PathId

Source§

impl StructuralPartialEq for PathId

Auto Trait Implementations§

§

impl Freeze for PathId

§

impl RefUnwindSafe for PathId

§

impl Send for PathId

§

impl Sync for PathId

§

impl Unpin for PathId

§

impl UnwindSafe for PathId

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.PathStats.html b/pr/509/docs/noq_proto/struct.PathStats.html new file mode 100644 index 000000000..a7ca44f86 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.PathStats.html @@ -0,0 +1,56 @@ +PathStats in noq_proto - Rust

PathStats

Struct PathStats 

Source
#[non_exhaustive]
pub struct PathStats { + pub rtt: Duration, + pub udp_tx: UdpStats, + pub udp_rx: UdpStats, + pub cwnd: u64, + pub congestion_events: u64, + pub lost_packets: u64, + pub lost_bytes: u64, + pub sent_plpmtud_probes: u64, + pub lost_plpmtud_probes: u64, + pub black_holes_detected: u64, + pub current_mtu: u16, +}
Expand description

Statistics related to a transmission path

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§rtt: Duration

Current best estimate of this connection’s latency (round-trip-time)

+
§udp_tx: UdpStats

Statistics about datagrams and bytes sent on this path

+
§udp_rx: UdpStats

Statistics about datagrams and bytes received on this path

+
§cwnd: u64

Current congestion window of the connection

+
§congestion_events: u64

Congestion events on the connection

+
§lost_packets: u64

The amount of packets lost on this path

+
§lost_bytes: u64

The amount of bytes lost on this path

+
§sent_plpmtud_probes: u64

The amount of PLPMTUD probe packets sent on this path (also counted by udp_tx.datagrams)

+
§lost_plpmtud_probes: u64

The amount of PLPMTUD probe packets lost on this path (ignored by lost_packets and +lost_bytes)

+
§black_holes_detected: u64

The number of times a black hole was detected in the path

+
§current_mtu: u16

Largest UDP payload size the path currently supports

+

Trait Implementations§

Source§

impl Clone for PathStats

Source§

fn clone(&self) -> PathStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for PathStats

Source§

fn default() -> PathStats

Returns the “default value” for a type. Read more
Source§

impl PartialEq for PathStats

Source§

fn eq(&self, other: &PathStats) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for PathStats

Source§

impl Eq for PathStats

Source§

impl StructuralPartialEq for PathStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.ProtectedInitialHeader.html b/pr/509/docs/noq_proto/struct.ProtectedInitialHeader.html new file mode 100644 index 000000000..238cb3221 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.ProtectedInitialHeader.html @@ -0,0 +1,38 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.QlogConfig.html b/pr/509/docs/noq_proto/struct.QlogConfig.html new file mode 100644 index 000000000..a76e2b194 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.QlogConfig.html @@ -0,0 +1,34 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.QlogFileFactory.html b/pr/509/docs/noq_proto/struct.QlogFileFactory.html new file mode 100644 index 000000000..d70ee3434 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.QlogFileFactory.html @@ -0,0 +1,39 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.RandomConnectionIdGenerator.html b/pr/509/docs/noq_proto/struct.RandomConnectionIdGenerator.html new file mode 100644 index 000000000..33ef1d6bd --- /dev/null +++ b/pr/509/docs/noq_proto/struct.RandomConnectionIdGenerator.html @@ -0,0 +1,33 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.RecvStream.html b/pr/509/docs/noq_proto/struct.RecvStream.html new file mode 100644 index 000000000..8a74a85c1 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.RecvStream.html @@ -0,0 +1,44 @@ +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 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.RetryError.html b/pr/509/docs/noq_proto/struct.RetryError.html new file mode 100644 index 000000000..edae410e3 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.RetryError.html @@ -0,0 +1,27 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.RttEstimator.html b/pr/509/docs/noq_proto/struct.RttEstimator.html new file mode 100644 index 000000000..a21c465a9 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.RttEstimator.html @@ -0,0 +1,32 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.SendStream.html b/pr/509/docs/noq_proto/struct.SendStream.html new file mode 100644 index 000000000..67bc1dac6 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.SendStream.html @@ -0,0 +1,53 @@ +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 [Bytes], +) -> Result<Written, WriteError>

Send data on the given stream

+

Returns the number of bytes and chunks successfully written. +Note that this method might also write a partial chunk. In this case +Written::chunks will not count this chunk as fully written. However +the chunk will be advanced and contain only non-written data after the call.

+
Source

pub fn stopped(&self) -> Result<Option<VarInt>, ClosedStream>

Check if this stream was stopped, get the reason if it was

+
Source

pub fn finish(&mut self) -> Result<(), FinishError>

Finish a send stream, signalling that no more data will be sent.

+

If this fails, no StreamEvent::Finished will be generated.

+
Source

pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream>

Abandon transmitting data on a stream

+
§Panics
+
    +
  • when applied to a receive stream
  • +
+
Source

pub fn set_priority(&mut self, priority: i32) -> Result<(), ClosedStream>

Set the priority of a stream

+
§Panics
+
    +
  • when applied to a receive stream
  • +
+
Source

pub fn priority(&self) -> Result<i32, ClosedStream>

Get the priority of a stream

+
§Panics
+
    +
  • when applied to a receive stream
  • +
+

Auto Trait Implementations§

§

impl<'a> Freeze for SendStream<'a>

§

impl<'a> !RefUnwindSafe for SendStream<'a>

§

impl<'a> Send for SendStream<'a>

§

impl<'a> Sync for SendStream<'a>

§

impl<'a> Unpin for SendStream<'a>

§

impl<'a> !UnwindSafe for SendStream<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.ServerConfig.html b/pr/509/docs/noq_proto/struct.ServerConfig.html new file mode 100644 index 000000000..b46b8dcf7 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.ServerConfig.html @@ -0,0 +1,102 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.ShouldTransmit.html b/pr/509/docs/noq_proto/struct.ShouldTransmit.html new file mode 100644 index 000000000..cf5b05fbd --- /dev/null +++ b/pr/509/docs/noq_proto/struct.ShouldTransmit.html @@ -0,0 +1,35 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.StdSystemTime.html b/pr/509/docs/noq_proto/struct.StdSystemTime.html new file mode 100644 index 000000000..317c42f31 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.StdSystemTime.html @@ -0,0 +1,26 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.StreamId.html b/pr/509/docs/noq_proto/struct.StreamId.html new file mode 100644 index 000000000..09acf13d9 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.StreamId.html @@ -0,0 +1,52 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.StreamInfo.html b/pr/509/docs/noq_proto/struct.StreamInfo.html new file mode 100644 index 000000000..21b90b831 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.StreamInfo.html @@ -0,0 +1,32 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.Streams.html b/pr/509/docs/noq_proto/struct.Streams.html new file mode 100644 index 000000000..0ef65b0da --- /dev/null +++ b/pr/509/docs/noq_proto/struct.Streams.html @@ -0,0 +1,35 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.TokenMemoryCache.html b/pr/509/docs/noq_proto/struct.TokenMemoryCache.html new file mode 100644 index 000000000..9ff9a863d --- /dev/null +++ b/pr/509/docs/noq_proto/struct.TokenMemoryCache.html @@ -0,0 +1,28 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.TokenReuseError.html b/pr/509/docs/noq_proto/struct.TokenReuseError.html new file mode 100644 index 000000000..336b87ea8 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.TokenReuseError.html @@ -0,0 +1,25 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.Transmit.html b/pr/509/docs/noq_proto/struct.Transmit.html new file mode 100644 index 000000000..9459f2b4c --- /dev/null +++ b/pr/509/docs/noq_proto/struct.Transmit.html @@ -0,0 +1,37 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.TransportConfig.html b/pr/509/docs/noq_proto/struct.TransportConfig.html new file mode 100644 index 000000000..bfc9580b6 --- /dev/null +++ b/pr/509/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 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 set_max_remote_nat_traversal_addresses( + &mut self, + max_addresses: u8, +) -> &mut Self

Sets the maximum number of nat traversal addresses this endpoint allows the remote to +advertise

+

Setting this to any nonzero value will enable 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, a default value of +DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED will be used.

+
Source

pub fn qlog_factory(&mut self, factory: Arc<dyn QlogFactory>) -> &mut Self

Configures qlog capturing by setting a QlogFactory.

+

This assigns a QlogFactory that produces qlog capture configurations for +individual connections.

+
Source

pub fn qlog_from_env(&mut self, prefix: &str) -> &mut Self

Configures qlog capturing through the QLOGDIR environment variable.

+

This uses QlogFileFactory::from_env to create a factory to write qlog traces +into the directory set through the QLOGDIR environment variable.

+

If QLOGDIR is not set, no traces will be written. If QLOGDIR is set to a path +that does not exist, it will be created.

+

The files will be prefixed with prefix.

+
Source

pub fn qlog_from_path( + &mut self, + path: impl AsRef<Path>, + prefix: &str, +) -> &mut Self

Configures qlog capturing into a directory.

+

This uses QlogFileFactory to create a factory to write qlog traces into +the specified directory. The files will be prefixed with prefix.

+

Trait Implementations§

Source§

impl Clone for TransportConfig

Source§

fn clone(&self) -> TransportConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for TransportConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for TransportConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.TransportError.html b/pr/509/docs/noq_proto/struct.TransportError.html new file mode 100644 index 000000000..d29c662c5 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.TransportError.html @@ -0,0 +1,45 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.TransportErrorCode.html b/pr/509/docs/noq_proto/struct.TransportErrorCode.html new file mode 100644 index 000000000..ca9a86f9b --- /dev/null +++ b/pr/509/docs/noq_proto/struct.TransportErrorCode.html @@ -0,0 +1,55 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.UdpStats.html b/pr/509/docs/noq_proto/struct.UdpStats.html new file mode 100644 index 000000000..44de660e4 --- /dev/null +++ b/pr/509/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 amount of UDP datagrams observed

+
§bytes: u64

The total amount of bytes which have been transferred inside UDP datagrams

+
§ios: u64

The amount of I/O operations executed

+

Can be less than datagrams when GSO, GRO, and/or batched system calls are in use.

+

Trait Implementations§

Source§

impl Clone for UdpStats

Source§

fn clone(&self) -> UdpStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for UdpStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for UdpStats

Source§

fn default() -> UdpStats

Returns the “default value” for a type. Read more
Source§

impl PartialEq for UdpStats

Source§

fn eq(&self, other: &UdpStats) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for UdpStats

Source§

impl Eq for UdpStats

Source§

impl StructuralPartialEq for UdpStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.ValidationTokenConfig.html b/pr/509/docs/noq_proto/struct.ValidationTokenConfig.html new file mode 100644 index 000000000..f729e8632 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.ValidationTokenConfig.html @@ -0,0 +1,74 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.VarInt.html b/pr/509/docs/noq_proto/struct.VarInt.html new file mode 100644 index 000000000..2bbf7ae60 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.VarInt.html @@ -0,0 +1,62 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.VarIntBoundsExceeded.html b/pr/509/docs/noq_proto/struct.VarIntBoundsExceeded.html new file mode 100644 index 000000000..ed90c4e60 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.VarIntBoundsExceeded.html @@ -0,0 +1,33 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/struct.Written.html b/pr/509/docs/noq_proto/struct.Written.html new file mode 100644 index 000000000..42918ae76 --- /dev/null +++ b/pr/509/docs/noq_proto/struct.Written.html @@ -0,0 +1,38 @@ +Written in noq_proto - Rust

Written

Struct Written 

Source
pub struct Written {
+    pub bytes: usize,
+    pub chunks: usize,
+}
Expand description

Indicates how many bytes and chunks had been transferred in a write operation

+

Fields§

§bytes: usize

The amount of bytes which had been written

+
§chunks: usize

The amount of full chunks which had been written

+

If a chunk was only partially written, it will not be counted by this field.

+

Trait Implementations§

Source§

impl Clone for Written

Source§

fn clone(&self) -> Written

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Written

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Written

Source§

fn default() -> Written

Returns the “default value” for a type. Read more
Source§

impl PartialEq for Written

Source§

fn eq(&self, other: &Written) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for Written

Source§

impl Eq for Written

Source§

impl StructuralPartialEq for Written

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/token/struct.NoneTokenLog.html b/pr/509/docs/noq_proto/token/struct.NoneTokenLog.html new file mode 100644 index 000000000..e684fd3a7 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/token/struct.NoneTokenStore.html b/pr/509/docs/noq_proto/token/struct.NoneTokenStore.html new file mode 100644 index 000000000..5a0f21893 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/token/struct.TokenReuseError.html b/pr/509/docs/noq_proto/token/struct.TokenReuseError.html new file mode 100644 index 000000000..d97456b89 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/token/trait.TokenLog.html b/pr/509/docs/noq_proto/token/trait.TokenLog.html new file mode 100644 index 000000000..f938fa057 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/token/trait.TokenStore.html b/pr/509/docs/noq_proto/token/trait.TokenStore.html new file mode 100644 index 000000000..6fc644c89 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/token_memory_cache/struct.TokenMemoryCache.html b/pr/509/docs/noq_proto/token_memory_cache/struct.TokenMemoryCache.html new file mode 100644 index 000000000..2bf2b5dd8 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/trait.ConnectionIdGenerator.html b/pr/509/docs/noq_proto/trait.ConnectionIdGenerator.html new file mode 100644 index 000000000..2b1eaf79d --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/trait.ConnectionIdParser.html b/pr/509/docs/noq_proto/trait.ConnectionIdParser.html new file mode 100644 index 000000000..2d9346733 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/trait.NetworkChangeHint.html b/pr/509/docs/noq_proto/trait.NetworkChangeHint.html new file mode 100644 index 000000000..ee9b94429 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/trait.QlogFactory.html b/pr/509/docs/noq_proto/trait.QlogFactory.html new file mode 100644 index 000000000..276659d23 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/trait.TimeSource.html b/pr/509/docs/noq_proto/trait.TimeSource.html new file mode 100644 index 000000000..50b677c9b --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/trait.TokenLog.html b/pr/509/docs/noq_proto/trait.TokenLog.html new file mode 100644 index 000000000..5ec6afc2f --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/trait.TokenStore.html b/pr/509/docs/noq_proto/trait.TokenStore.html new file mode 100644 index 000000000..6f46f6bb6 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/transport_error/struct.Code.html b/pr/509/docs/noq_proto/transport_error/struct.Code.html new file mode 100644 index 000000000..7df069c6f --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/transport_error/struct.Error.html b/pr/509/docs/noq_proto/transport_error/struct.Error.html new file mode 100644 index 000000000..9f626518c --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/transport_parameters/enum.Error.html b/pr/509/docs/noq_proto/transport_parameters/enum.Error.html new file mode 100644 index 000000000..4eec404eb --- /dev/null +++ b/pr/509/docs/noq_proto/transport_parameters/enum.Error.html @@ -0,0 +1,38 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/transport_parameters/index.html b/pr/509/docs/noq_proto/transport_parameters/index.html new file mode 100644 index 000000000..094d4f4f1 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/transport_parameters/sidebar-items.js b/pr/509/docs/noq_proto/transport_parameters/sidebar-items.js new file mode 100644 index 000000000..be0904c68 --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/transport_parameters/struct.TransportParameters.html b/pr/509/docs/noq_proto/transport_parameters/struct.TransportParameters.html new file mode 100644 index 000000000..c40bae122 --- /dev/null +++ b/pr/509/docs/noq_proto/transport_parameters/struct.TransportParameters.html @@ -0,0 +1,38 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/noq_proto/varint/struct.VarInt.html b/pr/509/docs/noq_proto/varint/struct.VarInt.html new file mode 100644 index 000000000..480d9e5ac --- /dev/null +++ b/pr/509/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/509/docs/noq_proto/varint/struct.VarIntBoundsExceeded.html b/pr/509/docs/noq_proto/varint/struct.VarIntBoundsExceeded.html new file mode 100644 index 000000000..514f3961b --- /dev/null +++ b/pr/509/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/509/docs/noq_udp/all.html b/pr/509/docs/noq_udp/all.html new file mode 100644 index 000000000..45414c58e --- /dev/null +++ b/pr/509/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/509/docs/noq_udp/constant.BATCH_SIZE.html b/pr/509/docs/noq_udp/constant.BATCH_SIZE.html new file mode 100644 index 000000000..a6dcb01f0 --- /dev/null +++ b/pr/509/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/509/docs/noq_udp/enum.EcnCodepoint.html b/pr/509/docs/noq_udp/enum.EcnCodepoint.html new file mode 100644 index 000000000..4d9005819 --- /dev/null +++ b/pr/509/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/509/docs/noq_udp/imp/struct.UdpSocketState.html b/pr/509/docs/noq_udp/imp/struct.UdpSocketState.html new file mode 100644 index 000000000..ada8b24e1 --- /dev/null +++ b/pr/509/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/509/docs/noq_udp/index.html b/pr/509/docs/noq_udp/index.html new file mode 100644 index 000000000..14b244b89 --- /dev/null +++ b/pr/509/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/509/docs/noq_udp/sidebar-items.js b/pr/509/docs/noq_udp/sidebar-items.js new file mode 100644 index 000000000..d1db6b942 --- /dev/null +++ b/pr/509/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/509/docs/noq_udp/struct.RecvMeta.html b/pr/509/docs/noq_udp/struct.RecvMeta.html new file mode 100644 index 000000000..542a6029f --- /dev/null +++ b/pr/509/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/509/docs/noq_udp/struct.Transmit.html b/pr/509/docs/noq_udp/struct.Transmit.html new file mode 100644 index 000000000..cf2508455 --- /dev/null +++ b/pr/509/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/509/docs/noq_udp/struct.UdpSockRef.html b/pr/509/docs/noq_udp/struct.UdpSockRef.html new file mode 100644 index 000000000..48148268b --- /dev/null +++ b/pr/509/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/509/docs/noq_udp/struct.UdpSocketState.html b/pr/509/docs/noq_udp/struct.UdpSocketState.html new file mode 100644 index 000000000..865b979f0 --- /dev/null +++ b/pr/509/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/509/docs/perf/all.html b/pr/509/docs/perf/all.html new file mode 100644 index 000000000..98ab50b67 --- /dev/null +++ b/pr/509/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/509/docs/perf/client/fn.run.html b/pr/509/docs/perf/client/fn.run.html new file mode 100644 index 000000000..a919a3a96 --- /dev/null +++ b/pr/509/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/509/docs/perf/client/index.html b/pr/509/docs/perf/client/index.html new file mode 100644 index 000000000..5e45b5396 --- /dev/null +++ b/pr/509/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/509/docs/perf/client/sidebar-items.js b/pr/509/docs/perf/client/sidebar-items.js new file mode 100644 index 000000000..3dceadfac --- /dev/null +++ b/pr/509/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/509/docs/perf/client/struct.Opt.html b/pr/509/docs/perf/client/struct.Opt.html new file mode 100644 index 000000000..634617d0c --- /dev/null +++ b/pr/509/docs/perf/client/struct.Opt.html @@ -0,0 +1,43 @@ +Opt in perf::client - Rust

Opt

Struct Opt 

Source
pub struct Opt { /* private fields */ }
Expand description

Connects to a QUIC perf server and maintains a specified pattern of requests until interrupted

+

Trait Implementations§

Source§

impl Args for Opt

Source§

fn group_id() -> Option<Id>

Report the [ArgGroup::id][crate::ArgGroup::id] for this set of arguments
Source§

fn augment_args<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate Self via +[FromArgMatches::from_arg_matches_mut] Read more
Source§

fn augment_args_for_update<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate self via +[FromArgMatches::update_from_arg_matches_mut] Read more
Source§

impl CommandFactory for Opt

Source§

fn command<'b>() -> Command

Build a [Command] that can instantiate Self. Read more
Source§

fn command_for_update<'b>() -> Command

Build a [Command] that can update self. Read more
Source§

impl FromArgMatches for Opt

Source§

fn from_arg_matches(__clap_arg_matches: &ArgMatches) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn from_arg_matches_mut( + __clap_arg_matches: &mut ArgMatches, +) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn update_from_arg_matches( + &mut self, + __clap_arg_matches: &ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

fn update_from_arg_matches_mut( + &mut self, + __clap_arg_matches: &mut ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

impl Parser for Opt

§

fn parse() -> Self

Parse from std::env::args_os(), [exit][Error::exit] on error.
§

fn try_parse() -> Result<Self, Error>

Parse from std::env::args_os(), return Err on error.
§

fn parse_from<I, T>(itr: I) -> Self
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, [exit][Error::exit] on error.
§

fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, return Err on error.
§

fn update_from<I, T>(&mut self, itr: I)
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, [exit][Error::exit] on error. Read more
§

fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, return Err on error.

Auto Trait Implementations§

§

impl Freeze for Opt

§

impl RefUnwindSafe for Opt

§

impl Send for Opt

§

impl Sync for Opt

§

impl Unpin for Opt

§

impl UnwindSafe for Opt

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/perf/enum.CongestionAlgorithm.html b/pr/509/docs/perf/enum.CongestionAlgorithm.html new file mode 100644 index 000000000..56d2fa5c9 --- /dev/null +++ b/pr/509/docs/perf/enum.CongestionAlgorithm.html @@ -0,0 +1,30 @@ +CongestionAlgorithm in perf - Rust

CongestionAlgorithm

Enum CongestionAlgorithm 

Source
pub enum CongestionAlgorithm {
+    Cubic,
+    Bbr,
+    NewReno,
+}

Variants§

§

Cubic

§

Bbr

§

NewReno

Implementations§

Source§

impl CongestionAlgorithm

Source

pub fn build(self) -> Arc<dyn ControllerFactory + Send + Sync + 'static>

Trait Implementations§

Source§

impl Clone for CongestionAlgorithm

Source§

fn clone(&self) -> CongestionAlgorithm

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl ValueEnum for CongestionAlgorithm

Source§

fn value_variants<'a>() -> &'a [Self]

All possible argument values, in display order.
Source§

fn to_possible_value<'a>(&self) -> Option<PossibleValue>

The canonical argument value. Read more
§

fn from_str(input: &str, ignore_case: bool) -> Result<Self, String>

Parse an argument into Self.
Source§

impl Copy for CongestionAlgorithm

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/perf/fn.parse_byte_size.html b/pr/509/docs/perf/fn.parse_byte_size.html new file mode 100644 index 000000000..6d4afc897 --- /dev/null +++ b/pr/509/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/509/docs/perf/index.html b/pr/509/docs/perf/index.html new file mode 100644 index 000000000..754aa4966 --- /dev/null +++ b/pr/509/docs/perf/index.html @@ -0,0 +1 @@ +perf - Rust

Crate perf

\ No newline at end of file diff --git a/pr/509/docs/perf/noprotection/index.html b/pr/509/docs/perf/noprotection/index.html new file mode 100644 index 000000000..e8a673713 --- /dev/null +++ b/pr/509/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/509/docs/perf/noprotection/sidebar-items.js b/pr/509/docs/perf/noprotection/sidebar-items.js new file mode 100644 index 000000000..7ed1562e0 --- /dev/null +++ b/pr/509/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/509/docs/perf/noprotection/struct.NoProtectionClientConfig.html b/pr/509/docs/perf/noprotection/struct.NoProtectionClientConfig.html new file mode 100644 index 000000000..c293660de --- /dev/null +++ b/pr/509/docs/perf/noprotection/struct.NoProtectionClientConfig.html @@ -0,0 +1,31 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/perf/noprotection/struct.NoProtectionServerConfig.html b/pr/509/docs/perf/noprotection/struct.NoProtectionServerConfig.html new file mode 100644 index 000000000..64ee9a2e2 --- /dev/null +++ b/pr/509/docs/perf/noprotection/struct.NoProtectionServerConfig.html @@ -0,0 +1,39 @@ +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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/perf/server/fn.run.html b/pr/509/docs/perf/server/fn.run.html new file mode 100644 index 000000000..e1e250aae --- /dev/null +++ b/pr/509/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/509/docs/perf/server/index.html b/pr/509/docs/perf/server/index.html new file mode 100644 index 000000000..de30e6257 --- /dev/null +++ b/pr/509/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/509/docs/perf/server/sidebar-items.js b/pr/509/docs/perf/server/sidebar-items.js new file mode 100644 index 000000000..3dceadfac --- /dev/null +++ b/pr/509/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/509/docs/perf/server/struct.Opt.html b/pr/509/docs/perf/server/struct.Opt.html new file mode 100644 index 000000000..50e54dddb --- /dev/null +++ b/pr/509/docs/perf/server/struct.Opt.html @@ -0,0 +1,42 @@ +Opt in perf::server - Rust

Opt

Struct Opt 

Source
pub struct Opt { /* private fields */ }

Trait Implementations§

Source§

impl Args for Opt

Source§

fn group_id() -> Option<Id>

Report the [ArgGroup::id][crate::ArgGroup::id] for this set of arguments
Source§

fn augment_args<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate Self via +[FromArgMatches::from_arg_matches_mut] Read more
Source§

fn augment_args_for_update<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate self via +[FromArgMatches::update_from_arg_matches_mut] Read more
Source§

impl CommandFactory for Opt

Source§

fn command<'b>() -> Command

Build a [Command] that can instantiate Self. Read more
Source§

fn command_for_update<'b>() -> Command

Build a [Command] that can update self. Read more
Source§

impl FromArgMatches for Opt

Source§

fn from_arg_matches(__clap_arg_matches: &ArgMatches) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn from_arg_matches_mut( + __clap_arg_matches: &mut ArgMatches, +) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn update_from_arg_matches( + &mut self, + __clap_arg_matches: &ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

fn update_from_arg_matches_mut( + &mut self, + __clap_arg_matches: &mut ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

impl Parser for Opt

§

fn parse() -> Self

Parse from std::env::args_os(), [exit][Error::exit] on error.
§

fn try_parse() -> Result<Self, Error>

Parse from std::env::args_os(), return Err on error.
§

fn parse_from<I, T>(itr: I) -> Self
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, [exit][Error::exit] on error.
§

fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, return Err on error.
§

fn update_from<I, T>(&mut self, itr: I)
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, [exit][Error::exit] on error. Read more
§

fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, return Err on error.

Auto Trait Implementations§

§

impl Freeze for Opt

§

impl RefUnwindSafe for Opt

§

impl Send for Opt

§

impl Sync for Opt

§

impl Unpin for Opt

§

impl UnwindSafe for Opt

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/perf/sidebar-items.js b/pr/509/docs/perf/sidebar-items.js new file mode 100644 index 000000000..317d83630 --- /dev/null +++ b/pr/509/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/509/docs/perf/static.PERF_CIPHER_SUITES.html b/pr/509/docs/perf/static.PERF_CIPHER_SUITES.html new file mode 100644 index 000000000..859ee319e --- /dev/null +++ b/pr/509/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/509/docs/perf/stats/index.html b/pr/509/docs/perf/stats/index.html new file mode 100644 index 000000000..023e188b0 --- /dev/null +++ b/pr/509/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/509/docs/perf/stats/sidebar-items.js b/pr/509/docs/perf/stats/sidebar-items.js new file mode 100644 index 000000000..5c7b90d68 --- /dev/null +++ b/pr/509/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/509/docs/perf/stats/struct.OpenStreamStats.html b/pr/509/docs/perf/stats/struct.OpenStreamStats.html new file mode 100644 index 000000000..220db2691 --- /dev/null +++ b/pr/509/docs/perf/stats/struct.OpenStreamStats.html @@ -0,0 +1,35 @@ +OpenStreamStats in perf::stats - Rust

OpenStreamStats

Struct OpenStreamStats 

Source
pub struct OpenStreamStats(/* private fields */);
Expand description

Statistics for the currently open streams

+

Implementations§

Source§

impl OpenStreamStats

Source

pub fn new_sender( + &self, + stream: &SendStream, + upload_size: u64, +) -> Arc<StreamStats>

Source

pub fn new_receiver( + &self, + stream: &RecvStream, + download_size: u64, +) -> Arc<StreamStats>

Trait Implementations§

Source§

impl Clone for OpenStreamStats

Source§

fn clone(&self) -> OpenStreamStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Default for OpenStreamStats

Source§

fn default() -> OpenStreamStats

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/perf/stats/struct.Stats.html b/pr/509/docs/perf/stats/struct.Stats.html new file mode 100644 index 000000000..e7e1b225c --- /dev/null +++ b/pr/509/docs/perf/stats/struct.Stats.html @@ -0,0 +1,24 @@ +Stats in perf::stats - Rust

Stats

Struct Stats 

Source
pub struct Stats { /* private fields */ }

Implementations§

Source§

impl Stats

Source

pub fn on_interval(&mut self, start: Instant, stream_stats: &OpenStreamStats)

Source

pub fn print(&self)

Source

pub fn print_json(&self, path: &Path) -> Result<()>

Trait Implementations§

Source§

impl Default for Stats

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl Freeze for Stats

§

impl RefUnwindSafe for Stats

§

impl Send for Stats

§

impl Sync for Stats

§

impl Unpin for Stats

§

impl UnwindSafe for Stats

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/perf/stats/struct.StreamStats.html b/pr/509/docs/perf/stats/struct.StreamStats.html new file mode 100644 index 000000000..3f151a40e --- /dev/null +++ b/pr/509/docs/perf/stats/struct.StreamStats.html @@ -0,0 +1,24 @@ +StreamStats in perf::stats - Rust

StreamStats

Struct StreamStats 

Source
pub struct StreamStats { /* private fields */ }

Implementations§

Source§

impl StreamStats

Source

pub fn on_first_byte(&self, latency: Duration)

Source

pub fn on_bytes(&self, bytes: usize)

Source

pub fn finish(&self, duration: Duration)

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/perf/struct.CommonOpt.html b/pr/509/docs/perf/struct.CommonOpt.html new file mode 100644 index 000000000..001c0a64e --- /dev/null +++ b/pr/509/docs/perf/struct.CommonOpt.html @@ -0,0 +1,84 @@ +CommonOpt in perf - Rust

CommonOpt

Struct CommonOpt 

Source
pub struct CommonOpt {
Show 14 fields + pub send_buffer_size: u64, + pub recv_buffer_size: u64, + pub conn_stats: bool, + pub keylog: bool, + pub initial_mtu: u16, + pub no_protection: bool, + pub initial_rtt: Option<u64>, + pub ack_frequency: bool, + pub cong_alg: Option<CongestionAlgorithm>, + pub stream_receive_window: Option<u64>, + pub receive_window: Option<u64>, + pub send_window: Option<u64>, + pub max_udp_payload_size: u16, + pub qlog_dir: Option<PathBuf>, +
}

Fields§

§send_buffer_size: u64

Send buffer size in bytes

+

This can use SI suffixes for sizes. For example, 1M will request +1MiB, 10G will request 10GiB.

+
§recv_buffer_size: u64

Receive buffer size in bytes

+

This can use SI suffixes for sizes. For example, 1M will request +1MiB, 10G will request 10GiB.

+
§conn_stats: bool

Whether to print connection statistics

+
§keylog: bool

Perform NSS-compatible TLS key logging to the file specified in SSLKEYLOGFILE.

+
§initial_mtu: u16

UDP payload size that the network must be capable of carrying

+
§no_protection: bool

Disable packet encryption/decryption (for debugging purpose)

+
§initial_rtt: Option<u64>

The initial round-trip-time (in msecs)

+
§ack_frequency: bool

Ack Frequency mode

+
§cong_alg: Option<CongestionAlgorithm>

Congestion algorithm to use

+
§stream_receive_window: Option<u64>

Maximum number of bytes the peer may transmit without acknowledgement on any one stream +before becoming blocked.

+

This can use SI suffixes for sizes. For example, 1M will limit to +1MiB, 10G will limit to 10GiB.

+
§receive_window: Option<u64>

Maximum number of bytes the peer may transmit across all streams of a connection before +becoming blocked.

+

This can use SI suffixes for sizes. For example, 1M will limit to +1MiB, 10G will limit to 10GiB.

+
§send_window: Option<u64>

Maximum number of bytes to transmit to a peer without acknowledgment

+

This can use SI suffixes for sizes. For example, 1M will limit to +1MiB, 10G will limit to 10GiB.

+
§max_udp_payload_size: u16

Max UDP payload size in bytes

+
§qlog_dir: Option<PathBuf>

qlog output directory

+

Alternatively you can set the QLOGDIR environment variable.

+

Implementations§

Trait Implementations§

Source§

impl Args for CommonOpt

Source§

fn group_id() -> Option<Id>

Report the [ArgGroup::id][crate::ArgGroup::id] for this set of arguments
Source§

fn augment_args<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate Self via +[FromArgMatches::from_arg_matches_mut] Read more
Source§

fn augment_args_for_update<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate self via +[FromArgMatches::update_from_arg_matches_mut] Read more
Source§

impl CommandFactory for CommonOpt

Source§

fn command<'b>() -> Command

Build a [Command] that can instantiate Self. Read more
Source§

fn command_for_update<'b>() -> Command

Build a [Command] that can update self. Read more
Source§

impl FromArgMatches for CommonOpt

Source§

fn from_arg_matches(__clap_arg_matches: &ArgMatches) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn from_arg_matches_mut( + __clap_arg_matches: &mut ArgMatches, +) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn update_from_arg_matches( + &mut self, + __clap_arg_matches: &ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

fn update_from_arg_matches_mut( + &mut self, + __clap_arg_matches: &mut ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

impl Parser for CommonOpt

§

fn parse() -> Self

Parse from std::env::args_os(), [exit][Error::exit] on error.
§

fn try_parse() -> Result<Self, Error>

Parse from std::env::args_os(), return Err on error.
§

fn parse_from<I, T>(itr: I) -> Self
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, [exit][Error::exit] on error.
§

fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, return Err on error.
§

fn update_from<I, T>(&mut self, itr: I)
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, [exit][Error::exit] on error. Read more
§

fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, return Err on error.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/509/docs/search.index/021c1e79bdd9.js b/pr/509/docs/search.index/021c1e79bdd9.js new file mode 100644 index 000000000..e4765d947 --- /dev/null +++ b/pr/509/docs/search.index/021c1e79bdd9.js @@ -0,0 +1 @@ +rn_("AQIA9K8RAAABAAEAAQABCgDzqRMAAAEAAQDzgQJtdAEGAPNxEQAAAQABAAGCArBgEzIAAbDQFEcAAWlyE4ACsBANMwABZWkBBQD06RMAAAEASQABAPMEAWPy9BIAAAEAI4ICsFASxgABZnQBAAGwABQ+AAFk9FoMAAABAAEAAQDzgQJlaTcGAACJsTAUZAABoGAAABFwoFAAABEFsJATBQABsEARAgABtRAHAR4UAAA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/02804be949b0.js b/pr/509/docs/search.index/02804be949b0.js new file mode 100644 index 000000000..a9f4edf38 --- /dev/null +++ b/pr/509/docs/search.index/02804be949b0.js @@ -0,0 +1 @@ +rn_("AQkA810UAAABADUAAYACsCAT/wABoCAAAA/laXkBCQGggAAAFJBmAX8TAAABAwGwABQUAExzAb4TAAAjgAKgIAAAD+FhcgECAPXeEAAAAQABAEcCAQDzAANpdHU6MAAAAQAAAAAADQAQAAAACgixCUQQvBDdEhUTFhPCE8MTyRPzE/QTEhQTFAGAAqAwAAAUQLCwE5sAAWFsAQwBoKAAABShd/KRFAAAAQABAQDzBxAAAAEAAQDzgAJ0dQEDAPOwEgAAAQABAAEBAPU/EQAAAQABAAEAAQAjgAKwkBPrAAFhdAEEAaBAAAAURGXyZBQAAAEAAQIAOzAAAAEAAAsABQBlEAUAvxIBAOUTAQA3FAAAVhQAABOCArDwFJQAAWZnAYECsIAUawABoGAAABSPZHMBCAD1JRQAAAEAAQABAAEA8wIBZvWhEAAAAQABAAEAAQAjgAOgIAAAD9+woBRpAAFkcnR37wEAjbAQFB8AAbDgFCEAAZfVDjswAAABAAAdARIATAQBAFYFAgA+BgIASwcEAHsIAwCCDAIArgwBAC4NAQDLDuwA/Q8BAL0QAQDOEAIA+RABAIoRAADgEQEA0BIAAI0TBwB5FAMA") \ No newline at end of file diff --git a/pr/509/docs/search.index/0452ffab0bec.js b/pr/509/docs/search.index/0452ffab0bec.js new file mode 100644 index 000000000..7fbe96578 --- /dev/null +++ b/pr/509/docs/search.index/0452ffab0bec.js @@ -0,0 +1 @@ +rn_("AQUA9OkTAAABAEkAAQDzAwFj8vQSAAABACOAArBwFJQAAWhvAYICsAAS0QABsAAR4gABZXNDAAOgoAAAFDigIAAAE3llaXT15BIAAAEAhAABAO4AAYACsHASyAABoLAAABP+ZW8BAgA7MAAAAQAABgABAGMRBgABAgGgQAAAFB1l8kcUAAABAPOAAmlvAQgA9OATAAABAIIABQATAQOwkBRCAAGwUBMrAVZnbHA7MAAAAQAAEgAJAHUQAgDlEQEAGhMBADsTAQBwEwEA0RMBAAgUAQAXFAEAVBQBAAGAArBAEqgBC6AgAAARKGFpAYECsDAT8QABsDASxgABY2kBAwGwABMVAAFzOzAAAAEAAA0AAwDgEQEAjRMHAHkUAwABAwD13hEAAAEAGgEBAFMB84ACcnYBAAA7MAAAAQAABQACAKYSAQD7EgMA8wIBZDswAAABAAAJAAMA2hEDAKESAwDTEgEAI4ACoJAAABQ3aW8TgAKw8BSGAAFsbwEFAPRTFAAADAAeAA8AI4ACoGAAABNvZ3MjgAKwkBQ1AAFibPcXAwCPsHAR+AABsGAUaQABsHAT4wABsGATKQABsFATFwABtfkXOzAAAAEAADwAGABNBgAA9QcBAHQJBQCECQAAtAkAAGwMAAC1DAEAKQ0CAFINAQDEDgMAyQ4BAAoQBADTEAEADBEAAC8RAABPEQkAixEAAI0RAwDYEQAAIRMAADgTAQBJEwUAWRMAAAsUAAA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/04e28bec524a.js b/pr/509/docs/search.index/04e28bec524a.js new file mode 100644 index 000000000..713363eeb --- /dev/null +++ b/pr/509/docs/search.index/04e28bec524a.js @@ -0,0 +1 @@ +rn_("AQkA810UAAABADUAAQwBoKAAABShd/KRFAAAAQABAQDzBxAAAAEAAQDzgAJ0dQGBArCAFGsAAaBgAAAUj2RzNwIAg4agQAAAFECwYBQUAEywMBP/AAECkA4BEgD0eRQAAAEAAQABAAEAxqBQAAAS0KAAAAAQoLAgEU0AAaBQAAAR8KEAAAAUdKBAAAAUHgADAgs7MAAAAQAAFQAHAEUGAACKDAAAMg0AABYQDQBvEQAA5hIBABkUAQABAQGwsBRmAAFsOzAAAAEAAAwAAwCxBwgAUA0BAFQQAQABAQGgMAAAESho84QJAAAwACQIAQIA9M8RAAABAAEAAQATgQKwIBHTAAFhc/OCAm5v9wEAgIegIAAABkSgQAAADHMVAQc=") \ No newline at end of file diff --git a/pr/509/docs/search.index/0504377ac740.js b/pr/509/docs/search.index/0504377ac740.js new file mode 100644 index 000000000..aaad90773 --- /dev/null +++ b/pr/509/docs/search.index/0504377ac740.js @@ -0,0 +1 @@ +rn_("AYECsDAQSgABsBATowABc3QBAwDzvBIAAAEAAQAjgAKwABG7AAFoaQEAADswAAABAAATAAEAEQgTACOAAqAwAAANJ2Fl84ACcnTzAAJhbDswAAABAAAoAAMAigATAI4BAAAwEBMA") \ No newline at end of file diff --git a/pr/509/docs/search.index/062704961ddf.js b/pr/509/docs/search.index/062704961ddf.js new file mode 100644 index 000000000..6a573be2e --- /dev/null +++ b/pr/509/docs/search.index/062704961ddf.js @@ -0,0 +1 @@ +rn_("AQIBsIATzwABdAGXCQAAAYECoBAAABMxoHAAABN2YWPzAQFy8qkRAAAYAPOAAmVpI4ACsAAMbwABZHLzAAFl9IQAAAC3DOYGAQA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/066d6a9a769d.js b/pr/509/docs/search.index/066d6a9a769d.js new file mode 100644 index 000000000..4eae1259a --- /dev/null +++ b/pr/509/docs/search.index/066d6a9a769d.js @@ -0,0 +1 @@ +rn_("AQQA9FMUAAAMAB4ADwABAgGwQBKuAAFlAd0PAAABgAKgkAAAFBmgkAAAFBoyM5cCAADGoCAAAAzIoGAAABFFsGANUgABAWQAAzswAAABAAAKAAMApQcFAJcMAABjEwMA") \ No newline at end of file diff --git a/pr/509/docs/search.index/06c602595384.js b/pr/509/docs/search.index/06c602595384.js new file mode 100644 index 000000000..a2803193d --- /dev/null +++ b/pr/509/docs/search.index/06c602595384.js @@ -0,0 +1 @@ +rn_("AQMBgBNpABDuczswAAABAAAFAAEASRMFACOCAqCQAAAUQWlzAQEA9BETAAABAI0AAQAjgAKwoBPxAAFjbgGEArCgE/8AAbCgFAEAAWNyAQIAOzAAAAEAAAYAAQBrEAYAE4ICoCAAABPiZXUBAgDzxREAAAEAAQABgAKwIBQhAAGgYAAAE3ZvdmOBA6AQAAATMWFjcwEDAPQjDQAAAQABAAEAAQEA86AJAAABAAEAAQEA9P8QAAABAJgAAQBTgAOgwAAAFE1ucHUBgAKwMBAnAAGhYAAAFIFhZQGBArBAErYAAbBAErgAATQ2AQUBoFAAABQKaPKqEgAAAQABBQD0gRAAAAEApwABAHOAhLAwE1wAAQGgBAEIAPNYEwAACAAaABOAArBAEeoAAWFlAQABoCAAABCecgFfEQAAAQAA9LATAAABAEsAAQCnAACAhaAwAAAQ/KAQAAARG7CgFBUAAQkgIQEJAaBAAAAURGXyZBQAAAEAQ4CEoCAAABRSoJAAABOvoTAAABRjEEkAAQIA9EUTAAABAJIAAQATgAKwQBMTAAFpeQEBAqAwAAAS6qBAAAATMGdyOzAAAAEAAAcAAgD9EAEAkhEFAAGAArAAE8oAAbAAE8wAATQ2AQYA9I0UAAABAAoAAQDzAgJydgH2EwAA8wIBZfSKEAAAAQClAAEA84ACZHTnrT8AEqAwAAARHKBAAAANOLDwFIoAAbBwE3cAATBhYmNlZ2hpa2xub3Byc3R1djswAAABAAAEAUUAZQUAAEEGAABJBgMAUAcAALAHAADVBwIA5QcAAPcHBQD+BwAAYQgAAIgIAQCCCQEAiAkBAJUJAADwCQEA9AmEAF4MAQBrDAAAbwwBAHoMAACwDAAAvQwAAMMPAQDqDwEAARAAAGQQAACOEAEAoBAAAK0QAQDwEAEA9BADAAYRAQA9EQEAXREAAHoRAgCpEQAAwREAAOQRAADoEQEA7hEBAPoRBACXEgAAphIBAKkSAAC1EgAAxhIBAPsSAwAPEwEAHBMBACMTAQApEwEALBMAAC8TAABBEwAAVhMBAHQTAACCEwQAnRMBAK4TAACwEwEAthMBAMETAAD8EwIAAxQEAB8UAQArFAAAPhQBAGYUAQChFAAA") \ No newline at end of file diff --git a/pr/509/docs/search.index/06cb80e90e12.js b/pr/509/docs/search.index/06cb80e90e12.js new file mode 100644 index 000000000..4a5b8cb61 --- /dev/null +++ b/pr/509/docs/search.index/06cb80e90e12.js @@ -0,0 +1 @@ +rn_("AYMCsCARgwEToJAAABQMbXABggKwoBP/AAGwoBQBAAFjcgECAaBAAAARgmUBRxAAADOAhLAgEQIAAbAgD8oAAVAUAPOAAm9y") \ No newline at end of file diff --git a/pr/509/docs/search.index/0808e3817873.js b/pr/509/docs/search.index/0808e3817873.js new file mode 100644 index 000000000..ced9b5972 --- /dev/null +++ b/pr/509/docs/search.index/0808e3817873.js @@ -0,0 +1 @@ +rn_("AQUBsEASswABZfLVEgAAAQABggKgkAAAFBmgkAAAFBoyMwEAAqAgAAASj6AgAAAJe2ltOzAAAAEAAAwABgBABQAAdgUFAOgJAADHDwEA7xMAAAEUAQDzAAFzATIRAAABBQD0exMAAAEAAQABAAEAAPXODAAAAQABAAEAAQDzgAJna3OAhLCwE9MAARFIAAEGAaBAAAAR8WXzTw0AAMoDAQABgAOwUBQSAAGgEAAADFmgcAAAE2xnbnJHAQAAhaBgAAAT8KBQAAAUQaCgAAAT4hUBBDswAAABAAARAAQA3gEBAL8QCAC1EQMAsBICAAGFArBwE/wAAbAwE1QAAWVzEwADsEAP5gABoGAAABGLYmVzOzAAAAEAABIABABCBgEAVQcKAF0QAQCwEAMAAQMA9HkQAAABAAQAAQABCgGgQAAAFERl8mQUAAABABOAArBgEqoAAWNp84ACZHLzAAJlbzswAAABAAAJAAIAJwgFAGoIAwABAgA7MAAAAQAACQABAE8MCQABAAD0tBAAAAEAAQABAAEBADswAAABAADiAAIAXwYAAJAI4QBjAAOwEBMTAAFlaXM6MAAAAQAAAAAADAAQAAAAjgnGCccJyAnJCXQMdQy/D8AP5Q8BEXcTeBPjgISwIAfjAAERQAQBAYSwMAxiAAGgQAAADR+ggAAAEV6wQA0gAAEAAB0B7QEAAAEFAPXEEwAAAQABAHMAAQATgAKgAAAAB4lhZwEDAPSIEgAAAQBiAQEA84ACaW8BAQA7MAAAAQAA4gACAAEIAABtC+EAAQgBsEAURQABZfUNFAAAAQABAAEAAQDzgQJndAEBAaBwAAAR92k7MAAAAQAA9AACAAAIAAB5CvMAAQUA884QAAABAAEA84ACYW8TgAKgQAAADIpydQGAAqAAAAAH5aAQAAAJjWR4EwACsCANRgABZXLyJxAAAAEAE4ACsLAUigABcngBCgD0ghMAAAEAAQABABOAArAQERQCJmFlAQQA9LAQAAABAAEAAQABBAA6MAAAAQAAAAAABwAQAAAAJRMmEzQTNRO0E7UTxxPIEwEDAPPDEgAAAQCnANOAhLBAEQYAAQFIAce/AwCPoIAAABLFsGANNgABsEAMswABsTAUYQABP6kfOzAAAAEAAEsAIgBtAAAAcQAAAGEFAQBrBQAAHAYBAMsHAQDbBwAA3wcBAMAJAADTCQcA6gkBAGEMAAB3DAEAfgwBAI8MBwCzDAEAzAwBABYNAQD0DwEAYhABANoQAQDkEAEA/BAAAB0RAgAtEQEAPxEEAGIRAACvEQMA7BEBAKASAAD/EgEAAxMBADATAACGFAEA") \ No newline at end of file diff --git a/pr/509/docs/search.index/0920e65a8fea.js b/pr/509/docs/search.index/0920e65a8fea.js new file mode 100644 index 000000000..44791db35 --- /dev/null +++ b/pr/509/docs/search.index/0920e65a8fea.js @@ -0,0 +1 @@ +rn_("AYMCoFAAABEUoKAAABM6a3ABAAGwQBKIAAFs9OoJAAABAPkGAQABCAD0nBIAAAEAAQABAAEEAbAAEO4AAXMBYRAAAAECAaBQAAAS+GMBHQ0AALcAAACFsEANVwABoDAAAAxslQABOzAAAAEAAAgABABpBQAAMgYCACANAQDSDwIAJwEAgIWwMAmuAAGgEAAAE2+wIAgDAAGRAEQBEAD0eRQAAAEAAQABAAEDAPTqCQAAAQD5BgEAY4ADoAAAAAxsbnJ1AQACsNATsAABoDAAAA/VYmr2FgYAAAEAAQBIAAEAaQEBAAGwgBPPAAF0OzAAAAEAAEAABABeBgAAlwkAAKMJAADYDD0AAQkA87wSAAABAAEA84ADY2d0AYMCoJAAABQZoJAAABQaMjMBgAKgUAAAFEGggAAAE2xjZfMAAnN08ssHAAABAAEAAaCwAAATsmLzQhMAAB8BAQAjgAKgAAAAE0FkciOAAqBQAAAUC2Nl84ACaXkBBAD1PxEAAAEAAQABAAEA8wEBbgHBBwAAAQEAOzAAAAEAAAkAAgBwCAcAARMBAAEGAPSlEwAAAQABAAEAAQ8A9IIUAAABAAEAAQDzgQJobQEAAbBAFEUAAWX1DRQAAAEAAQABAAEAU4ADoCAAABHZbG50120AgMugIAAAFHSwYBDVAAGwQBAKAAHAGzgK") \ No newline at end of file diff --git a/pr/509/docs/search.index/094d93788e8b.js b/pr/509/docs/search.index/094d93788e8b.js new file mode 100644 index 000000000..01c5e0372 --- /dev/null +++ b/pr/509/docs/search.index/094d93788e8b.js @@ -0,0 +1 @@ +rn_("AQIA9BETAAABAI0AAQABAwD0RRMAAAEAkgABAPMFAmJ1OjAAAAEAAAAAAAcAEAAAAI4QjxA9ET4R6BHpERwTHRMBAgD0dBEAAAEAawEBAAEFAPN6EQAAAQABAAEEAPS6EgAAAQCjAAEA84CEEQkA") \ No newline at end of file diff --git a/pr/509/docs/search.index/0b1f340b7ad1.js b/pr/509/docs/search.index/0b1f340b7ad1.js new file mode 100644 index 000000000..bdae6fea8 --- /dev/null +++ b/pr/509/docs/search.index/0b1f340b7ad1.js @@ -0,0 +1 @@ +rn_("AQIA9e0SAAABAOkAeQABABOAArAwEx4BPGlwAQYA85cTAAABAAEA8wICYnMBTBAAAPMBAWby8QcAAFMJAQIA9EUTAAABAJIAAQAzgAOwQBMTAAFpdXk=") \ No newline at end of file diff --git a/pr/509/docs/search.index/0b471b6e9f68.js b/pr/509/docs/search.index/0b471b6e9f68.js new file mode 100644 index 000000000..e19a69044 --- /dev/null +++ b/pr/509/docs/search.index/0b471b6e9f68.js @@ -0,0 +1 @@ +rn_("AYACsHAUdQABoAAAAAVIZmcBAQA7MAAAAQAABwABAJkMBwABAQGgcAAAEpBhAZ8HAAAjgAKhAAAAFH1kbnOAhKBgAAASjwBBSA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/0b9e3bf94d8e.js b/pr/509/docs/search.index/0b9e3bf94d8e.js new file mode 100644 index 000000000..0f4c00ef1 --- /dev/null +++ b/pr/509/docs/search.index/0b9e3bf94d8e.js @@ -0,0 +1 @@ +rn_("AQEAOjAAAAEAAAAAAAcAEAAAADsTPBPRE9ITCBQJFFQUVRTzBgFpAVkTAAAjgAKwUBMvAFdmbgEAAbBAEwkAAWU7MAAAAQAADQACAGAHDAAQEQAAI4ACoDAAAAxuYWU3AACAxbBQE4AAAbAQFCEAAbBgE4cAAQACmAg=") \ No newline at end of file diff --git a/pr/509/docs/search.index/0ba637addba8.js b/pr/509/docs/search.index/0ba637addba8.js new file mode 100644 index 000000000..b284718d2 --- /dev/null +++ b/pr/509/docs/search.index/0ba637addba8.js @@ -0,0 +1 @@ +rn_("AQkA810UAAABADUAAYACsCAT/wABoCAAAA/laXkBCQGggAAAFJBmAX8TAAABAwGwABQUAExzAb4TAABDgAOwIA/hARqwUBKsAAFhb3IBAgD13hAAAAEAAQBHAgEA8wADaXR1OjAAAAEAAAAAAA4AEAAAAAoIsQlEELwQ2RHdEhUTFhPCE8MTyRPzE/QTEhQTFAEGAPT/EgAAAQCGAQEAEwICsGATAQABZm3yLA0AAAEAI4ICsEATLQABZWwBgAKgMAAAFECwsBObAAFhbAEGAPPvDwAA2QABAAEFAPTpEwAAAQBJAAEA8wIBY/L0EgAAAQABAgGgMAAAEINhAa0MAAABgAKwUBErAAGwIA/BAAFjbvOAAm50AQUAOzAAAAEAAA4AAgAyDQAAFhANAAECADswAAABAAAHAAEA0wkHAOcFAAAJoDAAAAysoLAAABQSoIAAABGpsNAUIQABMGFmaWttcnV2OzAAAAEAAEoCBQDdAQAAPgPhAAEIAABtC+EAPg6EAAEMAaCgAAAUoXfykRQAAAEAAQEA8wcQAAABAAEA84ACdHUBAwDzsBIAAAEAAQABAQD1PxEAAAEAAQABAAEAAQEA9vMHAAABAI4AeAelAOYBY4ADsJAT6wABYW10AQMBoEAAABREZfJkFAAAAQATgAKwYBSUAAFlcgEDAaAAAAATyXMB+hAAAAECADowAAABAAAAAAAKABAAAADRCdIJLRBaE1sTiROKEywUgBSWFJcUgwAEoIAAABKKsHARbQABsCARDwDoZm5ydgGBCAAAAYECoAAAAAmxoDAAAA/lc3THAQCAxrAAFB8AAbCQEogAAaAQAAAH2QASiCgBAgA7MAAAAQAADQAGAGUQBQBqEQEAvxIBAOUTAQA3FAAAVhQAABOCArDwFJQAAWZnAYACsIAUawABoGAAABSPZHMTgAKwwBOwAAFpbwEIAPUlFAAAAQABAAEAAQDzAgFm9aEQAAABAAEAAQABACOAA6AgAAAP37CgFGkAAWRydPf7BwCPoDAAAA/Vl9cPOzAAAAEAADUBHQBMBAEAVgUCABYGAgA+BgIAYAYBAEsHBADKBwAA4gcAAHsIAwCFCQIAtQkBAIIMAgCuDAEA0wwDAC4NAQDLDuwA/Q8BAHsQAgC9EAEAzhACAPkQAQCKEQAA4BEBAJISAADQEgAABxMBAG0TAQCNEwcAeRQDAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/0bb8ecd0000d.js b/pr/509/docs/search.index/0bb8ecd0000d.js new file mode 100644 index 000000000..50e78f3ae --- /dev/null +++ b/pr/509/docs/search.index/0bb8ecd0000d.js @@ -0,0 +1 @@ +rn_("AQoA9IsTAAABAA8AAQABgwKgIAAAEFigMAAAEOlpdAEAAPT7EgAAAQABAAEA8wQBZPShEgAAAQABAAEAI4aEsKAUNQABsEAS+QABsHAT4wABFYAAAQACsDAQSgABsBATowABc3TyMAYAAEMGAYACsMAUYQABoAAAAAfbdHnzgIURwQA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/0d3c240b6746.js b/pr/509/docs/search.index/0d3c240b6746.js new file mode 100644 index 000000000..662a88169 --- /dev/null +++ b/pr/509/docs/search.index/0d3c240b6746.js @@ -0,0 +1 @@ +rn_("AQwBoKAAABShd/KRFAAAAQABAQDzBxAAAAEAAQABiYSggAAAFI+gQAAAFECgYAAAFGCgkAAAFJMCgAoTgYSgoAAAE9WxoBSfAAGw0BQ5AAEEAQnzgANhdHU=") \ No newline at end of file diff --git a/pr/509/docs/search.index/0d40bbfaf512.js b/pr/509/docs/search.index/0d40bbfaf512.js new file mode 100644 index 000000000..e544f322a --- /dev/null +++ b/pr/509/docs/search.index/0d40bbfaf512.js @@ -0,0 +1 @@ +rn_("AQcA9HsTAAABAAEAAQDzBQF0OzAAAAEAAAgAAgBhDAAAjwwHAAEFAPOXEwAAAQABADcAAICFsDAQWQABoKAAABOyoDAAABPvEEFI") \ No newline at end of file diff --git a/pr/509/docs/search.index/0e7685c20a2a.js b/pr/509/docs/search.index/0e7685c20a2a.js new file mode 100644 index 000000000..e67018f03 --- /dev/null +++ b/pr/509/docs/search.index/0e7685c20a2a.js @@ -0,0 +1 @@ +rn_("AQ0A9cQTAAABAAEAcwABAAECAPQKEQAAAQCCAQEAAQMA9HQRAAABAGsBAQDzAAJidQEmEAAAAQMA9AgRAAABAIIBAQDzhAJkcwECAPOPCQAAAQBVAPOAAml0ARgA854UAAABAAEAI4ECsMATwgABY20BBQGgYAAAErVy9HsMAAABAKgDAQABCQD23hIAAAEA9gCCADEAAQATgAKwYBDMAAFkbgEDAaBwAAATGWf05gcAAAEA/AEBAAEJAPNdFAAAAQA1AAGBArCAFGsAAaBgAAAUj2RzFwEAiYWw0BSRAAGgQAAAFECwYBQUAEwCkApnXgAAi7BwES0AAbCwEycAAaCgAAAS3bDwE/MAAW2BD/R8AAAAAQABAAEA") \ No newline at end of file diff --git a/pr/509/docs/search.index/0ee8581ceb4b.js b/pr/509/docs/search.index/0ee8581ceb4b.js new file mode 100644 index 000000000..96593ab07 --- /dev/null +++ b/pr/509/docs/search.index/0ee8581ceb4b.js @@ -0,0 +1 @@ +rn_("AQIBsEAUWwABdDswAAABAAANAAYA6xIDAFQTAQC/EwEA1hMBAFAUAQB1FAEAE4ACsDATHgE8aXABBQDzlxMAAAEAAQATgAKgMAAAE+9leVMCA7BwE/wAAWJlcwFMEAAAEwACoCAAAAY8Zm32dwAAAHkHAQDLAQEAhwc=") \ No newline at end of file diff --git a/pr/509/docs/search.index/0f8ffae0cedd.js b/pr/509/docs/search.index/0f8ffae0cedd.js new file mode 100644 index 000000000..545e84790 --- /dev/null +++ b/pr/509/docs/search.index/0f8ffae0cedd.js @@ -0,0 +1 @@ +rn_("AQABoDAAABGIdPLKEAAAAQABgAKgAAAAEJygAAAAEJ00NvMDAXYBTA0AABcBAACHsKATCQABsCAMfgABsGATAwABsIAS6AABoCAAABEBCURGOjAAAAEAAAAAAAkAEAAAAJQHlQeWB4wMBRBeEZkSmhKbEj0U") \ No newline at end of file diff --git a/pr/509/docs/search.index/0fc7936ce68d.js b/pr/509/docs/search.index/0fc7936ce68d.js new file mode 100644 index 000000000..428fe27b6 --- /dev/null +++ b/pr/509/docs/search.index/0fc7936ce68d.js @@ -0,0 +1 @@ +rn_("AQEA8xMQAAABAAEAAQMBoEAAABQHZfJ3FAAAAQABAQGgIAAAEURi88QMAAABAAEAE4ACoBAAAAzHYWNzgISgUAAAD/oQQAw=") \ No newline at end of file diff --git a/pr/509/docs/search.index/10020ef0a247.js b/pr/509/docs/search.index/10020ef0a247.js new file mode 100644 index 000000000..aabb192cc --- /dev/null +++ b/pr/509/docs/search.index/10020ef0a247.js @@ -0,0 +1 @@ +rn_("AYACoCAAAA/hoDAAABO+YXLzAAF0AQoIAAABBAGgQAAAFERl8mQUAAABAAEFADswAAABAAALAAUAZRAFAL8SAQDlEwEANxQAAFYUAADjAISgMAAAD9+FAAI7MAAAAQAACwAFAD4GAgBLBwQArgwBAIoRAADQEgAA") \ No newline at end of file diff --git a/pr/509/docs/search.index/10071b17a468.js b/pr/509/docs/search.index/10071b17a468.js new file mode 100644 index 000000000..4d28eb356 --- /dev/null +++ b/pr/509/docs/search.index/10071b17a468.js @@ -0,0 +1 @@ +rn_("AQACoCAAABN5sDAR4gABaXTy5BIAAAEAAQMAOzAAAAEAAAYAAQBjEQYAAQgA9OATAAABAIIABQATAQOwkBRCAAGwUBMrAVZnbHA7MAAAAQAAEgAJAHUQAgDlEQEAGhMBADsTAQBwEwEA0RMBAAgUAQAXFAEAVBQBAAGAArAwEd4AAbAwEeAAAXJ2AQQBsAASpgABZDswAAABAAAFAAIA2hEDANMSAQC3BQABh7BQEqgBC7BgEykAAZQJAzowAAABAAAAAAALABAAAAApDSoNKw3JDsoODBGNEY4RjxGQESETWRMjgAKgEAAADShubwEDAPNyEAAAAQABACOAAqAAAAAMa2RyYwCEoFAAABGKoDAAABCrFAEQ8iUIAAABAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/1062f3057d85.js b/pr/509/docs/search.index/1062f3057d85.js new file mode 100644 index 000000000..39915378e --- /dev/null +++ b/pr/509/docs/search.index/1062f3057d85.js @@ -0,0 +1 @@ +rn_("AQMBsDATRwABY/OFEQAAAQABAAEDAYATaQAQ7nM7MAAAAQAABQABAEkTBQAjggKgkAAAFEFpcwcBAIDFsFATEwABoKAAABOvsHATdwABoNAAABRNgAEYCPOAAmRy") \ No newline at end of file diff --git a/pr/509/docs/search.index/10722fb15319.js b/pr/509/docs/search.index/10722fb15319.js new file mode 100644 index 000000000..3bdf88dce --- /dev/null +++ b/pr/509/docs/search.index/10722fb15319.js @@ -0,0 +1 @@ +rn_("AQUA9HsTAAABAAEAAQABAAD1zgwAAAEAAQABAAEA84ACZ2sTgAKwsBPTAAFhbwGFArBwE/wAAbAwE1QAAWVzEwADsEAP5gABoGAAABGLYmVzOzAAAAEAABIABABCBgEAVQcKAF0QAQCwEAMAAQMA9HkQAAABAAQAAQABCgGgQAAAFERl8mQUAAABABOAArBgEqoAAWNp84ACZHLzAAJlbzswAAABAAAJAAIAJwgFAGoIAwABAgA7MAAAAQAACQABAE8MCQDzgAJvdAEDADswAAABAADiAAIAAQgAAG0L4QABAgGgcAAAEfdpOzAAAAEAAPQAAgAACAAAeQrzABOAAqBAAAAMinJ1AQQA9DQTAAABAJIAAQABAwDzwxIAAAEApwDzgAJhb9cFAACIsEANRgABoDAAAAxZsTAUYQABNIEWOzAAAAEAAB4ADQBhBQEAHAYBAGEMAACPDAcAswwBAPQPAQDaEAEAYhEAAK8RAwDsEQEAoBIAAP8SAQCGFAEA") \ No newline at end of file diff --git a/pr/509/docs/search.index/116b929a1e0d.js b/pr/509/docs/search.index/116b929a1e0d.js new file mode 100644 index 000000000..794a75495 --- /dev/null +++ b/pr/509/docs/search.index/116b929a1e0d.js @@ -0,0 +1 @@ +rn_("AQ8A9AgUAAABAEsAAQABBAD0jBAAAAEArgABAAECADowAAABAAAAAAAJABAAAADWEdcR6BLpEs8T0BMjFCQUlBSVFPMAAW/y8gkAAAEAU4ADsQAUZgABaG9wY4ADsIAThwABY3N0") \ No newline at end of file diff --git a/pr/509/docs/search.index/11770572d7de.js b/pr/509/docs/search.index/11770572d7de.js new file mode 100644 index 000000000..88f3b0792 --- /dev/null +++ b/pr/509/docs/search.index/11770572d7de.js @@ -0,0 +1 @@ +rn_("AQQA88URAAABAAEAAYICsEAStgABsEASuAABNDYBBgD0jRQAAAEACgABAPMCAXIB9hMAAPMCAWX0ihAAAAEApQABABOAAqBQAAAS6mR00wCEsLAUFQABEQEB9/AQAAABAGwAHQABAAEALQE=") \ No newline at end of file diff --git a/pr/509/docs/search.index/11d9d1c24f11.js b/pr/509/docs/search.index/11d9d1c24f11.js new file mode 100644 index 000000000..6af09e1d4 --- /dev/null +++ b/pr/509/docs/search.index/11d9d1c24f11.js @@ -0,0 +1 @@ +rn_("AYICsEAUQgABsGATIwABZXIBBQD0phAAAAEAAQABAPOAAmZtAQYAOjAAAAEAAAAAAAgAEAAAAO0P7g+REd4S3xLVE1cUiBSJFAEAArCwE+0AAaFAAAAUgWVn9AYIAAABAAEAAQDDAISgsAAAE76wQBC9AAEUIAT3fgcAAJ4FPwMBAC0BHwELAWOAA7AwECcAAWFlaQ==") \ No newline at end of file diff --git a/pr/509/docs/search.index/12e2aa203dff.js b/pr/509/docs/search.index/12e2aa203dff.js new file mode 100644 index 000000000..bb7d56217 --- /dev/null +++ b/pr/509/docs/search.index/12e2aa203dff.js @@ -0,0 +1 @@ +rn_("AQAA9GMTAAABAAEAAQABBADz0g8AAAEAAQABgAKgUAAAENmgYAAAEUZpcgGAAqAgAAAMyKBgAAARRWVoAQEBoCAAABFEYvPEDAAAAQABABOBAqAQAAAMx2FjAQYA9P8SAAABAIYBAQATAAKwYBMBAAFmbfIsDQAAAQAjgAKgwAAAE8lucicPAACIoEAAAA/PsEAP0AABoDAAAAzDNCAXOzAAAAEAABkAAwDoAQAAMQQOAE8MCQABgAOwcBOAAAGwMBMtAAGwgBO2AAFjcnMBAAGwIBOAAAFvOjAAAAEAAAAAAAkAEAAAANYR1xHoEukSzxPQEyMUJBSUFJUUAYACsCAJpgABsDATOAABcnT3BAAAyKAgAAAH/6BgAAAP+rCAEzgAAUAGkgn29QEAAGAClwUBAPYJAQA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/136ec6b58ef2.js b/pr/509/docs/search.index/136ec6b58ef2.js new file mode 100644 index 000000000..4646823b7 --- /dev/null +++ b/pr/509/docs/search.index/136ec6b58ef2.js @@ -0,0 +1 @@ +rn_("AQQA9lQNAAABAAEDAQAhAQEAAQoA88MSAAABAKcAAQEA96QJAAABAOMGAQBpAAEADAEjgAKwYBPTAAFpbwECAPQWDQAAAQDsBQEAAQUA9OkTAAABAEkAAQD3BAAAh6AgAAANGLCQEfIAAQScAzowAAABAAAAAAAIABAAAAAuBpMJlAm6DLsMAhEDEfQS9RI=") \ No newline at end of file diff --git a/pr/509/docs/search.index/13bc5713853d.js b/pr/509/docs/search.index/13bc5713853d.js new file mode 100644 index 000000000..c8a08692f --- /dev/null +++ b/pr/509/docs/search.index/13bc5713853d.js @@ -0,0 +1 @@ +rn_("AYMCsQAUlgABsNAUiAABa20BBAGgoAAAFKF38pEUAAABAAELAPQDFAAAAQABAAEAI4ACoIAAABQrZW9jBAOgYAAAE8FlbnABfQwAAAEKAPPlEwAAAQBwABOAAqIQAAAUoWxvdwAAAIWgYAAAEoWhAAAAFFgEgQM7MAAAAQAAOwAEAFEGBQDaBwAALQgzAIQRAABDgAOwgBGDAROgAAAAAfZka3Q=") \ No newline at end of file diff --git a/pr/509/docs/search.index/14600ff5b785.js b/pr/509/docs/search.index/14600ff5b785.js new file mode 100644 index 000000000..839fd72dc --- /dev/null +++ b/pr/509/docs/search.index/14600ff5b785.js @@ -0,0 +1 @@ +rn_("AQEA9PIHAAD9B9kAAQABBQD0JRMAAAEAjgABAPMCAWP1YhAAAAEAugABAAEAYwAEoDAAAAyNoHAAABHZZW5zdvOAEAAAkwAPAg==") \ No newline at end of file diff --git a/pr/509/docs/search.index/1471da9f23be.js b/pr/509/docs/search.index/1471da9f23be.js new file mode 100644 index 000000000..0635ee1c5 --- /dev/null +++ b/pr/509/docs/search.index/1471da9f23be.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAYAAgBRBgUAhBEAAAEEAbAAExUAAXM7MAAAAQAACwACAI0TBwB5FAMARwAAAYaggAAAE2+xABSGAAGwQBLRAAGwgBSUAAGwUBMXAAEVABb0xA4AAAEAAQABAAEFAPMMEAAAAQABAGOAA7BAEJMAAW1ubwGBArAADTAAAbAQEJEAAXN18wACYWk6MAAAAQAAAAAABwAQAAAA8gfvD8gQyRB7E3wTfRN+EwGAA7AAE8oAAbAAE8wAAbBwFD4AATQ2YQEEAPSNFAAAAQAKAAEAI4ECoEAAABFdbXABAgA7MAAAAQAADgAEAPUHAQBSDQEATxEJAIsRAAABAQA7MAAAAQAACAADAMUSAAA2EwAAmhQGAAEBADswAAABAAAKAAMAcRECAKUTAwCCFAMAI4ACoDAAABBkZGfXAwAAxqCAAAASoEBGgAg7MAAAAQAAGAAJAAMQAQAWEQEASxEBAFkRAwDIEQYAqRMCAPYTAABOFAEAfhQBAAEDAPadCQAAUgCdBgEArgABABOAArBwFB8AAW9yAQQBsLAUZgABbPRQDQAAAQADAwEAAQIAOzAAAAEAAAwAAwB7CQAAuBMFAG0UBQABAQDzShAAAAEARAIjgAKgsAAAFB5negECADowAAABAAAAAAAJABAAAADWEdcR6BLpEs8T0BMjFCQUlBSVFPMAAW/y8gkAAAEAAQUA9PYPAAABAOUAAQCDAISgQAAADUWwcBNaAAGwIAzMAAEQBQI7MAAAAQAACQADAF0FAwAZBgIAxRECAAGAAqAgAAARJbAgFB8AAWR0AYQCsLAUaQABsAATwgABYXPTgISgsAAAFFJAKAQBgAKgMAAAEfCwYBAKAAFockf/AQDNoCAAAAY4oCAAAAY5sHATtgABA2c0BzswAAABAAA3ABgAQAUAAHYFBQDoCQAAHg0AAMcPAQC0EAMAbBEAAMUSAADXEgUANhMBAFgTAABgEwAAehMAAIsTAQCVEwAAlxMCAJsTAQDvEwAA9RMAAAEUAQAbFAEALRQFAEEUAACaFAYA") \ No newline at end of file diff --git a/pr/509/docs/search.index/1473e4141b70.js b/pr/509/docs/search.index/1473e4141b70.js new file mode 100644 index 000000000..58478954e --- /dev/null +++ b/pr/509/docs/search.index/1473e4141b70.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABBEc/OCDAAAAQABAAECAPOBDAAA3wQBACMAAqBAAAANOmVy8loFAAABAAGAAqBAAAATwbAwEuYAAWFzAQIBoEAAABREZfJkFAAAAQDzgARsbnJ2") \ No newline at end of file diff --git a/pr/509/docs/search.index/14e2eb248d40.js b/pr/509/docs/search.index/14e2eb248d40.js new file mode 100644 index 000000000..605ad9869 --- /dev/null +++ b/pr/509/docs/search.index/14e2eb248d40.js @@ -0,0 +1 @@ +rn_("AQAA9AoRAAABAIIBAQAjgQKgYAAAEyxiaQEAAPT/DwAAAQDoAP8AI4ICsFATPwABZXMBiQKwIBRQAAGgMAAAFFppcAECAPQREwAAAQCNAAEAAQMA9EUTAAABAJIAAQDzhQJidQECAPR0EQAAAQBrAQEA84ACaWy3AQAAhaAgAAATGRIBDDowAAABAAAAAAAUABAAAAD9B4oIiwiMCI0IbQyhDKIMowykDCYQ4RCWExQUXRReFGAUaxRsFI8UkxQBBQDz+RMAAAEAAQBDhgOwkBR1AAGgAAAAEqliZHcBgAKwAAmeAAGwIA1NAytkbgEDAPP9DwAAAQD7AAECAPZ0DAAAAQBKAwEAtwMBAAEAADswAAABAAAGAAIAdRMAANoTBQAjgQKwQBRLAAFlc1OAA7AACboAAWV4eQGAA6BAAAAUc7AgEw0AAbAACbgAAWVyeAGAAqBAAAANOLBAECcAAWdsJxoAAMmgkAAAE1mwwBOhAAGgEAAAEXCgMAAAE/CwQA0wAAEAR6ALOjAAAAEAAAAAAAkAEAAAAAoGCwY2DTcNSw35D1IQUxAFEwYTAQYA9A8TAAABAI0AAQDzAwFi9AgRAAABAIIBAQC3DAAAiaBwAAASl7CQEz0AAbBgEEoAAaAwAAATYjiJEzowAAABAAAAAAAtABAAAABaBlsGXAZdBr8HwAd+CX8JgAmyCbMJeQxCDUMNRA1IDUkNSg32D/cPSBBJEE0QThBrEGwQbRBuEG8QcBBxEJAQlRCvENwQ3RBJEUoR6hJ2E5oTvhNTFF8UfRSMFA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/14e8961c1d4b.js b/pr/509/docs/search.index/14e8961c1d4b.js new file mode 100644 index 000000000..f97514997 --- /dev/null +++ b/pr/509/docs/search.index/14e8961c1d4b.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAUAAQBtFAUA8w0BbTswAAABAAAFAAEALRQFAEcAAACFsAAUPgABsEASswABoQAAABRzoAAAABP1OAgCOzAAAAEAABgABgBaDAMAMxEHANUSAQBPEwQAJRQEAJAUAAA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/14f8d441cdf1.js b/pr/509/docs/search.index/14f8d441cdf1.js new file mode 100644 index 000000000..f067f7ea4 --- /dev/null +++ b/pr/509/docs/search.index/14f8d441cdf1.js @@ -0,0 +1 @@ +rn_("AQABoJAAABNgaPIpEwAAAQDzAgFk8qAHAAABAAECADswAAABAAAFAAEAbRQFAPMLAW07MAAAAQAABQABAC0UBQAjgAKwIA02AAFhbwECAPOBCQAAJAMBAAGKArAAE8oAAbAAE8wAATQ2I4ECoDAAAAzLZXIBgAKwQBHsAAEVqo1BpJVjdbcGAACHsBAQXwABsFAUSQABEVEJ9T8FAAAKAL4AAQBOBw==") \ No newline at end of file diff --git a/pr/509/docs/search.index/15aa8d41a495.js b/pr/509/docs/search.index/15aa8d41a495.js new file mode 100644 index 000000000..a1e473414 --- /dev/null +++ b/pr/509/docs/search.index/15aa8d41a495.js @@ -0,0 +1 @@ +rn_("AYMCsQAUlgABsNAUiAABa20BCgDz5RMAAAEAcAATgAKiEAAAFKFsbzMChKBgAAAShaEAAAAUWASAAzswAAABAAA0AAIA2gcAAC0IMwA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/15d86d685190.js b/pr/509/docs/search.index/15d86d685190.js new file mode 100644 index 000000000..aad378fc2 --- /dev/null +++ b/pr/509/docs/search.index/15d86d685190.js @@ -0,0 +1 @@ +rn_("AQAA9IEQAAABAKcAAQABhAKwABF9AAGwABF/AAE0NvMCAWHyyQwAAAEAAQEA9bUSAABuAAEAQgEBAPOAAmVv84ACaGs=") \ No newline at end of file diff --git a/pr/509/docs/search.index/166712222acb.js b/pr/509/docs/search.index/166712222acb.js new file mode 100644 index 000000000..7294d177d --- /dev/null +++ b/pr/509/docs/search.index/166712222acb.js @@ -0,0 +1 @@ +rn_("AQIBsEAUWwABdDswAAABAAANAAYA6xIDAFQTAQC/EwEA1hMBAFAUAQB1FAEAE4ACsDATHgE8aXABBQDzlxMAAAEAAQATgAKgMAAAE+9leVMAA7BwE/wAAWJlcwFMEAAA") \ No newline at end of file diff --git a/pr/509/docs/search.index/16760629a188.js b/pr/509/docs/search.index/16760629a188.js new file mode 100644 index 000000000..015edd83f --- /dev/null +++ b/pr/509/docs/search.index/16760629a188.js @@ -0,0 +1 @@ +rn_("AQsA9IsTAAABAA8AAQABBAGwQBNDAAFlAZMSAADzAAF0AUAUAAABDQDzVxQAADEAAQABEADzgBQAABYAAQDzgQNrbXMBAAA7MAAAAQAABgACAHUTAADaEwUAI4YCsEAUSwABZXNjgAOwQBDKAAFjcnQzgISwYBDkAAGgEAAABk4BCAk=") \ No newline at end of file diff --git a/pr/509/docs/search.index/16ac7dbe5c38.js b/pr/509/docs/search.index/16ac7dbe5c38.js new file mode 100644 index 000000000..8922cd1c0 --- /dev/null +++ b/pr/509/docs/search.index/16ac7dbe5c38.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAUAAgDTDAMABxMBAAECAPN6EQAAAQABAAEGAPSCFAAAAQABAAEA8wYBbfScEgAAAQABAAEA84ACbnbzgAJhaQ==") \ No newline at end of file diff --git a/pr/509/docs/search.index/174deafe44ae.js b/pr/509/docs/search.index/174deafe44ae.js new file mode 100644 index 000000000..98c46cd24 --- /dev/null +++ b/pr/509/docs/search.index/174deafe44ae.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAIUAAgD4EAAAABKEAAEAAbBwE7AAAWXy8A8AAAEAIwACoEAAABQHZXP0WRAAAAEAHQQBADOAA6BgAAARqWRpbw==") \ No newline at end of file diff --git a/pr/509/docs/search.index/17adc63e271c.js b/pr/509/docs/search.index/17adc63e271c.js new file mode 100644 index 000000000..3ebcc1bf5 --- /dev/null +++ b/pr/509/docs/search.index/17adc63e271c.js @@ -0,0 +1 @@ +rn_("AQUA9U8QAAABAAEAQwIBAAEEAaAAAAAQ4XP1SA0AAAEAAQADAwEAAQIA9aoRAAABAAEAAQABAAGAArBgEwMAAaAgAAARAW95AQABoMAAABQeafRYEwAACAAaABsAAQYA9IIUAAABAAEAAQABAgD0/xAAAAEAmAABAAECAaBAAAATMHI7MAAAAQAABwACAP0QAQCSEQUA84ACYW9nAwAAhrCAEy8AV7CgE6MAAQwwBjswAAABAAAIAAMAdgwAAL0RAwCcEgMA84ICZWlHAQAAhbBwE7AAAaCAAAAUkKBAAAAQBbBABPR9CQAAcwYBAI4DAYECoCAAABD7sFASrAABYW8BBAGgAAAAE8lzAfoQAABjBAOwUBFqAAFjZXP2hQkAAAEAAQAuAAEA3AgBAAA7MAAAAQAAJAAIAM0HBwBPEAIAmxENAMIRAgCUEgEAFxMBAGcTAQD5EwIAAQEA9F4MAAABAI8FAQATAAOwMBQ1AAGwUBMLAAFlcHL08g8AAAEA5AABAAEAA7BAFAEAAaFAAAAUaKBQAAAR5GJjZgGXBwAA44CEsUAUjQABAjAQAQGEoLAAABO+sEAQvQABsMAT7QABoGAAABGRFCAE9n4HAADdCAEALQEfAQsBAQMAOzAAAAEAAAUAAQDKEgUAAQABoKAAABQqcvT7DwAAAQDqAAEA84ECZ3MBBwD1TxMAAAEAAQABAAEAAYACoEAAABFisFARSwABY2nzgAJhbwEAAaAgAAARgnLziwwAAD4DXAFDgISgAAAAFAegMAAAFAeABgwAHH0IICEBAwKwQBMNAAGgMAAAE2Jld/KVEAAAtQATgwKgcAAAE5pnbwECAPXtEgAAAQDpAHkAAQATgAKwMBMeATxpcAEGAPOXEwAAAQABAPMFAmJzAUwQAAABAgD0KRAAAAEAAQABACOAAqBgAAASkGJj978AAIygkAAAEpE7yRo6MAAAAQAAAAAAFwAQAAAA6gH4AfkBSQRKBEsEnwfYBwsIDAgNCA4IDwgQCFkMyAxODUcQWRBaEBsRmhF3FHgU") \ No newline at end of file diff --git a/pr/509/docs/search.index/1940d69372f3.js b/pr/509/docs/search.index/1940d69372f3.js new file mode 100644 index 000000000..8fbb10344 --- /dev/null +++ b/pr/509/docs/search.index/1940d69372f3.js @@ -0,0 +1 @@ +rn_("AQgA9OATAAABAIIABQATAAOwkBRCAAGwUBMrAVZnbHA7MAAAAQAAEgAJAHUQAgDlEQEAGhMBADsTAQBwEwEA0RMBAAgUAQAXFAEAVBQBAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/19935bf303f2.js b/pr/509/docs/search.index/19935bf303f2.js new file mode 100644 index 000000000..bcad4067a --- /dev/null +++ b/pr/509/docs/search.index/19935bf303f2.js @@ -0,0 +1 @@ +rn_("AQEAOjAAAAEAAAAAAAkAEAAAAO0P7g/ZEJER3hLfEtUTVxSIFIkUAYACsOATzwABsDAT5wABZHDDgISxQBSYAAGgkAAAE5YCgAk=") \ No newline at end of file diff --git a/pr/509/docs/search.index/1b0ddf9ba1d4.js b/pr/509/docs/search.index/1b0ddf9ba1d4.js new file mode 100644 index 000000000..d73389b28 --- /dev/null +++ b/pr/509/docs/search.index/1b0ddf9ba1d4.js @@ -0,0 +1 @@ +rn_("AQEAOjAAAAEAAAAAAAgAEAAAAIwJgxAiESMRJBHgE+ETYxRoFAECAPS4EAAAAQABAAEAE4ACoNAAABRSY3NTgAOgcAAAFE1laW+DgISwIBOHAAGgIAAAD/iwAAfdAAEKCAQ=") \ No newline at end of file diff --git a/pr/509/docs/search.index/1b58e8099167.js b/pr/509/docs/search.index/1b58e8099167.js new file mode 100644 index 000000000..3b034dd36 --- /dev/null +++ b/pr/509/docs/search.index/1b58e8099167.js @@ -0,0 +1 @@ +rn_("AQACoCAAABN5sDAR4gABaXTy5BIAAAEAAQMAOzAAAAEAAAYAAQBjEQYAAQgA9OATAAABAIIABQATAQOwkBRCAAGwUBMrAVZnbHA7MAAAAQAAEgAJAHUQAgDlEQEAGhMBADsTAQBwEwEA0RMBAAgUAQAXFAEAVBQBAAGAArAwEd4AAbAwEeAAAXJ2AQQBsAASpgABZDswAAABAAAFAAIA2hEDANMSAQC3BQABh7BQEqgBC7BgEykAAZQJAzowAAABAAAAAAALABAAAAApDSoNKw3JDsoODBGNEY4RjxGQESETWRMjgAKgEAAADShubwEDAPNyEAAAAQABACOAAqAAAAAMa2RyYwGEoFAAABGKoDAAABCrFAEQ8iUIAAABABOAArBgEsgAAWNnE4ADoLAAABP+oEAAAA7IZW9z") \ No newline at end of file diff --git a/pr/509/docs/search.index/1b8da10f40ca.js b/pr/509/docs/search.index/1b8da10f40ca.js new file mode 100644 index 000000000..704f044c9 --- /dev/null +++ b/pr/509/docs/search.index/1b8da10f40ca.js @@ -0,0 +1 @@ +rn_("AQIA9BETAAABAI0AAQABAwD0RRMAAAEAkgABAPMCAmJ1OjAAAAEAAAAAABMAEAAAANUH1gfXB+oP6w+OEI8QrRCuED0RPhHoEekRDxMQExwTHRMsE50TnhM=") \ No newline at end of file diff --git a/pr/509/docs/search.index/1bce199876db.js b/pr/509/docs/search.index/1bce199876db.js new file mode 100644 index 000000000..ed1bc018a --- /dev/null +++ b/pr/509/docs/search.index/1bce199876db.js @@ -0,0 +1 @@ +rn_("AQADoKAAABQ4oCAAABN5sDAR4gABZWl08uQSAAABAAEDADswAAABAAAGAAEAYxEGAAEIAPTgEwAAAQCCAAUAEwEDsJAUQgABsFATKwFWZ2xwOzAAAAEAABIACQB1EAIA5REBABoTAQA7EwEAcBMBANETAQAIFAEAFxQBAFQUAQABgAKwMBHeAAGwMBHgAAFydgECAbAAEqYAAWQ7MAAAAQAABQACANoRAwDTEgEAI4ECoJAAABQ3aW8BBwD0UxQAAAwAHgAPAHcLAAGIsFASqAELsGATKQABlQkDOjAAAAEAAAAAAA4AEAAAACkNKg0rDckOyg7TENQQDBEvEY0RjhGPEZARIRNZEyOAAqAQAAANKG5v") \ No newline at end of file diff --git a/pr/509/docs/search.index/1c551ca2e064.js b/pr/509/docs/search.index/1c551ca2e064.js new file mode 100644 index 000000000..ab7082a8c --- /dev/null +++ b/pr/509/docs/search.index/1c551ca2e064.js @@ -0,0 +1 @@ +rn_("AQABsGAT8wABZQEqFAAAAQYA9CMTAAABAEIBAQABBAD0PRMAAAEABAEBAPMAA2VyczowAAABAAAAAAAJABAAAACoCakJqglGEYMRiBKJEpYS6xPsEw==") \ No newline at end of file diff --git a/pr/509/docs/search.index/1cc87b30a9de.js b/pr/509/docs/search.index/1cc87b30a9de.js new file mode 100644 index 000000000..e1767e40c --- /dev/null +++ b/pr/509/docs/search.index/1cc87b30a9de.js @@ -0,0 +1 @@ +rn_("AQABsIAUHwABcPIbFAAAAQBDBQOgIAAAEV+gQAAAEqVkZXPzBhAAACwDAQABAgA7MAAAAQAAEwABADAQEwATgYSgUAAAERCgEAAADICgIAAADTscQAAzgAOwUBNtAAFhZm4=") \ No newline at end of file diff --git a/pr/509/docs/search.index/1fad72632495.js b/pr/509/docs/search.index/1fad72632495.js new file mode 100644 index 000000000..300f8591d --- /dev/null +++ b/pr/509/docs/search.index/1fad72632495.js @@ -0,0 +1 @@ +rn_("AQYA9P8SAAABAIYBAQATAgKwYBMBAAFmbfIsDQAAAQAjggKwQBMtAAFlbAEFAPTpEwAAAQBJAAEA8wMBY/L0EgAAAQABgQKgAAAACbGgMAAAD+VzdBOAAqAQAAAH2W5404CEsHAUlAABkEAB") \ No newline at end of file diff --git a/pr/509/docs/search.index/1fb07216cf6d.js b/pr/509/docs/search.index/1fb07216cf6d.js new file mode 100644 index 000000000..68f749e14 --- /dev/null +++ b/pr/509/docs/search.index/1fb07216cf6d.js @@ -0,0 +1 @@ +rn_("AQIA9aoRAAABAAEAAQABAAGAArBgEwMAAaAgAAARAW95AQABoMAAABQeafRYEwAACAAaABsAAQMA9HsTAAABAAEAAQABBgD0ghQAAAEAAQABAAECAPT/EAAAAQCYAAEAAQIBoEAAABMwcjswAAABAAAHAAIA/RABAJIRBQDzgAJhb2cHAACHsIATLwBXsKATowABDDAHOzAAAAEAAA4ABABaBAUAdgwAAL0RAwCcEgMApwAAgIWg8AAAE7OwUBBbAAGgkAAAEYmUgQA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/2045e5eacc73.js b/pr/509/docs/search.index/2045e5eacc73.js new file mode 100644 index 000000000..e904bf7f4 --- /dev/null +++ b/pr/509/docs/search.index/2045e5eacc73.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABPJcwH6EAAAAYACsDAQkQABsFAQkwABYWkBgAKgsAAAFFKgMAAAESVsbhcBAACFoCAAABKPoCAAAAl7sIAUHwABEJEEOjAAAAEAAAAAABAAEAAAAEAFdgV3BXgFeQV6BXsF6AnHD8gPNxPvE/UTARQCFBsUHBQBAgA6MAAAAQAAAAAACgAQAAAA0QnSCS0QWhNbE4kTihMsFIAUlhSXFAEAADswAAABAAAGAAIAdRMAANoTBQABAgGwMBNHAAFjOjAAAAEAAAAAABMAEAAAAEUN3g9yEHMQdBB5EHoQfhB/EA8RgRGFEYYRhxH3ET0TPhMdFEIUQxQBgQKwMBQVAAGwQBFtAAFhZROAAqCwAAAUWWFlAQEBsEAURQABZfUNFAAAAQABAAEAAQATgAKgEAAADGxhb4cPAADIsDAJjAb3sEAUSwABoIAAABKKQEyICfOBCAAA8QM7AAGAAqCAAAATYaCQAAATOmFrAQMA87ASAAABAAEAI4CEsSAUlAABoAAAAAmxoDAAAA/lBAQGAQgA9ZwUAAABAAEAAQABABOKArBAFHcAAWFykwCEoMAAABM2oQAAABP+AUAG8gMIAAABAAEDAaBAAAAR8WXzTw0AAMoDAQABBQD0pRMAAAEAAQABAFOAA7AAE1wAAWVzdAEGAPSCFAAAAQABAAEA8wgBbfScEgAAAQABAAEAAQIAOzAAAAEAAAUAAQBtFAUA8w0BbTswAAABAAAFAAEALRQFAAEEAPdXDQAAAQBNBQ4AAQCLAAEAAQMBgBNpABDuczswAAABAAAFAAEASRMFACOCAqCQAAAUQWlzAQUBoFAAABQKaPKqEgAAAQATgAKwMBNcAAFudAGAAqCAAAATerBAEeoAAWFlAYACoBAAABEboAAAABFfbnIBCQGgQAAAFERl8mQUAAABACOAAqCQAAATr2lsxyMAAAqgMAAAERywUBMTAAGw8BSKAAGwcBN3AAGg0AAAFE0wYmNlaGlrbm92OzAAAAEAABoACwBrDAAA9BADAPoRBAB0EwAAghMDALYTAQD+EwAAAxQDAB8UAQArFAAAPhQBAAECAqCwAAAUN6CwAAAUOGNz89MQAAABAFsAAQECsFAR6gABoMAAABQKaGnythMAAAEA84ACZWkBAQGgkAAAE3Rz9LgQAAABAAEAAQABAwGwABDuAAFzAWEQAADzgANoaXQBAQGgUAAAEvhjAR0NAAABBQGwgBQ+AAF2OzAAAAEAAAcAAgDIEQQAqRMCAAEBAPSBEAAAAQCnAAEA44CEsHAR7gABDggA92UPANGwkBKIAAGgUAAAERShAAAAFHOgoAAAE5agEAAAB9lAP78pOzAAAAEAADcAFgBoBQEAMgYCAKIHAgBaDAMAIA0BANIPAgDgDwAABhAAAC4QAQAyEQgAiBEAAMgSAQDREgEA1RIBADITAQBPEwQAoxMBAKwTAQCvEwAAJRQEAHMUAACQFAAA") \ No newline at end of file diff --git a/pr/509/docs/search.index/2096ecc2779b.js b/pr/509/docs/search.index/2096ecc2779b.js new file mode 100644 index 000000000..064015180 --- /dev/null +++ b/pr/509/docs/search.index/2096ecc2779b.js @@ -0,0 +1 @@ +rn_("AQIBsEAUWwABdDswAAABAAANAAYA6xIDAFQTAQC/EwEA1hMBAFAUAQB1FAEAE4ACsDATHgE8aXABBQDzlxMAAAEAAQATgAKgMAAAE+9leVMBA7BwE/wAAWJlcwFMEAAAE4ADsDAUaQABsCAHigABZWxz") \ No newline at end of file diff --git a/pr/509/docs/search.index/210dbcbbd7f7.js b/pr/509/docs/search.index/210dbcbbd7f7.js new file mode 100644 index 000000000..308b89b98 --- /dev/null +++ b/pr/509/docs/search.index/210dbcbbd7f7.js @@ -0,0 +1 @@ +rn_("AQAAOjAAAAEAAAAAAAoAEAAAAPQP9Q/aENsQYhHsEe0R/xIAE4YUhxQBBADz7w8AANkAAQDzAAFx91QNAAABAAEDAQDBAGAAAQDzAAJleTowAAABAAAAAAAOABAAAACMCaoMqwyDEJMQlBAiESMRJBHgE+ET8RPyE2MUaBQ=") \ No newline at end of file diff --git a/pr/509/docs/search.index/218355186cfc.js b/pr/509/docs/search.index/218355186cfc.js new file mode 100644 index 000000000..31a707af1 --- /dev/null +++ b/pr/509/docs/search.index/218355186cfc.js @@ -0,0 +1 @@ +rn_("AQMBsEARYAABZQHDDgAAAQUAOzAAAAEAAAYAAQBrEAYAAQMA9CMNAAABAAEAAQDzAAJwczowAAABAAAAAAAMABAAAABBBlAHlQmwDL0MBhEHEaYSpxL7EvwS/RL+EjMAA7BQD+IAyGRwcjswAAABAAAPAAYARAUAAAIGAwDPDwAA2hEDAKESAwDTEgEA") \ No newline at end of file diff --git a/pr/509/docs/search.index/2209e95f2e0d.js b/pr/509/docs/search.index/2209e95f2e0d.js new file mode 100644 index 000000000..69a7a161c --- /dev/null +++ b/pr/509/docs/search.index/2209e95f2e0d.js @@ -0,0 +1 @@ +rn_("AQkBoEAAABREZfJkFAAAAQABgAKwAA0zAAGggAAAE69kcAEDAaAAAAAQRnPy1g8AAAEAAQIDsGATMgABsNAURwABoIAAABPOaXJzAVIUAAD3AACAhaEwAAAUY5BJAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/221f869c6ba4.js b/pr/509/docs/search.index/221f869c6ba4.js new file mode 100644 index 000000000..5d230b86c --- /dev/null +++ b/pr/509/docs/search.index/221f869c6ba4.js @@ -0,0 +1 @@ +rn_("AYACsFAT/AABoAAAAAmNY3QBAQD0exMAAAEAAQABAAEDAPQ6DQAAeAaZAAEAAYACoBAAAAzLoFAAABKSY2QBAgDzgQkAACQDAQDzAAFt80kFAAC+AAEAAYECoBAAABMxoHAAABN2YWPzAAFy8+UHAADECRgAZw8AgIiwMA/iAMigMAAAFHMpMCM=") \ No newline at end of file diff --git a/pr/509/docs/search.index/22f4aa7b7735.js b/pr/509/docs/search.index/22f4aa7b7735.js new file mode 100644 index 000000000..ac8be3d95 --- /dev/null +++ b/pr/509/docs/search.index/22f4aa7b7735.js @@ -0,0 +1 @@ +rn_("AQIA9d4RAAABABoBAQBTAQEBAPciEQAAAQABALwCAQCCAAUAAQIA9LgQAAABAAEAAQATgAKg0AAAFFJjc/OAAmVvI4ACsAAH3QABbHTzAAJhcvLBDwAAAQA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/23f856af9139.js b/pr/509/docs/search.index/23f856af9139.js new file mode 100644 index 000000000..55d9dc6a4 --- /dev/null +++ b/pr/509/docs/search.index/23f856af9139.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAkAAQBPEQkAI4ADsGATbQABsDAP2gABaXN0AQgA9SUUAAABAAEAAQABAPMBAWb1oRAAAAEAAQABAAEAY4ADoDAAABMwZWd0") \ No newline at end of file diff --git a/pr/509/docs/search.index/2415f9b5fd49.js b/pr/509/docs/search.index/2415f9b5fd49.js new file mode 100644 index 000000000..cc686c2d6 --- /dev/null +++ b/pr/509/docs/search.index/2415f9b5fd49.js @@ -0,0 +1 @@ +rn_("AYECoBAAABMxoHAAABN2YWMBhwKwABPKAAGwABPMAAE0NlMAhKCQAAATWLCwE/EAAYEACfWwBwAANQC7CAkBGAA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/24b191264c4c.js b/pr/509/docs/search.index/24b191264c4c.js new file mode 100644 index 000000000..bf7ab8b14 --- /dev/null +++ b/pr/509/docs/search.index/24b191264c4c.js @@ -0,0 +1 @@ +rn_("AYACsHASiAABsCAUigABaW8BAAGw8BSKAAFr9v4TAAAFAAEAAQABACUA8wUBZPLIEgAAAQABAgDz3hAAAAEAAQDzAAFpOjAAAAEAAAAAAAcAEAAAALwQ3RLCE8MT8xP0ExIUExQjgwKwsBRpAAFhcwGBAqCgAAATMaBAAAAQgG9284CEAVAB") \ No newline at end of file diff --git a/pr/509/docs/search.index/250587bf9bab.js b/pr/509/docs/search.index/250587bf9bab.js new file mode 100644 index 000000000..2300d20bf --- /dev/null +++ b/pr/509/docs/search.index/250587bf9bab.js @@ -0,0 +1 @@ +rn_("AQUA9LoSAAABAKMAAQABCAD0chMAAAEAhAABADMBA7BAE0MAAWFiZTowAAABAAAAAAAJABAAAAC9B74HPg0/DdkPRRCREJIQkxJhEwEAADswAAABAAAfAA4AZgUBACIGCQCrCQEAhQwBADANAQDIDgAAzA8CAOEPAAD/DwEA6BABAPsQAACMEQAA4hEBAOcRAAABgQKwABF9AAGwABF/AAE0NiOAArCAFGkAAW9wAQABsEATPwABZfOIEQAASQEBALcBAICFoGAAABEmEAEL") \ No newline at end of file diff --git a/pr/509/docs/search.index/253763ab1d88.js b/pr/509/docs/search.index/253763ab1d88.js new file mode 100644 index 000000000..7dc13e711 --- /dev/null +++ b/pr/509/docs/search.index/253763ab1d88.js @@ -0,0 +1 @@ +rn_("AQMA83IQAAABAAEAAYACoLAAABQKsDATXAABbnRDAAOgIAAAERuwUBHqAAFlaGk7MAAAAQAABgADAGsMAAD7EQMAthMBAPOAAmRy") \ No newline at end of file diff --git a/pr/509/docs/search.index/267754446ed8.js b/pr/509/docs/search.index/267754446ed8.js new file mode 100644 index 000000000..792ed00e7 --- /dev/null +++ b/pr/509/docs/search.index/267754446ed8.js @@ -0,0 +1 @@ +rn_("AQACoDAAABEcoJAAABN6MGj1+hEAAIgBAQABAAEAE4ACoDAAAA/eZHIjAISwEAl6AxSwIAxgAPmgMAAAEAISABj1NQYAAC0AAQABAAEAAQIA9I0UAAABAAoAAQABAgDzexAAAAEAAQDzgAJpcgEBArBAEWAAAaBQAAATYWVz9MMOAACzAgEAeQIBCwD2iRMAAAEAogBUABYAAQDzgAJsdNOAhKCQAAATOgFEBA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/26bbc9b847a5.js b/pr/509/docs/search.index/26bbc9b847a5.js new file mode 100644 index 000000000..fcc778f4b --- /dev/null +++ b/pr/509/docs/search.index/26bbc9b847a5.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAUAAQBtFAUA8w0BbTswAAABAAAFAAEALRQFACMDhLBAErMAAaEAAAAUc6AAAAAT9TAIAjswAAABAAAUAAUAMxEHANUSAQBPEwQAJRQEAJAUAAA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/27c9a562bd0e.js b/pr/509/docs/search.index/27c9a562bd0e.js new file mode 100644 index 000000000..8d88ed9b1 --- /dev/null +++ b/pr/509/docs/search.index/27c9a562bd0e.js @@ -0,0 +1 @@ +rn_("AQAA9IIJAAABAD4K4AAnAAAAyqEQAAAUWaBAAAARFbBgFEkAAaAQAAAM17BQE+cAAbAwEXYAAaAAAAAMmLEgFGkAAbBAERMCD0BOsAk6MAAAAQAAAAAAEwAQAAAAmAmZCa4JrwlkDGUMZgxuDHEMJw2RErwSvRK+EnITcxOHE4gT9xP4Ew==") \ No newline at end of file diff --git a/pr/509/docs/search.index/28367a06fb2b.js b/pr/509/docs/search.index/28367a06fb2b.js new file mode 100644 index 000000000..863c3711d --- /dev/null +++ b/pr/509/docs/search.index/28367a06fb2b.js @@ -0,0 +1 @@ +rn_("AQcA9PsRAAABAAEAAQAjAAKgcAAAEfphcgEFCAAAAQAA9iARAAABAGwDAQAKAAEAAQAAOzAAAAEAAAYAAgB1EwAA2hMFACOIArBAFEsAAWVzAQ0BsEAURQABZfUNFAAAAQABAAEAAQABBQA7MAAAAQAACAADAPQRAgDvEgQADBQAANcDAACGEGLzBX2FFIQG9WMFAAABALUHAQABAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/2929fb67221c.js b/pr/509/docs/search.index/2929fb67221c.js new file mode 100644 index 000000000..dc9b28185 --- /dev/null +++ b/pr/509/docs/search.index/2929fb67221c.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAD4AAgCjCQAA2Aw9AAGAArBQEMoAAbCwE4sAAWFsYwAEsEAPxwABoBAAABCEYnBydjswAAABAAAGAAEAYxEGAAGAArCAERMCD7DgE/EAAWlyAQMBsEARYAABZQHDDgAAAQUAOzAAAAEAAAYAAQBrEAYAAQMA9CMNAAABAAEAAQDzAAJwczowAAABAAAAAAAMABAAAABBBlAHlQmwDL0MBhEHEaYSpxL7EvwS/RL+ElcAAACFsFAQvAIhsFAP4gDIoEAAABGLSIADOzAAAAEAAA8ABgBEBQAAAgYDAM8PAADaEQMAoRIDANMSAQDTAISgYAAADTUQCAbzjwEAAAEAiAs=") \ No newline at end of file diff --git a/pr/509/docs/search.index/296f1368d1fa.js b/pr/509/docs/search.index/296f1368d1fa.js new file mode 100644 index 000000000..95f4eae33 --- /dev/null +++ b/pr/509/docs/search.index/296f1368d1fa.js @@ -0,0 +1 @@ +rn_("AQEA9YMIAAABAAEAAQABAAEAAqAwAAARHKCQAAATejBo9foRAACIAQEAAQABABOAAqAwAAAP3mRyIwKEsBAJegMUsCAMYAD5oDAAABACEgAY9TUGAAAtAAEAAQABADOAhLCgEr8AAbBAD+gAARBJAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/2a75dabf6bdd.js b/pr/509/docs/search.index/2a75dabf6bdd.js new file mode 100644 index 000000000..5029dea11 --- /dev/null +++ b/pr/509/docs/search.index/2a75dabf6bdd.js @@ -0,0 +1 @@ +rn_("AYACsAAQLgABsCAMtQABZWkBiQKwIBRQAAGgMAAAFFppcBMAA6AgAAATGaCAAAATLGVpdfVtDAAANAABAAEAAQABhgKwkBR1AAGgAAAAEqliZAGAArAwEw0AAaAwAAAQ+XJ21wAAAIWgMAAAEHigMAAAE2IQARM6MAAAAQAAAAAACgAQAAAAfgl/CYAJsgmzCXkMkBCVEEkRShHqEmOAA7AQCXIAAWRtdA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/2b1d461fecf9.js b/pr/509/docs/search.index/2b1d461fecf9.js new file mode 100644 index 000000000..04cb30778 --- /dev/null +++ b/pr/509/docs/search.index/2b1d461fecf9.js @@ -0,0 +1 @@ +rn_("AQABsIATzwABdAGXCQAAAQkA87wSAAABAAEA84ACY2cBgwKgkAAAFBmgkAAAFBoyMwGAAqBQAAAUQaCAAAATbGNl8wACc3TyywcAAAEAAQABoLAAABOyYvNCEwAAHwEBACOAAqAAAAATQWRyI4ACoFAAABQLY2XzgAJpeQEGAPU/EQAAAQABAAEAAQABAQGwQBRFAAFl9Q0UAAABAAEAAQABAPcAAICFoBAAAAxsKWAA") \ No newline at end of file diff --git a/pr/509/docs/search.index/2b391fb231c1.js b/pr/509/docs/search.index/2b391fb231c1.js new file mode 100644 index 000000000..66c54b77b --- /dev/null +++ b/pr/509/docs/search.index/2b391fb231c1.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAYAAQBrEAYAAQMA9CMNAAABAAEAAQBjAAOgkAAAE2BocHM6MAAAAQAAAAAADgAQAAAAQQZQB5UJsAy9DAYRBxGmEqcS+xL8Ev0S/hIpEyoT") \ No newline at end of file diff --git a/pr/509/docs/search.index/2b4f77bea34a.js b/pr/509/docs/search.index/2b4f77bea34a.js new file mode 100644 index 000000000..24eaf7a6d --- /dev/null +++ b/pr/509/docs/search.index/2b4f77bea34a.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwDCEQIAZxMBAPkTAgABBwD2iRMAAAEAogBUABYAAQABBADzxREAAAEAAQAjAAKgYAAAEuphcvPwEAAAAQC4AQGAAqEQAAAUfbCAFHUAAWl1dwgAAIixUBSYAAGACZoAFzewcBPCAAGhMAAAFIwKQRvy2wkAANEGAYECoFAAABEUoKAAABM6a3ABAwA7MAAAAQAA7gACAC4NAQDLDuwA84ACbHTzgAJhZQ==") \ No newline at end of file diff --git a/pr/509/docs/search.index/2c5dcb185a0c.js b/pr/509/docs/search.index/2c5dcb185a0c.js new file mode 100644 index 000000000..3926c3f52 --- /dev/null +++ b/pr/509/docs/search.index/2c5dcb185a0c.js @@ -0,0 +1 @@ +rn_("AYkCsCAUUAABoDAAABRaaXATAAOgIAAAExmggAAAEyxlaXX1bQwAADQAAQABAAEAAYYCsJAUdQABoAAAABKpYmQBgAKwMBMNAAGgMAAAEPlydtcAAACFoDAAABB4oDAAABNiEAETOjAAAAEAAAAAAAkAEAAAAH4JfwmACbIJswmQEJUQSRFKEeoS") \ No newline at end of file diff --git a/pr/509/docs/search.index/2d5000411b5a.js b/pr/509/docs/search.index/2d5000411b5a.js new file mode 100644 index 000000000..816eb54ad --- /dev/null +++ b/pr/509/docs/search.index/2d5000411b5a.js @@ -0,0 +1 @@ +rn_("AQIA9aoRAAABAAEAAQABAAGAArBgEwMAAaAgAAARAW95AQABoMAAABQeafRYEwAACAAaABsAAQYA9IIUAAABAAEAAQABAgD0/xAAAAEAmAABAAECAaBAAAATMHI7MAAAAQAABwACAP0QAQCSEQUA84ACYW9nAwAAhrCAEy8AV7CgE6MAAQwwBjswAAABAAAIAAMAdgwAAL0RAwCcEgMA84ICZWkjAAKggAAAFJBmaPJ9CQAAAgoBAQD0XgwAAAEAjwUBABMAA7AwFDUAAbBQEwsAAWVwcvTyDwAAAQDkAAEAAQADsEAUAQABoUAAABRooFAAABHkYmNmAZcHAADzgAJtbgEBhKCwAAATvrBAEL0AAbDAE+0AAaBgAAARkRQgBPZ+BwAA3QgBAC0BHwELAQEFADswAAABAAAFAAEAyhIFAAEHAPVPEwAAAQABAAEAAQAjgAKgUAAAEWJhb/MAhSBJAjswAAABAAAIAAQA2AcAAAsIBQBODQAAmhEAAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/2e4ab4883d2f.js b/pr/509/docs/search.index/2e4ab4883d2f.js new file mode 100644 index 000000000..8d7220ad1 --- /dev/null +++ b/pr/509/docs/search.index/2e4ab4883d2f.js @@ -0,0 +1 @@ +rn_("AYACsEATWgABoMAAABQSZW8BAgD0zxEAAAEAAQABABMAA7AgEdMAAbCAFCMAAWFzdAEoDQAAAQQA8wwQAAABAAEAs4CEsEATbQABQQAG") \ No newline at end of file diff --git a/pr/509/docs/search.index/2ef7b6b51b41.js b/pr/509/docs/search.index/2ef7b6b51b41.js new file mode 100644 index 000000000..9d52c0b63 --- /dev/null +++ b/pr/509/docs/search.index/2ef7b6b51b41.js @@ -0,0 +1 @@ +rn_("AQEA9T8RAAABAAEAAQABACOBArCQE+sAAWF0Q4ADsRAUZAABoIAAABOuY290EwACsAAUOwABbnL26wEAAAEAYgIBAHIDuAs=") \ No newline at end of file diff --git a/pr/509/docs/search.index/30aa3843eff5.js b/pr/509/docs/search.index/30aa3843eff5.js new file mode 100644 index 000000000..20bdebb1c --- /dev/null +++ b/pr/509/docs/search.index/30aa3843eff5.js @@ -0,0 +1 @@ +rn_("AQIA93QMAAABAEoDAQBBAXYCAQABBQGwMBMHAAFo8+0TAAABAGsAE4MCoDAAABDRY3ABAwD09g8AAAEA5QABAAGBAqAAAAARiKAQAAAP2WV184ACYXIBgQKgkAAAE9ewUBKUAAFidxOAArCQEx8AAW5y8wACZXTyyhAAAAEAAQEA9c4MAAABAAEAAQABABOAArCwE9MAAWFvIwACsEANRgABZXQ7MAAAAQAACQAEAGEFAQAcBgEAswwBAK8RAwABAgA7MAAAAQAACQACAHAIBwABEwEAAYACsJATZwABoKAAABPWZXYTgAKxgBScAAFjbQGCArBAFAEAAaBQAAAR5GJmE4ACoEAAABQqZHMjgAKg0AAAFHRjbvOAhBEBQAEFAbBAE0MAAWUBkxIAAEOCA6EAAAAUgKDQAAAUV2ttcxOAArBgEOQAAWFyAQUAOzAAAAEAAIUAAgD4EAAAABKEAAGCA7AgEwMAAbAgEIgAAbBAEXgAAWtsc1OAA7AgFFsAAWt0dQGJhKCAAAAUj6BAAAAUQKBgAAAUYKCQAAAUkwKAChOChKCgAAAT1bGgFJ8AAbDQFDkAAQQBCQGAArFwFJoAAaDQAAAUE2NvI4ACsGAQ4gABaW8BAAD0fgwAAAEATQABACOBAqDgAAAULGV5AQ0AOjAAAAEAAAAAAAcAEAAAADsTPBPRE9ITCBQJFFQUVRQBgQKwQBNaAAGgwAAAFBJlbwECADowAAABAAAAAAAIABAAAADtD+4PkRHeEt8S1RNXFIgUiRTzAQFvOzAAAAEAAAkABADcCQEAphADAOIQAQDTEwEA84ADbW5yAQAAOzAAAAEAAAYAAgB1EwAA2hMFAEOAA7BAFEsAAaAgAAANRWVycyMAArAwE7YAAWNy8qMTAAABAAGBArCAEwkAAaEAAAAUVmNmAQsA9GMTAAABAAEAAQABgAKgAAAAEJygAAAAEJ00NvMCAXYBTA0AAPMAAmRsAZwJAADX/ikAkrCgE/8AAbBwEdYAAbCAEugAAaCwAAATIbAACboAAZ3dfzswAAABAAAWAAsAjAEAAF0FAwAZBgIAigcBAJQHAgCMDAAABRAAAF4RAADFEQIAmRICAD0UAAA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/30e186f1b9e2.js b/pr/509/docs/search.index/30e186f1b9e2.js new file mode 100644 index 000000000..291f58813 --- /dev/null +++ b/pr/509/docs/search.index/30e186f1b9e2.js @@ -0,0 +1 @@ +rn_("AYQCsAARfQABsAARfwABNDbzAAFh8skMAAABACOAArCQEYMBE2x0AYACsHATgAABsIATtgABY3MBAAA6MAAAAQAAAAAACQAQAAAA1hHXEegS6RLPE9ATIxQkFJQUlRTzgAJkZScBAICFoNAAABP2sBAFYwABoDAAABCEAggL") \ No newline at end of file diff --git a/pr/509/docs/search.index/315e1edbed3e.js b/pr/509/docs/search.index/315e1edbed3e.js new file mode 100644 index 000000000..6e1f413b8 --- /dev/null +++ b/pr/509/docs/search.index/315e1edbed3e.js @@ -0,0 +1 @@ +rn_("AQMBoEAAABQdZfJHFAAAAQABAAD0+xIAAAEAAQABAPMEAWT0oRIAAAEAAQABAKcAAAKFsKAUNQABsEAS+QABsHAT4wABFYgAAQsUAAABAAGwgBQfAAFw8hsUAAABAEMAA6AgAAARX6BAAAASpWRlc/MGEAAALAMBAPOAAmVp") \ No newline at end of file diff --git a/pr/509/docs/search.index/323e46e00169.js b/pr/509/docs/search.index/323e46e00169.js new file mode 100644 index 000000000..c8446f2b2 --- /dev/null +++ b/pr/509/docs/search.index/323e46e00169.js @@ -0,0 +1 @@ +rn_("AYICsEAStgABsEASuAABNDYBBgD0jRQAAAEACgABAPMCAXIB9hMAAPMDAWX0ihAAAAEApQABAFOAA7CwFBUAAWFlaQ==") \ No newline at end of file diff --git a/pr/509/docs/search.index/3289e9ef46be.js b/pr/509/docs/search.index/3289e9ef46be.js new file mode 100644 index 000000000..ba6301687 --- /dev/null +++ b/pr/509/docs/search.index/3289e9ef46be.js @@ -0,0 +1 @@ +rn_("AQIA9BETAAABAI0AAQABAgD0/xAAAAEAmAABAAEDAPRFEwAAAQCSAAEAAQIBoEAAABMwcjswAAABAAAHAAIA/RABAJIRBQDzAIQDQAg6MAAAAQAAAAAAFQAQAAAA1QfWB9cH6g/rD44QjxCtEK4QPRE+EegR6REPExATHBMdEywTVhNXE50TnhM=") \ No newline at end of file diff --git a/pr/509/docs/search.index/337c243b1b41.js b/pr/509/docs/search.index/337c243b1b41.js new file mode 100644 index 000000000..62bed5242 --- /dev/null +++ b/pr/509/docs/search.index/337c243b1b41.js @@ -0,0 +1 @@ +rn_("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") \ No newline at end of file diff --git a/pr/509/docs/search.index/34116ea5fc40.js b/pr/509/docs/search.index/34116ea5fc40.js new file mode 100644 index 000000000..034011307 --- /dev/null +++ b/pr/509/docs/search.index/34116ea5fc40.js @@ -0,0 +1 @@ +rn_("AQYA9KUTAAABAAEAAQABDwD0ghQAAAEAAQABAAEAAbDwFIoAAWv2/hMAAAUAAQABAAEAJQABAgD0gRAAAAEApwABAPcAAACFsQAUdwABiTAA8sgSAAABABOAArAQDKoAAWVvAQUAOzAAAAEAAA0AAQCbEQ0AAQIA8xMQAAABAAEAAQIA9A8QAAABAAEAAQABAwA7MAAAAQAACAABAL8QCAABgAKgAAAADICwMBRFAAFxcgGAAqBAAAARELDAFDsAAWVzAQEAOzAAAAEAABMAAQAwEBMAI4ACsJATrAABaW13NgAAiqBQAAAUCrBwE+sAAaAgAAANO5xVETswAAABAAALAAQA4QcAAOoQAwCqEgEADRQEAAECAbAwEXYAAXABkRIAALOAhKAgAAAR2QIoBA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/34d132c9cb45.js b/pr/509/docs/search.index/34d132c9cb45.js new file mode 100644 index 000000000..bae75741c --- /dev/null +++ b/pr/509/docs/search.index/34d132c9cb45.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwDCEQIAZxMBAPkTAgABBADzxREAAAEAAQAjAAKgYAAAEuphcvPwEAAAAQC4AQGAAqEQAAAUfbCAFHUAAWl1NwQAgYexUBSYAAGACZoAFzewcBPCAAGhMAAAFIwKQBsjgAKgMAAAEopudg==") \ No newline at end of file diff --git a/pr/509/docs/search.index/352e0d990534.js b/pr/509/docs/search.index/352e0d990534.js new file mode 100644 index 000000000..84344f57c --- /dev/null +++ b/pr/509/docs/search.index/352e0d990534.js @@ -0,0 +1 @@ +rn_("AQAA9LQQAAABAAEAAQDzAgFz9cYJAAABAAEAAQAcBgEGAPT7EQAAAQABAAEAAQQBoEAAABQHZfJ3FAAAAQABAQA7MAAAAQAABgABAJoUBgDzBgFl9E4UAAABAC8AAQAzBgOgYAAAE/VhcnXzhhAAAFwCAQDzgAJhZQEDAPS1EQAAAQABAAEAAQIA9NoPAAABACQEAQABgAOwAAgDAAGgAAAADHKwcBCTAAFucnOnBwAAh6BwAAAR+rBwEugAAZEFQTowAAABAAAAAAAMABAAAADHAcgByQHKAQUIbghvCM8JrAwTEBQQFRAcEQ==") \ No newline at end of file diff --git a/pr/509/docs/search.index/358ecde5a5a1.js b/pr/509/docs/search.index/358ecde5a5a1.js new file mode 100644 index 000000000..dc26fab97 --- /dev/null +++ b/pr/509/docs/search.index/358ecde5a5a1.js @@ -0,0 +1 @@ +rn_("AQIBoDAAABCDYQGtDAAAAYECoCAAABD7sFASrAABYW8BAwGgAAAAE8lzAfoQAAAjgAKgAAAACIFydmMAA7BQEWoAAWNlczswAAABAAAIAAQAhQkCALUJAQB7EAIAkhIAAAcBAICFsFARKwABoKAAABOVsPATrAABsCAPwQABRCEE84ACbnQ=") \ No newline at end of file diff --git a/pr/509/docs/search.index/36786b3fe579.js b/pr/509/docs/search.index/36786b3fe579.js new file mode 100644 index 000000000..534ebd006 --- /dev/null +++ b/pr/509/docs/search.index/36786b3fe579.js @@ -0,0 +1 @@ +rn_("AQIA9K8RAAABAAEAAQABAQDzxREAAAEAAQATgAJ3E2v9khZlcwEKAPOpEwAAAQABAAGAArAgFCEAAaBgAAATdm92AYACoDAAABK1oAAAABMxY2TzgIUFEAY=") \ No newline at end of file diff --git a/pr/509/docs/search.index/3884b95c3dd3.js b/pr/509/docs/search.index/3884b95c3dd3.js new file mode 100644 index 000000000..edf047b9c --- /dev/null +++ b/pr/509/docs/search.index/3884b95c3dd3.js @@ -0,0 +1 @@ +rn_("AYACoAAAABCcoAAAABCdNDYBgAOwABPKAAGwABPMAAGwcBQ+AAE0NmEBBAD0jRQAAAEACgABACOBAqBAAAARXW1wAQEAOzAAAAEAAAgAAwDFEgAANhMAAJoUBgDHAQACxaBAAAAQZKCAAAASoEAGgAg7MAAAAQAAFgAIAAMQAQAWEQEAWREDAMgRBgCpEwIA9hMAAE4UAQB+FAEA8wACZXY6MAAAAQAAAAAAEQAQAAAA8wH0AUcGTA3FD8YP3w/4D4oQixAwETERthK3ErgSuRIVFBYUAYECsHARWQABsHARWwABNDYjgAKwEAfsAAFucAEDArBAEw0AAaAwAAATYmV39JAQAAAFALQAAQATgQKgcAAAE5pnbyOAArBQEAMAAWR0VwEAAIWgEAAAEGSxIBROAAERIQH0zxEAAAEAAQABAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/3889638da158.js b/pr/509/docs/search.index/3889638da158.js new file mode 100644 index 000000000..3d7a237fa --- /dev/null +++ b/pr/509/docs/search.index/3889638da158.js @@ -0,0 +1 @@ +rn_("AQkA810UAAABADUAAQ0BoKAAABShd/KRFAAAAQABgQKwgBRrAAGgYAAAFI9kczcBAIaFoEAAABRAsGAUFABMApAK") \ No newline at end of file diff --git a/pr/509/docs/search.index/38b550452ed6.js b/pr/509/docs/search.index/38b550452ed6.js new file mode 100644 index 000000000..07172fe36 --- /dev/null +++ b/pr/509/docs/search.index/38b550452ed6.js @@ -0,0 +1 @@ +rn_("AYACsFARBgABoLAAABPiY3MBBADz0g8AAAEAAQABgAKgUAAAENmgYAAAEUZpcgGAAqAgAAAMyKBgAAARRWVoAQEBoCAAABFEYvPEDAAAAQABABOBAqAQAAAMx2FjAYACoBAAABOusDANWgABZXMjAAKgQAAAFERld/JkFAAAAQABBgD0/xIAAAEAhgEBABMAArBgEwEAAWZt8iwNAAABAAEBAPQREQAAAQCnAAEAI4ACoLAAABPJZGcBggKgoAAAEzGgQAAAEIBvdvOAA2ducgGAAqBAAAAS+KAwAAAPz2hsNz8AAIqwQA/QAAGgMAAADMM0YFc7MAAAAQAAGgAEAOgBAAAxBA4ATwwJAB0NAAA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/38d210a6c887.js b/pr/509/docs/search.index/38d210a6c887.js new file mode 100644 index 000000000..1f55b1dc4 --- /dev/null +++ b/pr/509/docs/search.index/38d210a6c887.js @@ -0,0 +1 @@ +rn_("AQYA9I0RAAABAAEAAQABAgA7MAAAAQAADAAFAGcMAQCWEAUAwRIBAB8TAQA4FAAA84ACZm4BAQA7MAAAAQAACAACALgPBgBHEwEA84ACZG4BAQDzugcAAAEAAQABhgKgAAAAE0GgAAAAE0JkcvMBAXYBbBMAAEMAA7CAEtUAAbCwFGYAAWtscjswAAABAAAMAAMAsQcIAFANAQBUEAEAAQIA9YMIAAABAAEAAQABAPMAhBRBAAFyAAAA") \ No newline at end of file diff --git a/pr/509/docs/search.index/394aea3c11a3.js b/pr/509/docs/search.index/394aea3c11a3.js new file mode 100644 index 000000000..f69cf0c51 --- /dev/null +++ b/pr/509/docs/search.index/394aea3c11a3.js @@ -0,0 +1 @@ +rn_("AQQAOjAAAAEAAAAAAAcAEAAAAOoP6w+tEK4QDxMQE50TnhMBAAD0ggkAAAEAPgrgACcAAACIoRAAABRZoEAAABEVsGAUSQABoBAAAAzXsFAT5wABsDARdgABsSAUaQABOcECOjAAAAEAAAAAABAAEAAAAJgJmQmuCa8JZAxlDGYMbgxxDCcNkRJyE3MThxOIE/cT+BMzgAOggAAAE/Blb3k=") \ No newline at end of file diff --git a/pr/509/docs/search.index/3965b74da6b0.js b/pr/509/docs/search.index/3965b74da6b0.js new file mode 100644 index 000000000..a934ccf4a --- /dev/null +++ b/pr/509/docs/search.index/3965b74da6b0.js @@ -0,0 +1 @@ +rn_("AYICsCARRwABsDARuwABZXABBQD09A8AAAEA5QABAFOAA7CQEewAAWFpcgGBArCgE/8AAbCgFAEAAWNyxwAAAIWwUA1AAAGgIAAACe6gIAAADHk8AAE6MAAAAQAAAAAACgAQAAAAlQGWAUAEiQekCaUJiBCJEPIQ8xD/EQ==") \ No newline at end of file diff --git a/pr/509/docs/search.index/39dc67d07722.js b/pr/509/docs/search.index/39dc67d07722.js new file mode 100644 index 000000000..7b612fde7 --- /dev/null +++ b/pr/509/docs/search.index/39dc67d07722.js @@ -0,0 +1 @@ +rn_("AQUA9LoSAAABAKMAAQABCAD0chMAAAEAhAABADMBA7BAE0MAAWFiZfU+DQAAAQAGA04CzgABAAD1hQwAAAEARgMBAAEA84ACc3UjgQKwgBNtAAFydAGAArBwEvYAAaAQAAANIm56AQYA9I4QAAABAK4AAQBzAISgoAAAFKECART1FAYAAAEAKw5RAAEA") \ No newline at end of file diff --git a/pr/509/docs/search.index/3a250acbb1fb.js b/pr/509/docs/search.index/3a250acbb1fb.js new file mode 100644 index 000000000..f9762674a --- /dev/null +++ b/pr/509/docs/search.index/3a250acbb1fb.js @@ -0,0 +1 @@ +rn_("AQAA9GMTAAABAAEAAQATAAKwgBM4AAFzdvTsCQAAAQD2CQEAE4ACsDARDQABbG4BgAKgAAAAEJygAAAAEJ00NvMDAXYBTA0AABMAAqBAAAARiGFz94wMAAB5A1kBOwEBAAEAogEBAgD0ERMAAAEAjQABAAEDAPRFEwAAAQCSAAEA8wACYnU6MAAAAQAAAAAAEwAQAAAA1QfWB9cH6g/rD44QjxCtEK4QPRE+EegR6REPExATHBMdEywTnROeE/OAAmR0E4ACoCAAAA1WZXIBBwGgQAAAFB1l8kcUAAABAAEAAPXeCQAAAQABAAEAAQAjgAKgoAAAFHNmcEcAAACFsDAPxQABoFAAABCEsHARBgABsUAUmgABgSkA8hAGAAABAPOAAmx0lwEAgIWxMBQfAAGxEBRJAAERRQA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/3b41ad6096cb.js b/pr/509/docs/search.index/3b41ad6096cb.js new file mode 100644 index 000000000..f7ae9fe9d --- /dev/null +++ b/pr/509/docs/search.index/3b41ad6096cb.js @@ -0,0 +1 @@ +rn_("AQUBsEASswABZfLVEgAAAQABAAA7MAAAAQAAEQAEAN4BAQC/EAgAtREDALASAgABCgGwQBRFAAFl9Q0UAAABAAEAAQABAEcGAACHsCARFAImsMAUigABoCAAAAeJsDAH4wABGIkKOjAAAAEAAAAAAAwAEAAAAN8H4AfACXcMeAx+DH8MzAzNDBYNFw0DEwQT") \ No newline at end of file diff --git a/pr/509/docs/search.index/3b6343e13631.js b/pr/509/docs/search.index/3b6343e13631.js new file mode 100644 index 000000000..4a2106c10 --- /dev/null +++ b/pr/509/docs/search.index/3b6343e13631.js @@ -0,0 +1 @@ +rn_("AQQBoEAAABGCZQFHEAAAAQgA9L0RAAABAAEAAQABAwGgQAAAEYFlAdgPAAABAgDzhwwAAAEAAQAjAAKwIAx3AAFrcvZ/BwAAAQCFCeUAAQAfAvOAAnJ4AQMBsIAUPgABdjswAAABAAALAAMAFhEBAMgRBgCpEwIAI4ACsGAQkAC5YXIBAwGgAAAAEEZz8tYPAAABACOAAqCwAAATzmVoAQMBoEAAABEVZfKYCQAAAQDXCwAAirAgB/EJU7CwEyMAAaCAAAARwaAAAAATMJ8ITDowAAABAAAAAAAJABAAAADpAUEEQgRDBGIMYwzEDMUMxgwxEw==") \ No newline at end of file diff --git a/pr/509/docs/search.index/3bc9fac5887b.js b/pr/509/docs/search.index/3bc9fac5887b.js new file mode 100644 index 000000000..cf93e7318 --- /dev/null +++ b/pr/509/docs/search.index/3bc9fac5887b.js @@ -0,0 +1 @@ +rn_("AQAA9EQQAADRAgEAswDzAAFzOzAAAAEAABkACABWBQIAggwCAP0PAQC9EAEA+RABAOARAQCNEwcAeRQDACOAArEQFH4AAWF0") \ No newline at end of file diff --git a/pr/509/docs/search.index/3c467f5cbf13.js b/pr/509/docs/search.index/3c467f5cbf13.js new file mode 100644 index 000000000..9ce2a58f3 --- /dev/null +++ b/pr/509/docs/search.index/3c467f5cbf13.js @@ -0,0 +1 @@ +rn_("AQAA9d4JAAABAAEAAQABACOAAqCgAAAUc2ZwRwAAAIWwMA/FAAGgUAAAEISwcBEGAAGxQBSaAAGBKQDyEAYAAAEA") \ No newline at end of file diff --git a/pr/509/docs/search.index/3c6fb78251b2.js b/pr/509/docs/search.index/3c6fb78251b2.js new file mode 100644 index 000000000..3f501d62a --- /dev/null +++ b/pr/509/docs/search.index/3c6fb78251b2.js @@ -0,0 +1 @@ +rn_("AQMA9ksNAACuAlkAAQCyAgEAAQQAOzAAAAEAAAoABACWEAUAwRIBAB8TAQA4FAAAAYYCoAAAABNBoAAAABNCZHLzAgF2AWwTAADzgAJlbycBAADHoDAAAA/4oHAAABKSoIAAABQLoNAAABOuoLAAABQLQEUICTswAAABAAAJAAMAVwYCAKsHBACxDAEAI4ACMj5G4AFpZHIBAAA6MAAAAQAAAAAACgAQAAAA0QnSCS0QWhNbE4kTihMsFIAUlhSXFBOAAqAwAAAQrGx0w4AEoAAAABCcoAAAABCdNDZhZQ==") \ No newline at end of file diff --git a/pr/509/docs/search.index/3e97732e8a32.js b/pr/509/docs/search.index/3e97732e8a32.js new file mode 100644 index 000000000..e8e336f6a --- /dev/null +++ b/pr/509/docs/search.index/3e97732e8a32.js @@ -0,0 +1 @@ +rn_("AQUA9OkTAAABAEkAAQDzBAFj8vQSAAABACOCArBQEsYAAWZ0AQABsAAUPgABZPRaDAAAAQABAAEA84ACZWkTgAKw4BRJAAF0dg==") \ No newline at end of file diff --git a/pr/509/docs/search.index/3ee5422cd9bc.js b/pr/509/docs/search.index/3ee5422cd9bc.js new file mode 100644 index 000000000..072e3e22e --- /dev/null +++ b/pr/509/docs/search.index/3ee5422cd9bc.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwDCEQIAZxMBAPkTAgABBADzxREAAAEAAQAjAAKgYAAAEuphcvPwEAAAAQC4AQGAAqEQAAAUfbCAFHUAAWl1NwQAgoexUBSYAAGACZoAFzewcBPCAAGhMAAAFIwKQBsTgAKwEBL2AAFpcg==") \ No newline at end of file diff --git a/pr/509/docs/search.index/3eea839e1662.js b/pr/509/docs/search.index/3eea839e1662.js new file mode 100644 index 000000000..095de8a4a --- /dev/null +++ b/pr/509/docs/search.index/3eea839e1662.js @@ -0,0 +1 @@ +rn_("AQkA810UAAABADUAAQwBoKAAABShd/KRFAAAAQABAQDzBxAAAAEAAQDzgAJ0dQGBArCAFGsAAaBgAAAUj2RzNwIAgIagQAAAFECwYBQUAEywMBP/AAECkA4BAAGwUBENAAFj9OgHAAABAAEA/AHDgISgEAAADMugUAAAEpIMQAQ=") \ No newline at end of file diff --git a/pr/509/docs/search.index/3fe3c85d7012.js b/pr/509/docs/search.index/3fe3c85d7012.js new file mode 100644 index 000000000..7bb9ebb86 --- /dev/null +++ b/pr/509/docs/search.index/3fe3c85d7012.js @@ -0,0 +1 @@ +rn_("AQEBsIATVgABdfLECQAAAQAjgAKwEAnCAAEzNgEGAPPyEAAAAQAMAROAArAAD8oAAW91AQMA87wSAAABAAEAI4ACsAARuwABaGkBAAA7MAAAAQAAEwABABEIEwAjgAKgMAAADSdhZfOAAnJ0E4ACsDATowABYWwBAAGggAAAFJBm8n0JAAACCgEHAPVPEwAAAQABAAEAAQAjgAKgUAAAEWJhb/MCAmZzOzAAAAEAAAgABADYBwAACwgFAE4NAACaEQAAE4ACoEAAAAxqYW8BAwGwQBRbAAF08r8TAAABACOFAqBAAAAT72JzIwADoEAAABEEoFAAABFvYWJm94oJAAABAEUA/AYBAF4AAQATgQKgUAAAEfdndAGAAqBAAAAR5KAgAAATzmFpAQIA9L4JAAABAIgHAQABAgDzpwwAAAEAAQATgAOwEAm8AAGwMBQBAAFpdXkBAgA7MAAAAQAACwACALgTBQBtFAUA8wgBbTswAAABAAAMAAMAbBEAANcSBQAtFAUAAYECoIAAABLFoHAAABQSYXPzgAJucrfbAACMoUAAABRooCAAAAnAsCAH3AHlN5UOOzAAAAEAAB0CBQDbAQAAAgImAZcHAAAACAAAeQrzAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/40de1d99c33a.js b/pr/509/docs/search.index/40de1d99c33a.js new file mode 100644 index 000000000..aefa64f25 --- /dev/null +++ b/pr/509/docs/search.index/40de1d99c33a.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAcAAQDTCQcAAQIA91gQAADPAJ0CAQABAHMAAQATgAKwMBDpAKNpdPMAAXPyaRQAAAEAM4ADsRAT4wABZG5w") \ No newline at end of file diff --git a/pr/509/docs/search.index/4137d3501001.js b/pr/509/docs/search.index/4137d3501001.js new file mode 100644 index 000000000..e79899e15 --- /dev/null +++ b/pr/509/docs/search.index/4137d3501001.js @@ -0,0 +1 @@ +rn_("AQIA9aoRAAABAAEAAQABAAGAArBgEwMAAaAgAAARAW95AQABoMAAABQeafRYEwAACAAaABsAAQYA9IIUAAABAAEAAQABAgD0/xAAAAEAmAABAAECAaBAAAATMHI7MAAAAQAABwACAP0QAQCSEQUA84ACYW9nAwAAhrCAEy8AV7CgE6MAAQwwBjswAAABAAAIAAMAdgwAAL0RAwCcEgMA84ACZWk=") \ No newline at end of file diff --git a/pr/509/docs/search.index/41a15bcea3f9.js b/pr/509/docs/search.index/41a15bcea3f9.js new file mode 100644 index 000000000..1fe03e094 --- /dev/null +++ b/pr/509/docs/search.index/41a15bcea3f9.js @@ -0,0 +1 @@ +rn_("AQMBoEAAABGBZQHYDwAAAQIA84cMAAABAAEAIwACsCAMdwABa3L2fwcAAAEAhQnlAAEAHwLzAAJyePJyCQAAAQA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/41dc73cd19e1.js b/pr/509/docs/search.index/41dc73cd19e1.js new file mode 100644 index 000000000..4a38e3cb2 --- /dev/null +++ b/pr/509/docs/search.index/41dc73cd19e1.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAAkABADcCQEAphADAOIQAQDTEwEAAQIBsEAUWwABdDswAAABAAANAAYA6xIDAFQTAQC/EwEA1hMBAFAUAQB1FAEAE4ACsDATHgE8aXABiAKwABPKAAGwABPMAAE0NhOBAqAwAAARhGRzAYACoCAAAAzLsGAT/AABbngBBQDzlxMAAAEAAQATgAKgMAAAE+9lefMAhBIAA/JMEAAA2gDzgAJydA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/4215b1682697.js b/pr/509/docs/search.index/4215b1682697.js new file mode 100644 index 000000000..08b797104 --- /dev/null +++ b/pr/509/docs/search.index/4215b1682697.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAIUAAgD+BwAA9AmEAAGEArBwFBsAAaBAAAARGGlzAQcA8/IQAAABAAwBI4ACoLAAABLFYXQBAAOwMAmmAAGwMBOAAAGgIAAAB/9hZG8B9QEAADcDAICGoMAAABQTsAAT5wABAJgV") \ No newline at end of file diff --git a/pr/509/docs/search.index/424d5a7a4fa1.js b/pr/509/docs/search.index/424d5a7a4fa1.js new file mode 100644 index 000000000..88594a371 --- /dev/null +++ b/pr/509/docs/search.index/424d5a7a4fa1.js @@ -0,0 +1 @@ +rn_("AQ0A9cQTAAABAAEAcwABAAGAAqEQAAAT/rBwFBUAAW5yAQcA9LURAAABAAEAAQAzAASgIAAACa2w4BPzAAExZWly9SUEAAABALMDtAE2AwECAPQKEQAAAQCCAQEAAQMA9HQRAAABAGsBAQDzAAJidQEmEAAAAQMA9AgRAAABAIIBAQDzhAJkcwECAPOPCQAAAQBVAPOAAml0ARgA854UAAABAAEAI4ECsMATwgABY20BDQGwQBRFAAFl9Q0UAAABAAEAAQABABOCArAAESAAAWtzAQQA9LQQAAABAAEAAQABgQKgIAAAERuwQBNcAAFlafOCAmV0AQQBoGAAABK1cvR7DAAAAQCoAwEA84ADYWVvAQkA9t4SAAABAPYAggAxAAEAE4ACsGAQzAABZG4BBgD19BAAAAEAAQABAH0CAQMBoHAAABMZZ/TmBwAAAQD8AQEAAQkA810UAAABADUAAYECsIAUawABoGAAABSPZHMXAQCIhbDQFJEAAaBAAAAUQLBgFBQATAKQCgEEAPSwEwAAAQBLAAEAU4ADsJAUigABZWhvAQABoEAAABGBZQHYDwAAI4ECsFARLQABa3R3/AEAjaGQAAAUgbCwEycAAaCgAAAS3f2BDzswAAABAAAdAAgAdgAAAHwAAwAMBgEAwgcHALgJAwCZDAcAXxABAIoSAAA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/4279670fcfbe.js b/pr/509/docs/search.index/4279670fcfbe.js new file mode 100644 index 000000000..c4f7d925c --- /dev/null +++ b/pr/509/docs/search.index/4279670fcfbe.js @@ -0,0 +1 @@ +rn_("AQQBoFAAABQ9cwGQEgAAAYICsEAStgABsEASuAABNDYBBgD0jRQAAAEACgABAPMCAXIB9hMAAPMDAWX0ihAAAAEApQABAFOEA7CwFBUAAWFlaWOAA6BQAAAUdGNldA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/427bea082f92.js b/pr/509/docs/search.index/427bea082f92.js new file mode 100644 index 000000000..94cf75e31 --- /dev/null +++ b/pr/509/docs/search.index/427bea082f92.js @@ -0,0 +1 @@ +rn_("AQACoCAAABN5sDAR4gABaXTy5BIAAAEAAQMAOzAAAAEAAAYAAQBjEQYAAQgA9OATAAABAIIABQATAQOwkBRCAAGwUBMrAVZnbHA7MAAAAQAAEgAJAHUQAgDlEQEAGhMBADsTAQBwEwEA0RMBAAgUAQAXFAEAVBQBAAGAArAwEd4AAbAwEeAAAXJ2AQQBsAASpgABZDswAAABAAAFAAIA2hEDANMSAQC3BQABh7BQEqgBC7BgEykAAZQJAzowAAABAAAAAAALABAAAAApDSoNKw3JDsoODBGNEY4RjxGQESETWRMjgAKgEAAADShubwEDAPNyEAAAAQABACOAAqAAAAAMa2RyYwGEoFAAABGKoDAAABCrFAEQ8iUIAAABABOAA7BgEsgAAbAAD8EAAWNnchOAA6CwAAAT/qBAAAAOyGVvcw==") \ No newline at end of file diff --git a/pr/509/docs/search.index/43d54762ebae.js b/pr/509/docs/search.index/43d54762ebae.js new file mode 100644 index 000000000..ceb1833b5 --- /dev/null +++ b/pr/509/docs/search.index/43d54762ebae.js @@ -0,0 +1 @@ +rn_("AYQCoCAAABEnoDAAABGMaXQBAQA7MAAAAQAAhQACAP4HAAD0CYQAYwADsAAB+AABZGVsOjAAAAEAAAAAAAcAEAAAAFkFHwYgBiEGAghFEYATgRM=") \ No newline at end of file diff --git a/pr/509/docs/search.index/43d7eeba4357.js b/pr/509/docs/search.index/43d7eeba4357.js new file mode 100644 index 000000000..57e2f3194 --- /dev/null +++ b/pr/509/docs/search.index/43d7eeba4357.js @@ -0,0 +1 @@ +rn_("AQMBgBNpABDuczswAAABAAAFAAEASRMFACOCAqCQAAAUQWlzBwEAhMWwUBMTAAGgoAAAE6+wcBN3AAGg0AAAFE2AARgI") \ No newline at end of file diff --git a/pr/509/docs/search.index/4417fc0d17a5.js b/pr/509/docs/search.index/4417fc0d17a5.js new file mode 100644 index 000000000..41c42f9ea --- /dev/null +++ b/pr/509/docs/search.index/4417fc0d17a5.js @@ -0,0 +1 @@ +rn_("AQUA9OkTAAABAEkAAQDzAwFj8vQSAAABACOAArBwFJQAAWhvAYICsAAS0QABsAAR4gABZXNDAAOgoAAAFDigIAAAE3llaXTy5BIAAAEAAQIAOzAAAAEAAAYAAQBjEQYAAQIBoEAAABQdZfJHFAAAAQDzgAJpbwEIAPTgEwAAAQCCAAUAEwEDsJAUQgABsFATKwFWZ2xwOzAAAAEAABIACQB1EAIA5REBABoTAQA7EwEAcBMBANETAQAIFAEAFxQBAFQUAQABgAKwQBKoAQugIAAAEShhaQGBArAwE/EAAbAwEsYAAWNpAQMBsAATFQABczswAAABAAANAAMA4BEBAI0TBwB5FAMAAQMA9d4RAAABABoBAQBTAfOAAnJ2AQAAOzAAAAEAAAUAAgCmEgEA+xIDAPMCAWQ7MAAAAQAACQADANoRAwChEgMA0xIBACOAAqCQAAAUN2lvE4ACsPAUhgABbG8BBQD0UxQAAAwAHgAPACOAAqBgAAATb2dzI4ACsJAUNQABYmz3xwAAjbBgFGkAAbBwE+MAAbBgEykAAbBQExcAAbXJFzowAAABAAAAAAAgABAAAABNBoQJtAm1DLYMKQ0qDSsNxA7FDsYOxw7JDsoOChALEAwQDRAOENMQ1BAMES8RjRGOEY8RkBHYESETOBM5E1kTCxQ=") \ No newline at end of file diff --git a/pr/509/docs/search.index/444aae938267.js b/pr/509/docs/search.index/444aae938267.js new file mode 100644 index 000000000..22f1624b7 --- /dev/null +++ b/pr/509/docs/search.index/444aae938267.js @@ -0,0 +1 @@ +rn_("AQEBoEAAABGIc/WMDAAA0gQ7AQEAAQATggKgIAAADVZlcgECAPT7DwAAAQDqAAEAI4ECoBAAAAyLZWkTgAKg8AAAFAtucjMAA7AwCasAAWVvdAHVEQAA") \ No newline at end of file diff --git a/pr/509/docs/search.index/44cfcd43b029.js b/pr/509/docs/search.index/44cfcd43b029.js new file mode 100644 index 000000000..f8e3d9ac9 --- /dev/null +++ b/pr/509/docs/search.index/44cfcd43b029.js @@ -0,0 +1 @@ +rn_("AQgBoIAAABSQZgF/EwAAE4ACoOAAABRYYnABAwGwABQUAExz9fYPAAABAOUAAQDhAgGAArBAEqwAAaAQAAARGHByAYEDoAAAABGIsAAP4QEaoBAAAA/ZZXN184ADYW9yAQIA9d4QAAABAAEARwIBAAEEADswAAABAAAJAAEATxEJAAGBAqCQAAAT17BQEpQAAWJ3o4CEsFAT8wABsJATHwABQCADVwMAAIagcAAAE86gQAAAFD0wQQw6MAAAAQAAAAAAOgAQAAAAZgVnBSIGIwYkBiUGJgYnBigGKQYqBisGCgirCawJsQmFDIYMpwyoDKkMMA0xDcgOzA/ND84P4Q//DwAQRBC8EMoQyxDoEOkQ+xAgESERjBHZEeIR4xHnEd0SFRMWE8ITwxPJE/MT9BMSFBMUKhSNFI4UmBSZFA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/45261c45eedf.js b/pr/509/docs/search.index/45261c45eedf.js new file mode 100644 index 000000000..489e50942 --- /dev/null +++ b/pr/509/docs/search.index/45261c45eedf.js @@ -0,0 +1 @@ +rn_("AQQBoEAAABQHZfJ3FAAAAQABAQA7MAAAAQAABgABAJoUBgDzBgFl9E4UAAABAC8AAQBTAISgIAAACdugYAAAE/UBAQnzhhAAAFwCAQA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/4656a4fb98bd.js b/pr/509/docs/search.index/4656a4fb98bd.js new file mode 100644 index 000000000..3f6a22037 --- /dev/null +++ b/pr/509/docs/search.index/4656a4fb98bd.js @@ -0,0 +1 @@ +rn_("AQkBoIAAABSQZgF/EwAAAQIA9d4QAAABAAEARwIBAPMAAml1OjAAAAEAAAAAAAcAEAAAALwQ3RLCE8MT8xP0ExIUExQBCAD1JRQAAAEAAQABAAEA8wIBZvWhEAAAAQABAAEAAQATgAKwoBRpAAFydPMCAmFz884QAAABAAEAAQQBsEAURQABZfUNFAAAAQABAAEAAQABAgA7MAAAAQAACwACALgTBQBtFAUA8wMBbTswAAABAAANAAQAHg0AAGwRAADXEgUALRQFAPOBAmNl84ACYWU=") \ No newline at end of file diff --git a/pr/509/docs/search.index/46b2a1fc2fe3.js b/pr/509/docs/search.index/46b2a1fc2fe3.js new file mode 100644 index 000000000..a02f28c1d --- /dev/null +++ b/pr/509/docs/search.index/46b2a1fc2fe3.js @@ -0,0 +1 @@ +rn_("AQABsKASwQABZTswAAABAAAJAAIAcAgHAAETAQABAAGgIAAADTVtAR4GAAABgAKwoBK/AAGwQA/oAAFsbwEAAPT7EgAAAQABAAEA8wQBZPShEgAAAQABAAEAIwKEsKAUNQABsEAS+QABsHAT4wABFYAAAQsUAAABAAGwgBQfAAFw8hsUAAABAEMAA6AgAAARX6BAAAASpWRlc/MGEAAALAMBAPOBAmVp1wIAAIawIBKGAAGw0BQjAAEXAQL0RQUAAAEAAQC/AA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/471028459586.js b/pr/509/docs/search.index/471028459586.js new file mode 100644 index 000000000..d4a4189ae --- /dev/null +++ b/pr/509/docs/search.index/471028459586.js @@ -0,0 +1 @@ +rn_("AQABoCAAAA01bQEeBgAAAQAA9PsSAAABAAEAAQDzBAFk9KESAAABAAEAAQAjhISwoBQ1AAGwQBL5AAGwcBPjAAEVgAAzgAOw0BQjAAFhZWk=") \ No newline at end of file diff --git a/pr/509/docs/search.index/4763fd511429.js b/pr/509/docs/search.index/4763fd511429.js new file mode 100644 index 000000000..75cbf2baf --- /dev/null +++ b/pr/509/docs/search.index/4763fd511429.js @@ -0,0 +1 @@ +rn_("AQQA9l0UAAABAA0AAQAjAAQAAQMA87ASAAABAAEAI4ECsSAUlAABY2sTgAKgMAAAFB1uclMAA6BAAAAMeWVvcvQoEQAAcAGvAQEA") \ No newline at end of file diff --git a/pr/509/docs/search.index/477aba3c874c.js b/pr/509/docs/search.index/477aba3c874c.js new file mode 100644 index 000000000..e35e56f7a --- /dev/null +++ b/pr/509/docs/search.index/477aba3c874c.js @@ -0,0 +1 @@ +rn_("AQcA9okTAAABAKIAVAAWAAEA8wEBaQHbCQAAAQAA9d4JAAABAAEAAQABAEcBAIDGAhweeb3ZoKAAABRzsGAT6wABoBAAAAm3AAogig==") \ No newline at end of file diff --git a/pr/509/docs/search.index/47876021cef2.js b/pr/509/docs/search.index/47876021cef2.js new file mode 100644 index 000000000..0191af9dd --- /dev/null +++ b/pr/509/docs/search.index/47876021cef2.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAUAAQBJEwUAg4YEsFATEwABsHATdwABoNAAABRNYm5vdhOAArAQEuYAAXByAQABoDAAABHndAGWEwAA8wIBc/KvEAAA6wITgAKgYAAAEKBldQECAPZUDQAAAQABAwEAIQEBAAGAArAAEtEAAYAOyDGgAWVzAQEA8+IHAACLCwEAY4ADsIAT5wABYm5084ADYW9yARQA9HkUAAABAAEAAQABAAA7MAAAAQAABgACAHUTAADaEwUAI4gCsEAUSwABZXMBBAGwMBNHAAFj84URAAABAAEAAQUAOzAAAAEAAAgAAwD0EQIA7xIEAAwUAADzAgNjZXDzGQ0AAAEAAQBjgAOgYAAAEAFlb3UBAAGwYBPzAAFlASoUAADzAgFz9qgJAAABAAEAnAc9ABMBAQgA87ASAAABAAEAQ4ADoBAAABN5oOAAABP+ZG9zAQIBoFAAABGEcgEmEQAAAYACoEAAABCsoHAAABQ4YWUBAAA7MAAAAQAACwACAIMHBQDKEgUA84ACdHYBAQD0ewgAAAEAAQABAPMAAm5y9p0JAABSAJ0GAQCuAAEA8wCHMJEM92oFAACaC+ABAQCEAAEA7gA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/48234c118f23.js b/pr/509/docs/search.index/48234c118f23.js new file mode 100644 index 000000000..a07820c68 --- /dev/null +++ b/pr/509/docs/search.index/48234c118f23.js @@ -0,0 +1 @@ +rn_("AQIBoKAAABNYZPKBBwAAAQABgQKgMAAAFFKgUAAAE+JjcwEEA7AgEU0AAaBQAAAR8KBAAAAUHmxzdvIZFAAAAQDzAANhZW/yUAQAAAEA") \ No newline at end of file diff --git a/pr/509/docs/search.index/48ea2bc1c340.js b/pr/509/docs/search.index/48ea2bc1c340.js new file mode 100644 index 000000000..bfd1bb3ca --- /dev/null +++ b/pr/509/docs/search.index/48ea2bc1c340.js @@ -0,0 +1 @@ +rn_("AYACsRAUdwABsBAMqgABZW8BBQA7MAAAAQAADQABAJsRDQABAgDzExAAAAEAAQABAgD0DxAAAAEAAQABAAEDADswAAABAAAIAAEAvxAIAIcHAACHsKATrAABsNAUOwABoFAAABQKjBQROzAAAAEAAAYAAwDhBwAA6hADAKoSAQDzgAJsdBOAA6AgAAANHLCgFCMAAWFkbg==") \ No newline at end of file diff --git a/pr/509/docs/search.index/49462535504e.js b/pr/509/docs/search.index/49462535504e.js new file mode 100644 index 000000000..24b1ea6f5 --- /dev/null +++ b/pr/509/docs/search.index/49462535504e.js @@ -0,0 +1 @@ +rn_("AQUA9LoSAAABAKMAAQABCAD0chMAAAEAhAABADMBA7BAE0MAAWFiZfU+DQAAAQAGA04CzgABAAD1hQwAAAEARgMBAAEA84ACc3UjgQKwgBNtAAFydAEGAPSOEAAAAQCuAAEAUwCEsIAS9gABoKAAABShAgEU9RQGAAABACsOUQABAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/498baefc1eaa.js b/pr/509/docs/search.index/498baefc1eaa.js new file mode 100644 index 000000000..030571286 --- /dev/null +++ b/pr/509/docs/search.index/498baefc1eaa.js @@ -0,0 +1 @@ +rn_("AQAEoAAAABCgsCARTQABoFAAABHwoEAAABQeZGxzdjswAAABAAATAAYARQYAAIoMAAAyDQAAFhANAG8RAAAZFAEAAQIA9M8RAAABAAEAAQAThAKwIBHTAAFhc/OAAmFl") \ No newline at end of file diff --git a/pr/509/docs/search.index/4a2f3dcae3f4.js b/pr/509/docs/search.index/4a2f3dcae3f4.js new file mode 100644 index 000000000..fe37df801 --- /dev/null +++ b/pr/509/docs/search.index/4a2f3dcae3f4.js @@ -0,0 +1 @@ +rn_("AQEA9coJAAABAAEAAQABAPMAAWnzOA0AAFIHAQABAQGgQAAAFAdl8ncUAAABAAEEAqAgAAAR1aAwAAASmGJoAdwPAADnAACAxaAQAAANOaAwAAAQhwQQKAE=") \ No newline at end of file diff --git a/pr/509/docs/search.index/4a39f4f617e6.js b/pr/509/docs/search.index/4a39f4f617e6.js new file mode 100644 index 000000000..9e10f220b --- /dev/null +++ b/pr/509/docs/search.index/4a39f4f617e6.js @@ -0,0 +1 @@ +rn_("AQEA9ToGAACADAEAowABAAEAADswAAABAAAHAAMA8QEBAMAMAQApEAMAAYMCsHATgAABsIATtgABY3MBBQD0JRMAAAEAjgABAPMIAWP1YhAAAAEAugABAAEAAQABoDAAABGIdPLKEAAAAQABgAKgAAAAEJygAAAAEJ00NvMDAXYBTA0AAJcAAACFsKATCQABsCAMfgABsIAS6AABCQQGOjAAAAEAAAAAAAkAEAAAAJQHlQeWB4wMBRBeEZkSmhKbEj0UAQIA9BETAAABAI0AAQABAwD0RRMAAAEAkgABAPMAAmJ1OjAAAAEAAAAAABUAEAAAANUH1gfXB+oP6w+OEI8QrRCuED0RPhHoEekRDxMQExwTHRMsE1YTVxOdE54TAQEA9DYGAAABAAUHAQC3AACAhbEAFCMAAbAAB98AAQwIRAEIADswAAABAAAFAAEAyhIFANcNAACIsEARLQABoCAAAA1WuQALOzAAAAEAAA8ABQBvAAEAjAcHAO4HAQCBEAEAKREBAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/4a715899379d.js b/pr/509/docs/search.index/4a715899379d.js new file mode 100644 index 000000000..7dea7ccde --- /dev/null +++ b/pr/509/docs/search.index/4a715899379d.js @@ -0,0 +1 @@ +rn_("AQEAOjAAAAEAAAAAAAgAEAAAAIwJgxAiESMRJBHgE+ETYxRoFAEBAqBAAAAUTaAwAAARKGVo9YQJAAAwAAEDAQAiBQECAPTPEQAAAQABAAEAE4ECsCAR0wABYXPzgAJubwECAPS4EAAAAQABAAEAEwACoNAAABRSY3PyLhAAAAEA84ADZWlv") \ No newline at end of file diff --git a/pr/509/docs/search.index/4b1b519a3f3d.js b/pr/509/docs/search.index/4b1b519a3f3d.js new file mode 100644 index 000000000..66500fbe5 --- /dev/null +++ b/pr/509/docs/search.index/4b1b519a3f3d.js @@ -0,0 +1 @@ +rn_("AQQBoEAAABQHZfJ3FAAAAQABAQA7MAAAAQAABgABAJoUBgDzBgFl9E4UAAABAC8AAQAzAwOgYAAAE/VhcnXzhhAAAFwCAQAjgAKAEXoAEAFkcg==") \ No newline at end of file diff --git a/pr/509/docs/search.index/4b1ce8d67124.js b/pr/509/docs/search.index/4b1ce8d67124.js new file mode 100644 index 000000000..491f65c5d --- /dev/null +++ b/pr/509/docs/search.index/4b1ce8d67124.js @@ -0,0 +1 @@ +rn_("AYQCsAARfQABsAARfwABNDbzAgFh8skMAAABAAEAAPW1EgAAbgABAEIBAQDzAAFk88MSAAABAKcAY4ADoCAAAAl8YWVv") \ No newline at end of file diff --git a/pr/509/docs/search.index/4bc8bd135a43.js b/pr/509/docs/search.index/4bc8bd135a43.js new file mode 100644 index 000000000..7988b72a1 --- /dev/null +++ b/pr/509/docs/search.index/4bc8bd135a43.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAkAAQBPEQkAAQIAOzAAAAEAABMAAQAwEBMAE4GEoFAAABEQoBAAAAyAoCAAAA07HEAAE4ACsFATbQABYW4zgAOwMA/aAAFpc3Q=") \ No newline at end of file diff --git a/pr/509/docs/search.index/4c7fab31325f.js b/pr/509/docs/search.index/4c7fab31325f.js new file mode 100644 index 000000000..79768e50f --- /dev/null +++ b/pr/509/docs/search.index/4c7fab31325f.js @@ -0,0 +1 @@ +rn_("AQgA9IEQAAABAKcAAQABAAGggAAAFJBm8n0JAAACCgEHAPVPEwAAAQABAAEAAQAjgAKgUAAAEWJhb/MCAmZzOzAAAAEAAAgABADYBwAACwgFAE4NAACaEQAAM4ADoEAAAAxqYWVv") \ No newline at end of file diff --git a/pr/509/docs/search.index/4c970f423583.js b/pr/509/docs/search.index/4c970f423583.js new file mode 100644 index 000000000..25b185f66 --- /dev/null +++ b/pr/509/docs/search.index/4c970f423583.js @@ -0,0 +1 @@ +rn_("AQcA9PsRAAABAAEAAQAjAQKgcAAAEfphcgEFCAAAAQMAOzAAAAEAAAgAAgC4DwYARxMBAPMAAmN0OzAAAAEAAFYAJwBKBQsACQYAAJEJAQCBDAAAhwwCADoNAABFDQIAVw0BAN4PAADjDwEA5g8BAOwPAAByEAIAeRABAH4QAQCeEAAADxEAABURAABgEQEAbREBAIERAQCFEQIA3hEBAPERAgD3EQAApRIAAK4SAQCzEgEA+RIBAAkTAQANEwEALRMBAD0TAwBDEwEAoRMBALITAAAdFAAAQhQEAEsUAgA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/4dcbd5c1822e.js b/pr/509/docs/search.index/4dcbd5c1822e.js new file mode 100644 index 000000000..64105ebe7 --- /dev/null +++ b/pr/509/docs/search.index/4dcbd5c1822e.js @@ -0,0 +1 @@ +rn_("AQQBoEAAABQHZfJ3FAAAAQABAQA7MAAAAQAABgABAJoUBgDzBgFl9E4UAAABAC8AAQAzAQOgYAAAE/VhcnXzhhAAAFwCAQA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/4dd7e14fd0c3.js b/pr/509/docs/search.index/4dd7e14fd0c3.js new file mode 100644 index 000000000..97fbf9377 --- /dev/null +++ b/pr/509/docs/search.index/4dd7e14fd0c3.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgB1EwAA2hMFACOIArBAFEsAAWVzAQQBsDATRwABY/OFEQAAAQABAAEFADswAAABAAAIAAMA9BECAO8SBAAMFAAA8wEDY2Vw8xkNAAABAAEAAQIA9p0JAABSAJ0GAQCuAAEA84ACbnI=") \ No newline at end of file diff --git a/pr/509/docs/search.index/4e1e65dd0971.js b/pr/509/docs/search.index/4e1e65dd0971.js new file mode 100644 index 000000000..7f3411766 --- /dev/null +++ b/pr/509/docs/search.index/4e1e65dd0971.js @@ -0,0 +1 @@ +rn_("AYACsHASyAABoLAAABP+ZW8jAQOwcBH4AAGAE2kAEO5tbnM7MAAAAQAAGgAGAPUHAQB0CQUAUg0BAE8RCQCLEQAASRMFAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/4e81395114a0.js b/pr/509/docs/search.index/4e81395114a0.js new file mode 100644 index 000000000..2bc18d00e --- /dev/null +++ b/pr/509/docs/search.index/4e81395114a0.js @@ -0,0 +1 @@ +rn_("AQAA9L4JAAABAIgHAQABCQGggAAAFJBmAX8TAAABAgD13hAAAAEAAQBHAgEA8wACaXU6MAAAAQAAAAAABwAQAAAAvBDdEsITwxPzE/QTEhQTFAEIAPUlFAAAAQABAAEAAQDzAgFm9aEQAAABAAEAAQABABOAArCgFGkAAXJ08wACYXPzzhAAAAEAAQDzgAJ0dg==") \ No newline at end of file diff --git a/pr/509/docs/search.index/4f1c4cfa01fd.js b/pr/509/docs/search.index/4f1c4cfa01fd.js new file mode 100644 index 000000000..92890cd08 --- /dev/null +++ b/pr/509/docs/search.index/4f1c4cfa01fd.js @@ -0,0 +1 @@ +rn_("AYkCsCAUUAABoDAAABRaaXATAAOgIAAAExmggAAAEyxlaXX1bQwAADQAAQABAAEAAYYCsJAUdQABoAAAABKpYmQBgAKwMBMNAAGgMAAAEPlydtcAAAKFoDAAABB4oDAAABNiEAETOjAAAAEAAAAAAAkAEAAAAH4JfwmACbIJswmQEJUQSRFKEeoSAQMBsDATJwABc/POEAAAAQABAAEAADswAAABAAAKAAMAcRECAKUTAwCCFAMA0wCEsCAIjgABEBgB9c4MAAABAAEAAQABAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/4f1dbded47de.js b/pr/509/docs/search.index/4f1dbded47de.js new file mode 100644 index 000000000..b5a28a1cf --- /dev/null +++ b/pr/509/docs/search.index/4f1dbded47de.js @@ -0,0 +1 @@ +rn_("AQQBoEAAABQHZfJ3FAAAAQABAQA7MAAAAQAABgABAJoUBgDzBgFl9E4UAAABAC8AAQAzBQOgYAAAE/VhcnXzhhAAAFwCAQA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/4f6d4bec726c.js b/pr/509/docs/search.index/4f6d4bec726c.js new file mode 100644 index 000000000..b21160d05 --- /dev/null +++ b/pr/509/docs/search.index/4f6d4bec726c.js @@ -0,0 +1 @@ +rn_("AQYA9KUTAAABAAEAAQABDwD0ghQAAAEAAQABAPOHAmhtAQAA9PsSAAABAAEAAQDzBAFk9KESAAABAAEAAQAjh4SwoBQ1AAGwQBL5AAGwcBPjAAEVgAABAAGgQAAAEJ5lOzAAAAEAAAcAAgD3BwUAiAgBABOAArBQEFkAAWRyAQkA9cQTAAABAAEAcwABALcCAICGsCASrAABQ9fuukNXEUkB") \ No newline at end of file diff --git a/pr/509/docs/search.index/501107c77886.js b/pr/509/docs/search.index/501107c77886.js new file mode 100644 index 000000000..e8002f902 --- /dev/null +++ b/pr/509/docs/search.index/501107c77886.js @@ -0,0 +1 @@ +rn_("AYACoGAAABPwoKAAABPiYWkBCwD0ghMAAAEAAQABAAEEAPSwEAAAAQABAAEAI4ACsEARBgABbHLzAANjZHMB/BAAAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/5017d5163d40.js b/pr/509/docs/search.index/5017d5163d40.js new file mode 100644 index 000000000..7f5854574 --- /dev/null +++ b/pr/509/docs/search.index/5017d5163d40.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABM3c/TgDwAAzAMBAAIAAQECsGAUIQABsEAUWwABZHQ7MAAAAQAADQAGAOsSAwBUEwEAvxMBANYTAQBQFAEAdRQBAAEBA7BwEfgAAbCAEsgAAYATaQAQ7m1uczswAAABAAALAAIAdAkFAEkTBQCjgISgsAAAE66xcBSUAAEIQIIBAISgcAAAEo+gcAAAEQ+xABQhAAGwABMrAVYCIQHyEgYAAAEAZwQAAIdNy9XBgi6gEAAABkSwMBMeATygQAAAETIRwQw7MAAAAQAABgACAHUTAADaEwUA") \ No newline at end of file diff --git a/pr/509/docs/search.index/5041ebb4629e.js b/pr/509/docs/search.index/5041ebb4629e.js new file mode 100644 index 000000000..21176cf46 --- /dev/null +++ b/pr/509/docs/search.index/5041ebb4629e.js @@ -0,0 +1 @@ +rn_("AYACsHARdgABsCAUOwABYWQBBQD0NBMAAAEAkgABAPMAAWMBoBIAAEOAA7AwE4cAAbCgFCEAAWFzeQEGAPQWEQAAAQC2AAEAk4CEoAAAABFdsSAUfgABCSAB84ACZWk=") \ No newline at end of file diff --git a/pr/509/docs/search.index/50ff48e6df46.js b/pr/509/docs/search.index/50ff48e6df46.js new file mode 100644 index 000000000..4a0ce8258 --- /dev/null +++ b/pr/509/docs/search.index/50ff48e6df46.js @@ -0,0 +1 @@ +rn_("AYQCoCAAABEnoDAAABGMaXQBAQA7MAAAAQAAhQACAP4HAAD0CYQA8wACZWz1WQUAAKkCQwk7AgEAAQIAOzAAAAEAAOIAAgBpDAAAXA3hAMcAAACFsLATLQABoBAAABOusDANWgABFDACOzAAAAEAAA4BCwDAAQYAOwYAAGYG4QDNBwcATxACAJsRDQDCEQIAlBIBABcTAQBnEwEA+RMCAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/51394b8419e6.js b/pr/509/docs/search.index/51394b8419e6.js new file mode 100644 index 000000000..6d042c6c9 --- /dev/null +++ b/pr/509/docs/search.index/51394b8419e6.js @@ -0,0 +1 @@ +rn_("AYACsAARfQABsAARfwABNDYBBAGgoAAAFKF38pEUAAABAAELAPQDFAAAAQABAAEAI4ACoIAAABQrZW9jAgOgYAAAE8FlbnABfQwAAPMAAmF2AW0HAAA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/5188e972bd4c.js b/pr/509/docs/search.index/5188e972bd4c.js new file mode 100644 index 000000000..617f51e5a --- /dev/null +++ b/pr/509/docs/search.index/5188e972bd4c.js @@ -0,0 +1 @@ +rn_("AQABoDAAAA0caQFiEwAAAQABsDARSwABbvLjBwAAAQABAQDzYQgAAGIHAQATAAKgUAAAEK9lc/UgBAAAAQABAAEAAQABhAKwoBP/AAGwoBQBAAFjcvMBAXTz5BEAAOIAAQABAwGwYBRJAAFmOzAAAAEAAAYAAwBkDAIAchMBAPcTAQDzgAJhZfOAhAAoBQ==") \ No newline at end of file diff --git a/pr/509/docs/search.index/51a24df5c4b4.js b/pr/509/docs/search.index/51a24df5c4b4.js new file mode 100644 index 000000000..57912aee2 --- /dev/null +++ b/pr/509/docs/search.index/51a24df5c4b4.js @@ -0,0 +1 @@ +rn_("AQEBsIATVgABdfLECQAAAQAjgAKwEAnCAAEzNgECAPS+CQAAAQCIBwEAAQIA86cMAAABAAEAE4ACsBAJvAABaXUBAgA7MAAAAQAACwACALgTBQBtFAUA8wkBbTswAAABAAAMAAMAbBEAANcSBQAtFAUAdxAAAImgMAAAE86gcAAAEfegIAAACcCwMBG7AAGwIAfcAeUzhQo7MAAAAQAAHAIEANsBAAACAiYBAAgAAHkK8wA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/5206354980a8.js b/pr/509/docs/search.index/5206354980a8.js new file mode 100644 index 000000000..ac07c5b3a --- /dev/null +++ b/pr/509/docs/search.index/5206354980a8.js @@ -0,0 +1 @@ +rn_("AYACoIAAABLFsHAUEgABYXMBAAGggAAAFJBm8n0JAAACCgEHAPVPEwAAAQABAAEAAQAjgAKgUAAAEWJhb/MAAmZzOzAAAAEAAAgABADYBwAACwgFAE4NAACaEQAA84ACZHk=") \ No newline at end of file diff --git a/pr/509/docs/search.index/5240bbf87b4a.js b/pr/509/docs/search.index/5240bbf87b4a.js new file mode 100644 index 000000000..55edbeac3 --- /dev/null +++ b/pr/509/docs/search.index/5240bbf87b4a.js @@ -0,0 +1 @@ +rn_("AQcA9cQTAAABAAEAcwABAAEHAPPOEAAAAQABAAGAAqAAAAAH5aAQAAAJjWR48wEBZfInEAAAAQDnAAAAhbBgDTYAAbBADLMAATIIAfRrBQAAcAJSCQEA") \ No newline at end of file diff --git a/pr/509/docs/search.index/5244d6639a96.js b/pr/509/docs/search.index/5244d6639a96.js new file mode 100644 index 000000000..08b390abe --- /dev/null +++ b/pr/509/docs/search.index/5244d6639a96.js @@ -0,0 +1 @@ +rn_("AQEBsFAP6AABY/RFBQAAAQABAL8AAQABoIAAABSQZgF/EwAA8wYBc/JODQAATAQzAAOgYAAAFHRjaXPy0xEAAAEA") \ No newline at end of file diff --git a/pr/509/docs/search.index/52c94dd0f0aa.js b/pr/509/docs/search.index/52c94dd0f0aa.js new file mode 100644 index 000000000..21d6dad0a --- /dev/null +++ b/pr/509/docs/search.index/52c94dd0f0aa.js @@ -0,0 +1 @@ +rn_("AQQBsAATFQABczswAAABAAALAAIAjRMHAHkUAwBHAAAFhqCAAAATb7EAFIYAAbBAEtEAAbCAFJQAAbBQExcAARUAFvTEDgAAAQABAAEAQwCEoCAAAAmwoNAAABN5oDAAAAmOFQAE8+ABAAABAB4G") \ No newline at end of file diff --git a/pr/509/docs/search.index/52d9d2ca2fb3.js b/pr/509/docs/search.index/52d9d2ca2fb3.js new file mode 100644 index 000000000..600d77e3f --- /dev/null +++ b/pr/509/docs/search.index/52d9d2ca2fb3.js @@ -0,0 +1 @@ +rn_("AYICoFAAABLQoQAAABR0Y3QjAAKwQA/jAAFldTswAAABAAA3ABYAUwcBAGcMAQBADQEA6A8BAGUQBQCWEAUADREBAGoRAQD0EQIAvxIDAO8SBAAfEwEAJRMBADQTAQC0EwEAxxMBAOUTAQDpEwEADBQAADMUAQA3FAEAVhQAAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/52e3970e8139.js b/pr/509/docs/search.index/52e3970e8139.js new file mode 100644 index 000000000..3d0d29483 --- /dev/null +++ b/pr/509/docs/search.index/52e3970e8139.js @@ -0,0 +1 @@ +rn_("AQMA9NMMAAABAAEAAQABAAGwUBQhAAFyAWUFAABTAYSwQBKuAAGwQBRbAAGYAAQ6MAAAAQAAAAAAKAAQAAAAUgRTBFQEMQaPCZAJtwnlCSIN3Q9KEEsQWBCHEN4Q3xDgECcRjxLrEuwS7RLuEhkTJxMoE1QTVRO/E8ATxBPFE8YT1hPXEzkUOhRQFFEUdRR2FA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/52f9dda67630.js b/pr/509/docs/search.index/52f9dda67630.js new file mode 100644 index 000000000..1f668f34a --- /dev/null +++ b/pr/509/docs/search.index/52f9dda67630.js @@ -0,0 +1 @@ +rn_("AQUA9CUTAAABAI4AAQDzAAFj9WIQAAABALoAAQABAAEJAPNdFAAAAQA1AAENAaCgAAAUoXfykRQAAAEAAYECsIAUawABoGAAABSPZHM3AQCEhaBAAAAUQLBgFBQATAKQCiOAAqBwAAARXmRyAQsBoEAAABREZfJkFAAAAQATgAKwMBB+AAFkcvMCAWX0aggAAAEAAQABACOAArAgDGIAAWV5AQoA87ASAAABAAEAAQsAOzAAAAEAAAcAAQCNEwcAAQIA9OkTAAABAEkAAQC3AACAhbBwEuQAAbDQFIYAAWAgBgEGAPO8EgAAAQABAAEDAbBAFFsAAXTyvxMAAAEAI4UCoEAAABPvYnMjAgOgQAAAEQSgUAAAEW9hYmb3igkAAAEARQD8BgEAXgABAPOAAmlwAQQA83sQAAABAAEA8wABcDswAAABAAAMAAEAbgcMACOAA6AwAAAMcbBwE6EAAWFlafOAA2RtbgEAADowAAABAAAAAAAHABAAAACVCb0MphKnEvsS/BL9Ev4S8wIBZDswAAABAAAJAAMA2hEDAKESAwDTEgEAI4ACoJAAABQ3aW8BgAOwMBNWAAGAE5cAEAGwUBEgAAFjc3jzAAFlAWwFAADnFQAAiaAAAAAMy6BAAAANH7BADSAAAZBIXQHtAQAA") \ No newline at end of file diff --git a/pr/509/docs/search.index/550e643162da.js b/pr/509/docs/search.index/550e643162da.js new file mode 100644 index 000000000..19f8b57b4 --- /dev/null +++ b/pr/509/docs/search.index/550e643162da.js @@ -0,0 +1 @@ +rn_("AQAA9MYHAAABAAEAAQABhQKwcBP8AAGwMBNUAAFlcxMAA7BAD+YAAaBgAAARi2JlczswAAABAAASAAQAQgYBAFUHCgBdEAEAsBADAAECAPNNBgAAvQkBAAEDAPXeDwAAmwABAAQAAQABCgGgQAAAFERl8mQUAAABACMABKAwAAARHKCQAAATerBgEqoAATBjaGn1+hEAAIgBAQABAAEA84ACZHLnBAAAh7AQCXoDFLAgDGAA+aAwAAAQAhJBODswAAABAAASAAYAgQACADUGAABiBgMAJwgFAGoIAwBqDAAA") \ No newline at end of file diff --git a/pr/509/docs/search.index/564d23e1fbb1.js b/pr/509/docs/search.index/564d23e1fbb1.js new file mode 100644 index 000000000..357a02de7 --- /dev/null +++ b/pr/509/docs/search.index/564d23e1fbb1.js @@ -0,0 +1 @@ +rn_("AQcAOzAAAAEAAIUAAgD4EAAAABKEAAEDADswAAABAACEAAEAPg6EAAECADswAAABAAAHAAEAmQwHAPMAA2JpdDswAAABAACFAAIAQwUAAH0FhAA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/566273acdbeb.js b/pr/509/docs/search.index/566273acdbeb.js new file mode 100644 index 000000000..7def42a88 --- /dev/null +++ b/pr/509/docs/search.index/566273acdbeb.js @@ -0,0 +1 @@ +rn_("AQAA9iARAAABAGwDAQAKAAEAAQAAOzAAAAEAAAYAAgB1EwAA2hMFACOIArBAFEsAAWVzAQ0BsEAURQABZfUNFAAAAQABAAEAAQABBQA7MAAAAQAACAADAPQRAgDvEgQADBQAANcBAACFEGLzBX2FFIQC8xkNAAABAAEA") \ No newline at end of file diff --git a/pr/509/docs/search.index/56ea93cff34a.js b/pr/509/docs/search.index/56ea93cff34a.js new file mode 100644 index 000000000..25bbb79c3 --- /dev/null +++ b/pr/509/docs/search.index/56ea93cff34a.js @@ -0,0 +1 @@ +rn_("AQIBsEAToQABZfJRBwAAAQABBADzexAAAAEAAQBDAAOwQBMJAAGxABPlAAFlZnA7MAAAAQAAJAAGAPcBAABgBwwAbgcMALgPBgAQEQAARxMBAGOAA7AwDG4AA2FlaQ==") \ No newline at end of file diff --git a/pr/509/docs/search.index/57e0023cd6b3.js b/pr/509/docs/search.index/57e0023cd6b3.js new file mode 100644 index 000000000..32f5191cc --- /dev/null +++ b/pr/509/docs/search.index/57e0023cd6b3.js @@ -0,0 +1 @@ +rn_("AQYA9BYRAAABALYAAQATgQOgAAAAEV2xIBR+AAFhZG4BFAD1nBQAAAEAAQABAAEAAQUBsIAUPgABdjswAAABAAAHAAIAyBEEAKkTAgAjgAKwcBHuAAFiZFcAAAGGoMAAABN0oKAAABOWoJAAABNhsFAQkQABBeAC9KIHAAABAAEAzwwBBgDzwxIAAAEApwABAAGgYAAAD/pyAVUEAADXAQCAxaAwAAAMagAAigo=") \ No newline at end of file diff --git a/pr/509/docs/search.index/57f16691bc95.js b/pr/509/docs/search.index/57f16691bc95.js new file mode 100644 index 000000000..9a080b8c3 --- /dev/null +++ b/pr/509/docs/search.index/57f16691bc95.js @@ -0,0 +1 @@ +rn_("AQAA9GMTAAABAAEAAQATAgKwgBM4AAFzdvTsCQAAAQD2CQEA8wABYwG8DAAAAQICoLAAABQ3oLAAABQ4Y3Pz0xAAAAEAWwABgQKwUBHqAAGgwAAAFApoafOCAmVp84ACZW8=") \ No newline at end of file diff --git a/pr/509/docs/search.index/59cb3d108e66.js b/pr/509/docs/search.index/59cb3d108e66.js new file mode 100644 index 000000000..ae49374cf --- /dev/null +++ b/pr/509/docs/search.index/59cb3d108e66.js @@ -0,0 +1 @@ +rn_("AQEBoEAAABREZfJkFAAAAQABgAOgMAAAEpGw4BQjAAGwkBRmAAFhY22HAQCAhrAQES0AAbAQEU0AAaCgAAAUUqDwAAAUczFJAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/5a87fbcb1ab9.js b/pr/509/docs/search.index/5a87fbcb1ab9.js new file mode 100644 index 000000000..a9d4056cf --- /dev/null +++ b/pr/509/docs/search.index/5a87fbcb1ab9.js @@ -0,0 +1 @@ +rn_("AQEA9MYHAAABAAEAAQABAQGgQAAAEYhz9YwMAADSBDsBAQABABOCAqAgAAANVmVyI4ADoEAAAAyLsDAJqwABZW90AQAA9MIHAAABAAEAAQABAgD0ixMAAAEADwABABOAArBAFDsAAWJ284ACb3gBCgD1xBMAAAEAAQBzAAEAAYECoCAAABD7sFASrAABYW8BBAGgAAAAE8lzAfoQAABjAwOwUBFqAAFjZXP2hQkAAAEAAQAuAAEA3AgBAQD0Iw0AAAEAAQABACOAArAwEysBVnJ08wCGAcAH8tEBAACpBg==") \ No newline at end of file diff --git a/pr/509/docs/search.index/5aa9de2b1ec7.js b/pr/509/docs/search.index/5aa9de2b1ec7.js new file mode 100644 index 000000000..780fde6b5 --- /dev/null +++ b/pr/509/docs/search.index/5aa9de2b1ec7.js @@ -0,0 +1 @@ +rn_("AQQBsAATFQABczswAAABAAALAAIAjRMHAHkUAwBHAAAEhqCAAAATb7EAFIYAAbBAEtEAAbCAFJQAAbBQExcAARUAFvTEDgAAAQABAAEA") \ No newline at end of file diff --git a/pr/509/docs/search.index/5b9b294539ab.js b/pr/509/docs/search.index/5b9b294539ab.js new file mode 100644 index 000000000..ef15ea9bc --- /dev/null +++ b/pr/509/docs/search.index/5b9b294539ab.js @@ -0,0 +1 @@ +rn_("AQMA9JMJAAABACYDAQABAwDztwwAAAEAAQDzgAJpbwEBAbBQEQ0AAWP06AcAAAEAAQD8ASOBAqBQAAAQ0mNuAQQAOzAAAAEAAAcAAwDCEQIAZxMBAPkTAgABAQA6MAAAAQAAAAAABwAQAAAA5RHmERoTGxNwE3ETFxQYFPMJAWkBDBEAANcCAACHsMATXAABoFAAAA/JoRAAABQ9FAEXOzAAAAEAACMAAQCZASMA") \ No newline at end of file diff --git a/pr/509/docs/search.index/5c1fe6b3e4a8.js b/pr/509/docs/search.index/5c1fe6b3e4a8.js new file mode 100644 index 000000000..417307861 --- /dev/null +++ b/pr/509/docs/search.index/5c1fe6b3e4a8.js @@ -0,0 +1 @@ +rn_("AYACoFAAABFGoFAAABKFYXUBgwKxABSWAAGw0BSIAAFrbQEEAaCgAAAUoXfykRQAAAEAAQsA9AMUAAABAAEAAQAjgAKggAAAFCtlb2MDA6BgAAATwWVucAF9DAAAI4ACoEAAABDZbXABCgDz5RMAAAEAcAATgAKiEAAAFKFsb/cAAACFoQAAABRYBIEDOzAAAAEAADsABABRBgUA2gcAAC0IMwCEEQAA") \ No newline at end of file diff --git a/pr/509/docs/search.index/5efb7b1e3360.js b/pr/509/docs/search.index/5efb7b1e3360.js new file mode 100644 index 000000000..d6d3a8e9c --- /dev/null +++ b/pr/509/docs/search.index/5efb7b1e3360.js @@ -0,0 +1 @@ +rn_("AQYA9M8TAAABAFMAAQAjgAKhEAAAFIFjchOAAqAgAAAJjmh5AQEBoGAAABR0c/LTEQAAAQAjgAKwIAnwAAFldQGAArAgEC4AAaCQAAAUKmFlAQcBsEAURQABZfUNFAAAAQABAAEAAQABBAGwABMVAAFzOzAAAAEAAAsAAgCNEwcAeRQDAEcAAASGoIAAABNvsQAUhgABsEAS0QABsIAUlAABsFATFwABFQAW9MQOAAABAAEAAQCDAISwQBSKAAGwMBMHAAGwMBOhAAHAAAM6MAAAAQAAAAAACQAQAAAAXAURERIRsxG0EbkRuhHtE+4TWRQBAwGwQBRbAAF08r8TAAABACOFAqBAAAAT72JzAYACoAAAAAmwoCAAABEEZ3ABAQD0zxEAAAEAAQABAPOAAmRst+gAAIyg0AAAE3mgUAAAEW+gkAAAE5WwUBL2AAGgMAAADMK3mAc7MAAAAQAAYAATAOABAQDmAQEALQQDAF4GAABRBwEA7AcBAP8HAACKCQEAlwkAAKMJAADQCQAA2Aw9ACgNAAD7DwEAzBABAOYQAQArEQEAqhEEAKwSAQA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/5f36959d2de3.js b/pr/509/docs/search.index/5f36959d2de3.js new file mode 100644 index 000000000..833cc656d --- /dev/null +++ b/pr/509/docs/search.index/5f36959d2de3.js @@ -0,0 +1 @@ +rn_("AQAAOjAAAAEAAAAAAAoAEAAAAPQP9Q/aENsQYhHsEe0R/xIAE4YUhxTzAQF58vETAAABACMAA6BAAAARgaBQAAAUPWVvc/LYDwAAuAI=") \ No newline at end of file diff --git a/pr/509/docs/search.index/611ff1ea6784.js b/pr/509/docs/search.index/611ff1ea6784.js new file mode 100644 index 000000000..60f8e7a88 --- /dev/null +++ b/pr/509/docs/search.index/611ff1ea6784.js @@ -0,0 +1 @@ +rn_("AQIBoQAAABRfZPLrBwAA/gEBBgGgMAAAEed0AZYTAAABAwGwUBLmAAFl9JYJAAAoAwEAxgMBAQD0SQYAAAEAAQABAAEBAaEAAAAUU2TySAYAADcClwcAAIewoBLkAAGg4AAAFCoegArzJwQAAAEAAQA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/61b4c5b2a89e.js b/pr/509/docs/search.index/61b4c5b2a89e.js new file mode 100644 index 000000000..b485228aa --- /dev/null +++ b/pr/509/docs/search.index/61b4c5b2a89e.js @@ -0,0 +1 @@ +rn_("AQMBoEAAABHxZfNPDQAAygMBAAEGAPQWEQAAAQC2AAEAE4EDoAAAABFdsSAUfgABYWRuARQA9ZwUAAABAAEAAQABAAEFAbCAFD4AAXY7MAAAAQAABwACAMgRBACpEwIAI4ACsHAR7gABYmRXAAABhqDAAAATdKCgAAATlqCQAAATYbBQEJEAAQXgAvSiBwAAAQABAM8MAQYA88MSAAABAKcA84ADcnR2AQMAOzAAAAEAAAcAAgClEwMAghQDAPMAAXMBQRQAAPMAAXMB8BEAAGcBAICGoDAAAAyYsAATXAABoBAAAA01EkAO") \ No newline at end of file diff --git a/pr/509/docs/search.index/61fe3550b1f8.js b/pr/509/docs/search.index/61fe3550b1f8.js new file mode 100644 index 000000000..5b55b43bd --- /dev/null +++ b/pr/509/docs/search.index/61fe3550b1f8.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAOIAAgBpDAAAXA3hAPMAAW07MAAAAQAA4gACADsGAABmBuEAAQMAOzAAAAEAAAgAAgC4DwYARxMBAPMAAWM7MAAAAQAAVgAnAEoFCwAJBgAAkQkBAIEMAACHDAIAOg0AAEUNAgBXDQEA3g8AAOMPAQDmDwEA7A8AAHIQAgB5EAEAfhABAJ4QAAAPEQAAFREAAGARAQBtEQEAgREBAIURAgDeEQEA8RECAPcRAAClEgAArhIBALMSAQD5EgEACRMBAA0TAQAtEwEAPRMDAEMTAQChEwEAshMAAB0UAABCFAQASxQCAPOAAnJ3AQkA810UAAABADUAAQwBoKAAABShd/KRFAAAAQABAQDzBxAAAAEAAQDzgAJ0dQGBArCAFGsAAaBgAAAUj2RzNwIAgYagQAAAFECwYBQUAEywMBP/AAECkA4BiAKwABPKAAGwABPMAAE0NjOAA6AwAAARhGRuc2MAA6AQAAAFa2FlbwF7AAAA") \ No newline at end of file diff --git a/pr/509/docs/search.index/6386aba6a284.js b/pr/509/docs/search.index/6386aba6a284.js new file mode 100644 index 000000000..32f7059fa --- /dev/null +++ b/pr/509/docs/search.index/6386aba6a284.js @@ -0,0 +1 @@ +rn_("AQAA9AoRAAABAIIBAQAjgQKgYAAAEyxiaQEAAPT/DwAAAQDoAP8AI4ECsFATPwABZXMTgAKwAAfcAeVhcgGJArAgFFAAAaAwAAAUWmlwAQIA9BETAAABAI0AAQABAwD0RRMAAAEAkgABAPOFAmJ1AQIA9HQRAAABAGsBAQDzgAJpbLcBAACFoCAAABMZEgEMOjAAAAEAAAAAABYAEAAAAP0HigiLCIwIjQhtDKEMogyjDKQMJhDhEFwTXROWExQUXRReFGAUaxRsFI8UkxQ=") \ No newline at end of file diff --git a/pr/509/docs/search.index/6410b272eb51.js b/pr/509/docs/search.index/6410b272eb51.js new file mode 100644 index 000000000..8b52b6784 --- /dev/null +++ b/pr/509/docs/search.index/6410b272eb51.js @@ -0,0 +1 @@ +rn_("AQEBoGAAABR0c/LTEQAAAQABCAD1JRQAAAEAAQABAAEAowCEsCAJ8AABoIAAABLdMEAIOzAAAAEAAAUAAgDSAQAAoRAEAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/64b4fe03993a.js b/pr/509/docs/search.index/64b4fe03993a.js new file mode 100644 index 000000000..00057bcab --- /dev/null +++ b/pr/509/docs/search.index/64b4fe03993a.js @@ -0,0 +1 @@ +rn_("ARMA9ZwUAAABAAEAAQABABOAAqDAAAATNmFvAQICoLAAABQ3oLAAABQ4Y3Pz0xAAAAEAWwABgQKwUBHqAAGgwAAAFApoafOAAmVpI4ACoLAAABN0aXQBBQGwgBQ+AAF2OzAAAAEAAAcAAgDIEQQAqRMCACOAArBwEe4AAWJkdwAAAIagoAAAE5agkAAAE2GwUBCRAAEF4AL0ogcAAAEAAQDPDA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/64d9a6218304.js b/pr/509/docs/search.index/64d9a6218304.js new file mode 100644 index 000000000..c8c639a86 --- /dev/null +++ b/pr/509/docs/search.index/64d9a6218304.js @@ -0,0 +1 @@ +rn_("AQEA9yIRAAABAAEAvAIBAIIABQABAgD0uBAAAAEAAQABABOAAqDQAAAUUmNz84ACZW8jgQKwAAfdAAFsdAEAAPRJBgAAAQABAAEAU4CEoFAAABShsKAT7QABCCAF") \ No newline at end of file diff --git a/pr/509/docs/search.index/665ebf7362b0.js b/pr/509/docs/search.index/665ebf7362b0.js new file mode 100644 index 000000000..9de51c889 --- /dev/null +++ b/pr/509/docs/search.index/665ebf7362b0.js @@ -0,0 +1 @@ +rn_("AYECoCAAABR0sEAQCgABYnABBQD0NBMAAAEAkgABAPMDAWMBoBIAAAEMAPR5FAAAAQABAAEA84ACbnYBgAKwMAyuAAGgMAAADLBpbwEDAaAAAAAUQXMB8BEAAMcDAICGsEAMbwABsAAGDgABkUgB") \ No newline at end of file diff --git a/pr/509/docs/search.index/669a36c47371.js b/pr/509/docs/search.index/669a36c47371.js new file mode 100644 index 000000000..cf731458c --- /dev/null +++ b/pr/509/docs/search.index/669a36c47371.js @@ -0,0 +1 @@ +rn_("AQABoLAAABOyYvNCEwAAHwEBAAGAAqAgAAAP4aAwAAATvmFy8wABdAEKCAAAAQQBoEAAABREZfJkFAAAAQABBQA7MAAAAQAACwAFAGUQBQC/EgEA5RMBADcUAABWFAAA4wGEoDAAAA/fhQACOzAAAAEAAAsABQA+BgIASwcEAK4MAQCKEQAA0BIAAGOAA7AAEAEDQGRucg==") \ No newline at end of file diff --git a/pr/509/docs/search.index/674f02e77287.js b/pr/509/docs/search.index/674f02e77287.js new file mode 100644 index 000000000..f8a140d3a --- /dev/null +++ b/pr/509/docs/search.index/674f02e77287.js @@ -0,0 +1 @@ +rn_("AQQA9lQNAAABAAEDAQAhAQEAAQoA88MSAAABAKcAAQEA96QJAAABAOMGAQBpAAEADAEjgAKwYBPTAAFpbwECAPQWDQAAAQDsBQEAAQUA9OkTAAABAEkAAQD3CAAAiLCQE6MAAaAgAAANGLCQEfIAAQTcAzowAAABAAAAAAAIABAAAAAuBpMJlAm6DLsMAhEDEfQS9RI=") \ No newline at end of file diff --git a/pr/509/docs/search.index/689d2c6c80c6.js b/pr/509/docs/search.index/689d2c6c80c6.js new file mode 100644 index 000000000..4dc41feee --- /dev/null +++ b/pr/509/docs/search.index/689d2c6c80c6.js @@ -0,0 +1 @@ +rn_("AQsBoEAAABQdZfJHFAAAAQABBQD0+xEAAAEAAQABAAGDArAAEPAAAbBQE2cAAWR3E4ACoKAAABPWZXYBAgD0ChEAAAEAggEBAAEAAPT/DwAAAQDoAP8AI4ICsFATPwABZXMBAgD0ERMAAAEAjQABAAEDAPRFEwAAAQCSAAEA84UCYnUBAgD0dBEAAAEAawEBAPOAAmls8wADYnR1OjAAAAEAAAAAAA8AEAAAAP0HigiLCIwIjQgmEOEQlhMUFF0UXhRgFGsUbBSPFJMUAQwA8/kTAAABAAEAAYACsAAJngABoCAAAA1NZG4BBgD0DxMAAAEAjQABAPMDAWL0CBEAAAEAggEBAJcGAACHsEAP/QABoHAAABKXsJATPQABOAkDOzAAAAEAACIAEQBaBgMAvwcBAEINAgBIDQIA9g8BAEgQAQBNEAEAaxAGAK8QAADcEAEAdhMAAJoTAAC+EwAAUxQAAF8UAAB9FAAAjBQAAAEDAPQjEwAAAQBCAQEA84ACY213AQCAxbGAFJwAAUCBBAg=") \ No newline at end of file diff --git a/pr/509/docs/search.index/68b6cd216296.js b/pr/509/docs/search.index/68b6cd216296.js new file mode 100644 index 000000000..ce440d3ca --- /dev/null +++ b/pr/509/docs/search.index/68b6cd216296.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwDCEQIAZxMBAPkTAgABBADzxREAAAEAAQAjAAKgYAAAEuphcvPwEAAAAQC4AQGAAqEQAAAUfbCAFHUAAWl1NwQAgYexUBSYAAGACZoAFzewcBPCAAGhMAAAFIwKQBsBAAA7MAAAAQAAhAABAD4OhAABAgD2iRMAAAEAogBUABYAAQAjgQKgIAAAEQ9ydvOAAmVvI4ACoCAAABKKZHQBAAA7MAAAAQAA5AAEAF8GAACQCOEA1wwAAHARAADzgANkbnY=") \ No newline at end of file diff --git a/pr/509/docs/search.index/69496b573a06.js b/pr/509/docs/search.index/69496b573a06.js new file mode 100644 index 000000000..2adac89ff --- /dev/null +++ b/pr/509/docs/search.index/69496b573a06.js @@ -0,0 +1 @@ +rn_("AYMCoJAAABQZoJAAABQaMjMBgAKgUAAAFEGggAAAE2xjZfMAAnN08ssHAAABAAEAAaCwAAATsmLzQhMAAB8BAQAjgAKwABABA0BkcgEAAbCAFB8AAXDyGxQAAAEAQwADoCAAABFfoEAAABKlZGVz8wYQAAAsAwEAE4ICoCAAABQLZWnzgAJjZQEEADswAAABAAAJAAQA3AkBAKYQAwDiEAEA0xMBALOAhKCQAAAUcxABQg==") \ No newline at end of file diff --git a/pr/509/docs/search.index/69993716acd0.js b/pr/509/docs/search.index/69993716acd0.js new file mode 100644 index 000000000..808726cc7 --- /dev/null +++ b/pr/509/docs/search.index/69993716acd0.js @@ -0,0 +1 @@ +rn_("AYACsIAS9gABsKATbQABaXQBgAKw4BRhAAGwQBBfAAFscgEGAPTqEAAAAQABAAEARwUAAIeg0AAAFD2wUBBdAAGgAAAAE2+w8BQbAAEFkQPyAhAAAKkA") \ No newline at end of file diff --git a/pr/509/docs/search.index/6b029a433070.js b/pr/509/docs/search.index/6b029a433070.js new file mode 100644 index 000000000..5eca30879 --- /dev/null +++ b/pr/509/docs/search.index/6b029a433070.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAABEABQBFBgAAigwAADINAAAWEA0AbxEAAAGEArAAEX0AAbAAEX8AATQ2FwgAAIigIAAAEdWgMAAAEUWg4AAAE5WgMAAAEpiwcA/wAAGgIAAABk7LwAg7MAAAAQAABgEVAJgBAAA8BgAATgYAAHsJAACBCQAAoAkCAGkMAAClDAEAyQwBADUNAABcDeEA3A8AAO0PAQDZEAAAkREAAN4SAQC4EwUA1RMAAFcUAABtFAUAiBQBAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/6d657b4263d8.js b/pr/509/docs/search.index/6d657b4263d8.js new file mode 100644 index 000000000..183c5b399 --- /dev/null +++ b/pr/509/docs/search.index/6d657b4263d8.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAABYACQBlEAUAahEBAPQRAgC/EgEA7xIEAOUTAQAMFAAANxQAAFYUAAAjgAOh8AAAFKGw8BSUAAFjZmcjgAKwUBOjAAFtbgEFAPPlEwAAAQBwAPMEAWMBihEAAAEBAbBwFDsAAXA7MAAAAQAACQACAGIIBwDVEAEAAQIAOzAAAAEAAAsAAgC4EwUAbRQFAPMBAW07MAAAAQAADQAEAB4NAABsEQAA1xIFAC0UBQATAAKgMAAADSJlc/L4EQAAAQABggKwcBQbAAGgQAAAERhpc/cBAICGsJAT/wABkEkI") \ No newline at end of file diff --git a/pr/509/docs/search.index/6fe836e11237.js b/pr/509/docs/search.index/6fe836e11237.js new file mode 100644 index 000000000..65d93a857 --- /dev/null +++ b/pr/509/docs/search.index/6fe836e11237.js @@ -0,0 +1 @@ +rn_("AQABsPAUigABa/b+EwAABQABAAEAAQAlABMAArEAFHcAAWRu8sgSAAABABOAArAQDKoAAWVvAQUAOzAAAAEAAA0AAQCbEQ0AAQIA8xMQAAABAAEAAQIA9A8QAAABAAEAAQABAwA7MAAAAQAACAABAL8QCAABgAKgQAAAERCwwBQ7AAFlcwEBADswAAABAAATAAEAMBATACOAArCQE6wAAWltNzYAAIqgEAAADICgUAAAFAqwcBPrAAGgIAAADTucVRE7MAAAAQAABgADAOEHAADqEAMAqhIBAPOAAmx0") \ No newline at end of file diff --git a/pr/509/docs/search.index/711a3a237476.js b/pr/509/docs/search.index/711a3a237476.js new file mode 100644 index 000000000..ed1f9afbd --- /dev/null +++ b/pr/509/docs/search.index/711a3a237476.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgB1EwAA2hMFACOIArBAFEsAAWVzAQQBsDATRwABY/OFEQAAAQABAAEFADswAAABAAAIAAMA9BECAO8SBAAMFAAA8wMDY2Vw8xkNAAABAAEAAQABsGAT8wABZQEqFAAA8wIBc/aoCQAAAQABAJwHPQATAQECAaBQAAARhHIBJhEAAPOAA2ZtcA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/714a27f59b30.js b/pr/509/docs/search.index/714a27f59b30.js new file mode 100644 index 000000000..a1361835c --- /dev/null +++ b/pr/509/docs/search.index/714a27f59b30.js @@ -0,0 +1 @@ +rn_("AQgA9SUUAAABAAEAAQABAPMAAWY7MAAAAQAABQACANIBAAChEAQAAQIAOzAAAAEAAAsAAgC4EwUAbRQFAPMGAW07MAAAAQAADQAEAB4NAABsEQAA1xIFAC0UBQBjgAOwAAmmAAFlbXM=") \ No newline at end of file diff --git a/pr/509/docs/search.index/7250c12d601c.js b/pr/509/docs/search.index/7250c12d601c.js new file mode 100644 index 000000000..dde7bd191 --- /dev/null +++ b/pr/509/docs/search.index/7250c12d601c.js @@ -0,0 +1 @@ +rn_("AYQCsCARgwEToJAAABQMbXABgAKgMAAAERCgUAAAE7JjcpcAAACFsCATgAABsMAUOwABoGAAABHYEEAOOzAAAAEAABIABgBtBQgA1hEBAOgSAQDPEwEAIxQBAJQUAQA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/72d51fa47912.js b/pr/509/docs/search.index/72d51fa47912.js new file mode 100644 index 000000000..a7023b992 --- /dev/null +++ b/pr/509/docs/search.index/72d51fa47912.js @@ -0,0 +1 @@ +rn_("AYYCoAAAABNBoAAAABNCZHIBgAKgcAAAE86gMAAAERhmdAELAaBAAAAUHWXyRxQAAAEAI4ECsFAQ8AABY3YBAAGwgBQfAAFw8hsUAAABAEMGA6AgAAARX6BAAAASpWRlc/MGEAAALAMBAPMABGlyc3Y7MAAAAQAADwAFAIMHBQDKEgUA9hIBAGwTAABSFAAA") \ No newline at end of file diff --git a/pr/509/docs/search.index/74a202ff2fce.js b/pr/509/docs/search.index/74a202ff2fce.js new file mode 100644 index 000000000..80c6be6af --- /dev/null +++ b/pr/509/docs/search.index/74a202ff2fce.js @@ -0,0 +1 @@ +rn_("AQMA87ASAAABAAEAI4ECsSAUlAABY2sBBAGgQAAAEfFl808NAADKAwEAAQAA9fQQAAABAAEAAQB9AnOAhKAwAAAUHQgwAQ==") \ No newline at end of file diff --git a/pr/509/docs/search.index/74ebe484d8fb.js b/pr/509/docs/search.index/74ebe484d8fb.js new file mode 100644 index 000000000..41b049c45 --- /dev/null +++ b/pr/509/docs/search.index/74ebe484d8fb.js @@ -0,0 +1 @@ +rn_("AYkCsCAUUAABoDAAABRaaXATAAOgIAAAExmggAAAEyxlaXX1bQwAADQAAQABAAEAAYYCsJAUdQABoAAAABKpYmQBAgD0dAwAAAEASgMBAAEAADswAAABAAAGAAIAdRMAANoTBQAjgQKwQBRLAAFlc1OAA7AACboAAWV4eQGAArAgEw0AAbAACbgAAXJ4RwEAAMawwBOhAAGgEAAAEXCwQA0wAAGgMAAAEPkARIAL9AoGAAABACsHAQDXAAAAhaAwAAAQeKAwAAATYhABEzowAAABAAAAAAAKABAAAAB+CX8JgAmyCbMJeQyQEJUQSRFKEeoS") \ No newline at end of file diff --git a/pr/509/docs/search.index/74fff8bb6461.js b/pr/509/docs/search.index/74fff8bb6461.js new file mode 100644 index 000000000..d6864f3e9 --- /dev/null +++ b/pr/509/docs/search.index/74fff8bb6461.js @@ -0,0 +1 @@ +rn_("AQUA9FANAAABAAMDAQABAAD0twwAAAEAAQBmAAECADowAAABAAAAAAAJABAAAADtD+4P2RCREd4S3xLVE1cUiBSJFAEDAPOgCQAAAQABANcBAACGoEAAABHxsIAR1gABGEAHOjAAAAEAAAAAABcAEAAAAPMH9AeCCK0J3AndCU8N2g/bD/oPnxCmEKcQqBCpEOIQ4xAZERoRhRLTE9QT/xMAFA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/7511c2f31675.js b/pr/509/docs/search.index/7511c2f31675.js new file mode 100644 index 000000000..105c5b747 --- /dev/null +++ b/pr/509/docs/search.index/7511c2f31675.js @@ -0,0 +1 @@ +rn_("AQQBsEAURQABZfUNFAAAAQABAAEAAQABAgA7MAAAAQAACwACALgTBQBtFAUA8wEBbTswAAABAAANAAQAHg0AAGwRAADXEgUALRQFAPMBAWXy+BEAAAEAM4CEsBAQkwABoCAAAAxyFEAE") \ No newline at end of file diff --git a/pr/509/docs/search.index/7545996f0dd7.js b/pr/509/docs/search.index/7545996f0dd7.js new file mode 100644 index 000000000..3a4212e77 --- /dev/null +++ b/pr/509/docs/search.index/7545996f0dd7.js @@ -0,0 +1 @@ +rn_("AQoBoIAAABSQZgF/EwAAAQgA9SUUAAABAAEAAQABAPMDAWb1oRAAAAEAAQABAAEA84MCYXMBAgKgIAAAEdWgMAAAEphiaAHcDwAAI4ACoIAAABM2YXDzAAJobQFIBQAA") \ No newline at end of file diff --git a/pr/509/docs/search.index/758f270c34be.js b/pr/509/docs/search.index/758f270c34be.js new file mode 100644 index 000000000..4abaff658 --- /dev/null +++ b/pr/509/docs/search.index/758f270c34be.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAYAAQBjEQYAAQAA96QJAAABAOMGAQBpAAEADAEBggKgIAAAESegMAAAEYxpdPOAA2FncwEBAaBAAAAUHWXyRxQAAAEAI4ACsFAT0wABZnPzgAJpbw==") \ No newline at end of file diff --git a/pr/509/docs/search.index/7643889a294b.js b/pr/509/docs/search.index/7643889a294b.js new file mode 100644 index 000000000..711c270d6 --- /dev/null +++ b/pr/509/docs/search.index/7643889a294b.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgC1EQMAsBICAAEKAbBAFEUAAWX1DRQAAAEAAQABAAEA8wACaXP0fgwAAAEATQABACOBAqDgAAAULGV5") \ No newline at end of file diff --git a/pr/509/docs/search.index/768d33a8e276.js b/pr/509/docs/search.index/768d33a8e276.js new file mode 100644 index 000000000..d4cf5a1b8 --- /dev/null +++ b/pr/509/docs/search.index/768d33a8e276.js @@ -0,0 +1 @@ +rn_("AQIAOjAAAAEAAAAAAA4AEAAAAFcNWA0VEfER8hHzEaUSrhKvErMStBIJEwoTPxNAEwEJAPN6EQAAAQABAAEKAPO8EgAAAQABAAEGAPU/EQAAAQABAAEAAQDzgAJkbnMAhLEgFCEAAQARA/ITEwAAAQABDQD2iRMAAAEAogBUABYAAQABgAKwoBOhAAGgEAAADI1jdQEBAPSwEwAAAQBLAAEA8wABZfLwCQAAAQD3AACAxbAwEL0AAQACqAg=") \ No newline at end of file diff --git a/pr/509/docs/search.index/768fd5752adb.js b/pr/509/docs/search.index/768fd5752adb.js new file mode 100644 index 000000000..15e03e504 --- /dev/null +++ b/pr/509/docs/search.index/768fd5752adb.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABM3c/TgDwAAzAMBAAIAAQECsGAUIQABsEAUWwABZHQ7MAAAAQAADQAGAOsSAwBUEwEAvxMBANYTAQBQFAEAdRQBAAEBA7BwEfgAAbCAEsgAAYATaQAQ7m1uczswAAABAAALAAIAdAkFAEkTBQCjgISgsAAAE66xcBSUAAEIQIJHAQAAhU3L1cGCLrEQFCEAAbAwEx4BPBGBCDswAAABAAAGAAIAdRMAANoTBQA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/77136bfd9216.js b/pr/509/docs/search.index/77136bfd9216.js new file mode 100644 index 000000000..af7df2505 --- /dev/null +++ b/pr/509/docs/search.index/77136bfd9216.js @@ -0,0 +1 @@ +rn_("AYADsAATygABsAATzAABsHAUPgABNDZhAQQA9I0UAAABAAoAAQAjgQKgQAAAEV1tcAEBADswAAABAAAIAAMAxRIAADYTAACaFAYAxwEAAMWgQAAAEGSggAAAEqBABoAIOzAAAAEAABYACAADEAEAFhEBAFkRAwDIEQYAqRMCAPYTAABOFAEAfhQBAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/77277d28d69a.js b/pr/509/docs/search.index/77277d28d69a.js new file mode 100644 index 000000000..d8e343e81 --- /dev/null +++ b/pr/509/docs/search.index/77277d28d69a.js @@ -0,0 +1 @@ +rn_("AQkA810UAAABADUAAQ0BoKAAABShd/KRFAAAAQABgQKwgBRrAAGgYAAAFI9kczcBAIWFoEAAABRAsGAUFABMApAK") \ No newline at end of file diff --git a/pr/509/docs/search.index/779f19a4182a.js b/pr/509/docs/search.index/779f19a4182a.js new file mode 100644 index 000000000..2cef09970 --- /dev/null +++ b/pr/509/docs/search.index/779f19a4182a.js @@ -0,0 +1 @@ +rn_("AQACoDAAABEcoJAAABN6MGj1+hEAAIgBAQABAAEAE4ACoDAAAA/eZHIjAYSwEAl6AxSwIAxgAPmgMAAAEAISABj1NQYAAC0AAQABAAEA") \ No newline at end of file diff --git a/pr/509/docs/search.index/791ca440f256.js b/pr/509/docs/search.index/791ca440f256.js new file mode 100644 index 000000000..99a2906e7 --- /dev/null +++ b/pr/509/docs/search.index/791ca440f256.js @@ -0,0 +1 @@ +rn_("AYACsRAUdwABsBAMqgABZW8BBQA7MAAAAQAADQABAJsRDQABAgDzExAAAAEAAQABAgD0DxAAAAEAAQABAAEDADswAAABAAAIAAEAvxAIAIcHAACHsKATrAABsNAUOwABoFAAABQKjBQROzAAAAEAAAYAAwDhBwAA6hADAKoSAQDzgQJsdPMAAWk7MAAAAQAAhgADAGEEAAD4EAAAABKEAAEFAPNdFAAAAQA1AAEAAaBAAAAQ/GXziAkAAAEADgkTgAKgMAAAD+xkcvMAAWXzewcAAAEAAQDzgAJodAEBADswAAABAACFAAIA+BAAAAAShAATgAKgUAAAEC1pdAEIAPTgEwAAAQCCAAUAEwCEsPAUXwAtsJAUQgABsFATKwFWQIkAOzAAAAEAABIACQB1EAIA5REBABoTAQA7EwEAcBMBANETAQAIFAEAFxQBAFQUAQDXAQAAhrCAEbMAAaBAAAAJrQgwJjowAAABAAAAAAANABAAAACGAIcA6wfpCWAMWQ0KEQsRjRKOEhETEhOfE6AT") \ No newline at end of file diff --git a/pr/509/docs/search.index/7a10444b994a.js b/pr/509/docs/search.index/7a10444b994a.js new file mode 100644 index 000000000..1f720960b --- /dev/null +++ b/pr/509/docs/search.index/7a10444b994a.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAO4AAgAuDQEAyw7sAAEBAaAAAAATN3P1aAUAAHgKzAMBAAIAAQECoGAAABOyoOAAABQMZXQ7MAAAAQAACAABAG0FCAABgAOwIBKGAAGwsBK/AAGwsBLBAAFiY3PzgARjbHR2") \ No newline at end of file diff --git a/pr/509/docs/search.index/7abae646c037.js b/pr/509/docs/search.index/7abae646c037.js new file mode 100644 index 000000000..d833534e1 --- /dev/null +++ b/pr/509/docs/search.index/7abae646c037.js @@ -0,0 +1 @@ +rn_("AQMA9ksNAACuAlkAAQCyAgEAAQQAOzAAAAEAAAoABACWEAUAwRIBAB8TAQA4FAAAAYYCoAAAABNBoAAAABNCZHLzAgF2AWwTAADzgAJlbycBAADHoDAAAA/4oHAAABKSoIAAABQLoNAAABOuoLAAABQLQEUICTswAAABAAAJAAMAVwYCAKsHBACxDAEAI4ACMj5G4AFpZHIBAAA6MAAAAQAAAAAACgAQAAAA0QnSCS0QWhNbE4kTihMsFIAUlhSXFBOAAqAwAAAQrGx084ACYWU=") \ No newline at end of file diff --git a/pr/509/docs/search.index/7c04cb118ed6.js b/pr/509/docs/search.index/7c04cb118ed6.js new file mode 100644 index 000000000..ed82179be --- /dev/null +++ b/pr/509/docs/search.index/7c04cb118ed6.js @@ -0,0 +1 @@ +rn_("AQUA9LoSAAABAKMAAQABCAD0chMAAAEAhAABADMAA7BAE0MAAWFiZTowAAABAAAAAAAJABAAAAC9B74HPg0/DdkPRRCREJIQkxJhEw==") \ No newline at end of file diff --git a/pr/509/docs/search.index/7cb7d80cd09e.js b/pr/509/docs/search.index/7cb7d80cd09e.js new file mode 100644 index 000000000..e884495be --- /dev/null +++ b/pr/509/docs/search.index/7cb7d80cd09e.js @@ -0,0 +1 @@ +rn_("AQwA86kTAAABAAEAAQYA83ERAAABAAEAAYICsGATMgABsNAURwABaXITgAKwEA0zAAFlaRcDAIOIsTAUZAABoGAAABFwoFAAABEFsJATBQABsEARAgABtBAH8wABYfS+CQAAAQCIBwEA") \ No newline at end of file diff --git a/pr/509/docs/search.index/7cb8f8f69140.js b/pr/509/docs/search.index/7cb8f8f69140.js new file mode 100644 index 000000000..386541d99 --- /dev/null +++ b/pr/509/docs/search.index/7cb8f8f69140.js @@ -0,0 +1 @@ +rn_("AQsA9FMUAAAMAB4ADwABCgA6MAAAAQAAAAAABwAQAAAAOxM8E9ET0hMIFAkUVBRVFPOBAm90EwCEsCATLwBXsDARSwABoFAAABEyEDACOzAAAAEAAAgABAB5AAEAngADAOMHAQDuCQAA") \ No newline at end of file diff --git a/pr/509/docs/search.index/7da1c8fa437d.js b/pr/509/docs/search.index/7da1c8fa437d.js new file mode 100644 index 000000000..f48b6a39d --- /dev/null +++ b/pr/509/docs/search.index/7da1c8fa437d.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAAUAAQDKEgUAAQQBoFAAABQKaPKqEgAAAQABAAKwQBOhAAGgoAAAFCplcjowAAABAAAAAAAHABAAAABRB1IH7AftB/sP/A/mEOcQ84ADZ2lz") \ No newline at end of file diff --git a/pr/509/docs/search.index/7df5bd0efad4.js b/pr/509/docs/search.index/7df5bd0efad4.js new file mode 100644 index 000000000..fc419cc8b --- /dev/null +++ b/pr/509/docs/search.index/7df5bd0efad4.js @@ -0,0 +1 @@ +rn_("AQQBsAATFQABczswAAABAAALAAIAjRMHAHkUAwBHAAABhqCAAAATb7EAFIYAAbBAEtEAAbCAFJQAAbBQExcAARUAFvTEDgAAAQABAAEAAQUA8wwQAAABAAEAY4CEsEAQkwABoAAAAAdtAPAA") \ No newline at end of file diff --git a/pr/509/docs/search.index/7f5125453ec9.js b/pr/509/docs/search.index/7f5125453ec9.js new file mode 100644 index 000000000..2565c64a1 --- /dev/null +++ b/pr/509/docs/search.index/7f5125453ec9.js @@ -0,0 +1 @@ +rn_("AYMCsQAUlgABsNAUiAABa20BCgDz5RMAAAEAcAATgAKiEAAAFKFsbzMBhKBgAAAShaEAAAAUWASAAzswAAABAAA0AAIA2gcAAC0IMwA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/7fb39206b4b2.js b/pr/509/docs/search.index/7fb39206b4b2.js new file mode 100644 index 000000000..5e971c3c2 --- /dev/null +++ b/pr/509/docs/search.index/7fb39206b4b2.js @@ -0,0 +1 @@ +rn_("AQQA9LQQAAABAAEAAQABAAA6MAAAAQAAAAAACgAQAAAA9A/1D9oQ2xBiEewR7RH/EgAThhSHFPMBAXny8RMAAAEAAQADoKAAABQ4oCAAABN5sDAR4gABZWl08uQSAAABAAEDADswAAABAAAGAAEAYxEGAAEIAPTgEwAAAQCCAAUAEwEDsJAUQgABsFATKwFWZ2xwOzAAAAEAABIACQB1EAIA5REBABoTAQA7EwEAcBMBANETAQAIFAEAFxQBAFQUAQABgAKwMBHeAAGwMBHgAAFydgECAbAAEqYAAWQ7MAAAAQAABQACANoRAwDTEgEAI4ECoJAAABQ3aW8BBwD0UxQAAAwAHgAPAHcWAAGJsFAT8QABsFASqAELsGATKQABtQkDOjAAAAEAAAAAAA4AEAAAACkNKg0rDckOyg7TENQQDBEvEY0RjhGPEZARIRNZE0OAA6AgAAANH6AQAAANKG1ubwEDAPRyEAAAAQABAA0BAYACoLAAABQKsDATXAABbnRDAAOgIAAAERuwUBHqAAFlaGk7MAAAAQAABgADAGsMAAD7EQMAthMBAPOAAmRyhwsAAAqAAdUAEAGAAdgAEAGgUAAAEYqgUAAAFD2gMAAAEKCgMAAAEKswMWNlaW9zdHV39SUIAAABALIHuAJSAQ==") \ No newline at end of file diff --git a/pr/509/docs/search.index/7fc9cfd68b15.js b/pr/509/docs/search.index/7fc9cfd68b15.js new file mode 100644 index 000000000..90dcb403c --- /dev/null +++ b/pr/509/docs/search.index/7fc9cfd68b15.js @@ -0,0 +1 @@ +rn_("AQIA9AoRAAABAIIBAQABAAD0/w8AAAEA6AD/ACOCArBQEz8AAWVzAQIA9BETAAABAI0AAQABAwD0RRMAAAEAkgABAPOFAmJ1AQIA9HQRAAABAGsBAQDzgAJpbPMAA2J0dTowAAABAAAAAAAPABAAAAD9B4oIiwiMCI0IJhDhEJYTFBRdFF4UYBRrFGwUjxSTFAEMAPP5EwAAAQABAAGAArAACZ4AAaAgAAANTWRuAQYA9A8TAAABAI0AAQDzAwFi9AgRAAABAIIBAQCXBgAAh7BAD/0AAaBwAAASl7CQEz0AATgJAzswAAABAAAiABEAWgYDAL8HAQBCDQIASA0CAPYPAQBIEAEATRABAGsQBgCvEAAA3BABAHYTAACaEwAAvhMAAFMUAABfFAAAfRQAAIwUAAA=") \ No newline at end of file diff --git a/pr/509/docs/search.index/7fe8c77cc844.js b/pr/509/docs/search.index/7fe8c77cc844.js new file mode 100644 index 000000000..e562c7def --- /dev/null +++ b/pr/509/docs/search.index/7fe8c77cc844.js @@ -0,0 +1 @@ +rn_("AQgA9M8TAAABAFMAAQABAgGgYAAAFHRz8tMRAAABAAGAArAgEC4AAaCQAAAUKmFlAQcBsEAURQABZfUNFAAAAQABAAEAAQABAwGwQBRbAAF08r8TAAABACOFAqBAAAAT72JzAQEA9M8RAAABAAEAAQATgAKgMAAAEQRkbDd0AACLsEAToQABoFAAABFvoJAAABOVsFAS9gABoDAAAAzCs5gHOzAAAAEAAF0AEQDmAQEALQQDAF4GAABRBwEA7AcBAIoJAQCXCQAAowkAANAJAADYDD0AKA0AAPsPAQDMEAEA5hABACsRAQCqEQQArBIBAA==") \ No newline at end of file diff --git a/pr/509/docs/search.index/alias/a762150c532c.js b/pr/509/docs/search.index/alias/a762150c532c.js new file mode 100644 index 000000000..f829e4636 --- /dev/null +++ b/pr/509/docs/search.index/alias/a762150c532c.js @@ -0,0 +1 @@ +rd_("") \ No newline at end of file diff --git a/pr/509/docs/search.index/crateNames/2450c9cb4357.js b/pr/509/docs/search.index/crateNames/2450c9cb4357.js new file mode 100644 index 000000000..c00c15628 --- /dev/null +++ b/pr/509/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/509/docs/search.index/desc/0f6717b5c42f.js b/pr/509/docs/search.index/desc/0f6717b5c42f.js new file mode 100644 index 000000000..bd939f366 --- /dev/null +++ b/pr/509/docs/search.index/desc/0f6717b5c42f.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 stream00AfWhich path is now openAhWith path was abandoned.B`Which path had its state droppedAmPath which has changed statusCfPath over which the observed address was reported, \xe2\x80\xa6AlWhich stream is now readableAlWhich stream is now writableAnWhich stream has been finishedAmWhich stream has been stoppedAnType of the Long header packetBcExperimental! Use at your own risk.0ClWhether a stream communicates data in both directions or \xe2\x80\xa60AhContains the error valueAoThe largest representable valueAlThe maximum path ID allowed.10ClConnects to a QUIC perf server and maintains a specified \xe2\x80\xa6BmData flows only from the stream\xe2\x80\x99s initiator0AnWhich directions data flows in0CaDirectionality for which streams have been openedCdDirectionality for which streams are newly availableCjExplicit congestion notification bits to set on the packet0CeThe Explicit Congestion Notification bits for the \xe2\x80\xa61B`The current best RTT estimation.BeThe amount of I/O operations executed0BcLength of QUIC packet being decodedAlLength of the packet payload0BmThe number of bytes the associated buffer hasBbSet a custom TokenLog0CaMinimum RTT registered so far for this estimator.ClConstruct with an approximate maximum memory usage and a \xe2\x80\xa6CjConstruct an endpoint with arbitrary configuration and \xe2\x80\xa6DiCreates a new QlogConfig that writes a qlog trace to the \xe2\x80\xa6CiCreates a new qlog factory that writes files into the \xe2\x80\xa6D`Create a default config with a particular reset_keyCmCreate a default config with a particular handshake token \xe2\x80\xa6CkCreate a default config with a particular cryptographic \xe2\x80\xa6AmConstruct cid from byte arrayoConstruct emptyAeCreate a new StreamIdDjConstruct a state using the given config and current time \xe2\x80\xa600:8AeCreate a new endpointDcCreate a new instance of FixedLengthConnectionIdParserBdCreate a generator with a random key:987DgBegin decoding a QUIC packet from bytes, returning any \xe2\x80\xa67BkConstruct an error with a code and a reasonCgInitialize Random CID generator with a fixed CID length87666BfQUIC transport protocol implementationCkGet SystemTime::now() or the mocked equivalentAhLook up the current time01ChCurrent best estimate of this connection\xe2\x80\x99s latency \xe2\x80\xa6000ChThe ECT(0) codepoint, indicating that an endpoint is \xe2\x80\xa600ChThe ECT(1) codepoint, indicating that an endpoint is \xe2\x80\xa600CaA complete set of keys for a certain packet space0CbA Long packet header, as used during the handshakeChNot attributed to any particular FrameType.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 pathCjThe source address of the datagram(s) contained in the \xe2\x80\xa6mType of errorBkCurrent congestion window of the connection0gPayloadAoReturns the argument unchanged.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000BaCalls U::from(self).000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dNextCoMethod 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\xa6BiConstruct a fresh Controller0AiThe contents of the chunkCiThe total amount of bytes which have been transferred \xe2\x80\xa6BjThe amount of bytes which had been written210BnUnderlying error describing reason for failureBaClose the connection immediately.CnClose all of this endpoint\xe2\x80\x99s connections immediately and \xe2\x80\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.2BcTitle to record in the qlog capture0CmWrite a buffer into this stream, returning how many bytes \xe2\x80\xa6AmSend data on the given streamCcEncode TransportParameters into bufferC`Future produced by Endpoint::acceptClPaths marked as backup will only be used if there are no \xe2\x80\xa6CeChunks returned from RecvStream::read().AmThe initiator of a connectionAdRun as a perf client1CgAttempted to initiate NAT traversal on a closed, or \xe2\x80\xa6CcFuture produced by Connection::open_biAjA new path has been openedChOne or more new streams has been opened and might be \xe2\x80\xa6ChId representing different paths when using multipath \xe2\x80\xa60AbCoding result typeAlThe acceptor of a 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\xa61C`The amount of full chunks which had been written0BlAllows to configure the desired cipher suiteCiHelper to construct an endpoint for use with outgoing \xe2\x80\xa6CcWait for the connection to be closed for any reasonAfCoding related traits.BnAccess the configuration used by this endpointChTraits and implementations for the QUIC cryptography \xe2\x80\xa6BjCreate QUIC error code from TLS alert codeBoTLS configuration used for incoming connections210BkAn underlying crypto (e.g. TLS) layer errorCgDecode a plain header from given buffer, with given \xe2\x80\xa6DiDecode a Self from the provided buffer, if the buffer is \xe2\x80\xa6ChThe destination IP address which was encoded in this \xe2\x80\xa6D`Append the encoding of self to the provided buffer.CmNotify the peer that no more data will ever be written to \xe2\x80\xa6CnFinish a send stream, signalling that no more data will be \xe2\x80\xa6B`Process an incoming UDP datagramAfHeader protection keys0ClIgnore this incoming connection attempt, not sending any \xe2\x80\xa60C`Potentially store a token for later one-time use0CfPerform NSS-compatible TLS key logging to the file \xe2\x80\xa6AhThe offset in the stream0B`Packet deliveries were confirmed0AfPacket protection keys0lRandom valueBcHuman-readable reason for the close000BhHuman-readable explanation of the reasonBeReason that the connection was closedBcReason why this path was abandoned.AjSwitch to a new UDP socketBgReject this incoming connection attempt0AgKey for decrypting dataAmThe remote side of this tuple1AmTLS interface based on rustls0CnHelper to construct an endpoint for use with both incoming \xe2\x80\xa6BkOptional source IP address for the datagram00CgThe current local PathStatus of this path.B`The new status set by the remoteCfThe size of a single datagram in the associated bufferCjStatistics about datagrams and bytes received on this pathCgStatistics about UDP datagrams received on a connection10CfStatistics about datagrams and bytes sent on this pathCjStatistics about UDP datagrams transmitted on a connection10AnMethod for verifying a message0CcNumber of ack-eliciting bytes that may be in 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\xa6ClIndicates how many bytes and chunks had been transferred \xe2\x80\xa60l0-RTT packetCjReturns the length of a CID for connections created by \xe2\x80\xa60CdProvide the length of dst_cid in short header packetCcThe total number of clients which should be createdAlConnect to a remote endpointAeInitiate a 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.C`Statistics about frames received on a connection0CcStatistics about frames transmitted on a connection0DhConstructs cid by reading len bytes from a Buf0DkCreates a new qlog factory that writes files into QLOGDIR, \xe2\x80\xa60BfCreate a generator with a specific keyBjConstruct a VarInt infallibly0BeSucceeds iff x < 2^620AeWhat type we receivedAoWhat handshake type we received10CbWhether 0-RTT is/was possible during the handshakeCmSpecifies the time to wait after completing MTU discovery \xe2\x80\xa60CcReturns Self for use in down-casting to extract \xe2\x80\xa60EhReturns true if the Connection associated with this handle \xe2\x80\xa6CmDuration after an address validation token was issued for \xe2\x80\xa60CeThe local IP address which was used when the peer \xe2\x80\xa6000AmThe local side of this tuple.ClCompute the maximum size of datagrams that may be passed \xe2\x80\xa6BnInitiate a new outgoing unidirectional stream.BcGet the priority of the send streamAlGet the priority of a streamCfThe negotiated application protocol, if ALPN is in use0Aeqlog output directoryBbOptional response to transmit backCiOptional seed to be used internally for random number \xe2\x80\xa60AlSlow start threshold (bytes)0BeSucceeds iff x < 2^6200000DaSends a Transmit on the given socket without any \xe2\x80\xa6DkQuickly determine whether cid could have been generated by \xe2\x80\xa600BmA path was abandoned and is no longer usable.CfFuture produced by Connection::accept_uniCdPaths marked with as available will be used when \xe2\x80\xa6CnAt least one new stream of a certain directionality may be \xe2\x80\xa6ClConfiguration for the Bbr congestion controller0BkThe connection was successfully establishedAoAPI to control datagram trafficAlDecoding of QUIC primitives.CkA path was discarded and all remaining state for it has \xe2\x80\xa6BgInfallible encoding of QUIC primitives.CnIdentifies a network path by the combination of remote and \xe2\x80\xa6A`Handshake packetBcKeys used to protect packet headers0CiCatch-all error for problems while decoding transport \xe2\x80\xa6CeNat traversal attempt failed due to a multipath errorBdKeys used to protect packet payloads0BmErrors that might trigger a path being closedAnApplication events about pathsBiStatistics related to a transmission path0AeA read error occurredBmErrors that arise from reading from a stream.C`Errors triggered when reading from a recv streamCiAn object for asynchronously writing to an associated \xe2\x80\xa6BmAccept the next incoming bidirectional streamBbDuplicate the controller\xe2\x80\x99s state0BdThe amount of UDP datagrams observedAaControl datagrams1BeCreate new object from the given bits00BoWhich side of a connection initiated the stream0CmConvert into a 0-RTT or 0.5-RTT connection at the cost of \xe2\x80\xa6BoShorthand for self == Side::Client0B`Whether the connection is closedBoShorthand for self == Side::Server0CbWhether it is legal to respond with a retry packet0CdWhether to allow clients to migrate to new addresses0CdConstruct a timer that will expire at 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\xa6A`Opens a new pathBmPing the remote endpoint over a specific pathCiAttempts to read from the stream into the provided bufferCeReceive UDP datagrams, or register to be woken if \xe2\x80\xa6CkSend a UDP datagram, or register to be woken if sending \xe2\x80\xa6ClPrivate key used to send authenticated connection resets \xe2\x80\xa60BmGenerate the integrity tag for a retry packet0BgA stable identifier for this connectionCePrivate key used to authenticate data included in \xe2\x80\xa60BlThe position of a token in the packet bufferCgTransport configuration to use for incoming connections0CjWait for all connections on the endpoint to be cleanly \xe2\x80\xa6BoWrite a buffer into this stream in its entiretyCiAbstract implementation of an async timer for runtime \xe2\x80\xa6BoNumber of UDP packets to send/receive at a timeCkError indicating that a path has not been opened or has \xe2\x80\xa6BnThe path is already closed or was never opened1BeIn-progress connection attempt futureAbA QUIC connection.CeProtocol state and logic for a single QUIC connectionCeCommon interface for different congestion controllers0CkNumber of frames transmitted or received of each frame type0BoThe connection ID was not recognized by the \xe2\x80\xa60DdThe type used to refer to FrameTypes in closing and \xe2\x80\xa6AnThe QUIC-MULTIPATH path statusBeConfiguration for qlog trace logging.0BnA stream that can only be used to receive dataAaAccess to streamsChErrors that arise while waiting for a stream to be resetDhError for attempting to retry an Incoming which already \xe2\x80\xa60BkA stream that can only be used to send data3BmObject to get current SystemTime0AdA rustls TLS session0CkResponsible for storing validation tokens received from \xe2\x80\xa60AeA borrowed UDP socketBjErrors that arise from writing to a streamBoErrors triggered while writing to a send streamBoAccept the next incoming uni-directional streamCkWhether the implementation is permitted to set the spin \xe2\x80\xa60CiCloses 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 toBeThe amount of bytes lost on this path0CjPad UDP datagrams carrying application data to current \xe2\x80\xa60AgReturns path statistics0CjThe final path stats, they are no longer available via \xe2\x80\xa6C`Attempt to write bytes from buf into the stream.AmRead the next segment of dataAoReads the next segment of data.CkRead an exact number of bytes contiguously from the stream.BnSets the PathStatus of this path.BoEpoch qlog event times are recorded relative to0ClError type for attempting to accept an IncomingBjErrors in the configuration of an endpoint0AeGeneric crypto errors0CnConfiguration for the Cubic congestion controller0CdReasons why attempting to finish a stream might failCnMaximum duration of inactivity to accept before timing out \xe2\x80\xa60AnValue exceeds supported bounds0D`Constructs a QlogConfig for individual connections.0AfA noq runtime for smolB`Application events about streamsCbAccept this incoming connection using a custom \xe2\x80\xa6CdLargest UDP payload size the path currently supportsClStorage size required for the largest packet that can be \xe2\x80\xa61BiDescription to record in the qlog capture0BjThe socket this datagram should be sent to00BkStarting guess for maximum UDP payload sizeCkThe initial value to be used as the maximum UDP payload \xe2\x80\xa60ChUDP payload size that the network must be capable of \xe2\x80\xa6BjThe RTT used before an RTT sample is taken0BfThe initial round-trip-time (in msecs)CeThe amount of concurrent streams which should be usedDdPackets are acked in batches, all with the same now \xe2\x80\xa60AdPacing rate (bits/s)0DiA broadcast receiver of PathEvents for all paths in this \xe2\x80\xa6DfGets the local PathStatus for a known PathIdAnRead the next segments of dataCkConvenience method to read all remaining data into a bufferAlProvide control over streamsBoThe sample size used for this key\xe2\x80\x99s algorithm01CiMaximum number of bytes to transmit to a peer without \xe2\x80\xa600BoThe server name specified by the client, if any0BmObject to get current SystemTime0BdSet a custom TokenStore0CaNumber of bytes to transmit from client to serverCnSpecifies the upper bound to the max UDP payload size that \xe2\x80\xa60ChReturns a weak reference to the inner connection struct.CdReturns a WeakPathHandle for this path.DgCreate a server config with the given crypto::ServerConfig0CeSets a prefix to the filename of the generated files.0DbWrite a single Bytes into this stream in its entiretyBbAn ADD_ADDRESS frame was received.BfConfiguration for outgoing connectionsCaClient-side configuration for the crypto protocol10CgThe stream has already been stopped, finished, or resetBmThe stream has already been finished or resetCmError indicating that a stream has not been opened or has \xe2\x80\xa6CjThe stream has not been opened or was already stopped, \xe2\x80\xa6CfThe stream has not been opened or has already been \xe2\x80\xa6CmThe stream has not been opened or was already finished or \xe2\x80\xa63CgErrors in the parameters being used to create a new \xe2\x80\xa60BkProtocol-level identifier for a connection.0CkWe couldn\xe2\x80\x99t decrypt a message. This is invariably fatal.0BjExplicit congestion notification codepoint00CnWe couldn\xe2\x80\x99t encrypt a message because it was larger than \xe2\x80\xa60BhParameters that are semantically invalidCaThis is the last path, which can not be abandonedAkn0\xe2\x80\x99s nat traversal eventsDhNull implementation of TokenLog, which never accepts tokens0ClReceived an observation of our external address from the \xe2\x80\xa6CiFuture produced by Connection::read_datagramBiThe remote changed the status of the pathBlRTT estimation for a particular network pathCnFuture produced by Connection::send_datagram_waitBiParameters governing incoming connectionsCaServer-side configuration for the crypto protocol10CmErrors that arise while monitoring for a send stream stop \xe2\x80\xa6AgA noq runtime for TokioC`Returns the lifetime of generated Connection IDs0BlIf the connection is closed, the reason why.CjConnect to a remote endpoint using a custom configuration.AlConservative estimate of RTTBnGet the 0-RTT keys if available (clients only)0AgWhich types we expectedB`Which handshake type we expected10AcGenerates a new CID0ClThe number of segments to read when GRO is enabled. Used \xe2\x80\xa6DeProcess ConnectionEvents generated by the associated \xe2\x80\xa6EaProcess EndpointEvents emitted from related ConnectionsCgCreate the initial set of keys given the client\xe2\x80\x99s \xe2\x80\xa6000BgThe amount of packets lost on this path0DcMaximum number of Incoming to allow to exist at a time0CjWhether datagrams might get fragmented into multiple parts0CmWhether transmitted datagrams might get fragmented by the \xe2\x80\xa6CkReturns the path\xe2\x80\x99s network path represented as a 4-tuple.CkThe original destination CID when initiating the connectionCiThe original destination connection ID sent by the clientDhReturns the next time at which handle_timeout should be \xe2\x80\xa6D`Configures qlog capturing by setting a QlogFactory.0CkThe segment size if this transmission contains multiple \xe2\x80\xa600ChThe number of streams that may have unacknowledged data.CbSet the lifetime of CIDs created by this generator0BcSet the priority of the send streamAlSet the priority of a streamCnInitialize a QUIC-compatible TLS client configuration with \xe2\x80\xa6000DgWrite a slice of Bytes into this stream, returning how \xe2\x80\xa6AmSend data on the given streamCkWe received a fatal alert. This means the peer is unhappy.0BhBloom filter-based TokenLog0CmThe connection could not be created because not enough of \xe2\x80\xa6000CaEvent resulting from processing a single datagramBlEvents sent from a Connection to an EndpointAoStatistics on Endpoint activityBnThe stream finished before all bytes were readBlAuthentication data for (rustls) TLS session0Ccreceived an invalid Retry Token in a client 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\xa610CnAttempts to read from the stream into the provided buffer, \xe2\x80\xa6AkReturns packets to transmitDjConfigures qlog capturing through the QLOGDIR environment \xe2\x80\xa60AoReceive an application datagramDfTransmit data as an unreliable, unordered application \xe2\x80\xa6CmWhether to implement fair queuing for send streams having \xe2\x80\xa60BfLength of sign\xe2\x80\x99s output0BnStart a client session with this configurationBnStart a server session with this configuration10BdA REMOVE_ADDRESS frame was received.CgAbstract implementation of a UDP socket for runtime \xe2\x80\xa6BgErrors triggered when abandoning a pathAgThe connection was lost00000ChGlobal configuration for the endpoint, affecting all \xe2\x80\xa60Cithe endpoint encountered an internal error and cannot \xe2\x80\xa60FbBasic adapter to let Incoming be await-ed like a ConnectingBhEncounter a frame ID that was not valid.CeThe peer sent us a TLS message with invalid contents.0Amno viable network path exists0DhNull implementation of TokenStore, which does not store \xe2\x80\xa60CiThe peer deviated from the standard TLS protocol. The \xe2\x80\xa60BmErrors that arise from reading from a stream.C`Errors from RecvStream::read_to_endCaIndicates whether a frame needs to be transmittedCmThe peer violated the QUIC specification as understood by \xe2\x80\xa60CiTransport-level errors occur when a peer violates the \xe2\x80\xa6C`Tokio-compatible UDP socket with some useful \xe2\x80\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 update0BkConfigures qlog capturing into a directory.0AlRead bytes of handshake data0BeWhether to use the unordered read APIClMaximum number of bytes the peer may transmit across all \xe2\x80\xa600CkCompletes when the stream has been reset by the peer or \xe2\x80\xa60CiCheck whether this stream has been reset by the peer, \xe2\x80\xa6AjThe peer\xe2\x80\x99s UDP addressesAhThe peer\xe2\x80\x99s UDP address0BgThe peer\xe2\x80\x99s UDP address for this path.1CeSaturating integer addition. Computes self + rhs, \xe2\x80\xa6000ChSaturating integer subtraction. Computes self - rhs, \xe2\x80\xa60CkMaximum reordering in time space before time based loss \xe2\x80\xa60ChReason given by the transport for closing the connection0BfReasons why a connection might be lost0CiThe datagram is redirected to its ConnectionBlEvents sent from an Endpoint to a ConnectionAeConnection statistics0BiStatistics for the currently open streamsAcPlain packet headerBkEnables writing qlog traces to a directory.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 packetCaThe endpoint can no longer create new connections0Cnreceived a STREAM frame or a RESET_STREAM frame containing \xe2\x80\xa60ClSpecific failure cases from keys_match or a \xe2\x80\xa60A`key update error0CaCurrent limits do not allow us to open more pathsClThe peer doesn\xe2\x80\x99t support a protocol version/feature we \xe2\x80\xa60BjA QUIC-compatible TLS client configuration0BjA QUIC-compatible TLS server configuration0EfTokenStore implementation that stores up to N tokens per \xe2\x80\xa60CgAn endpoint (local or remote) tried to add too many \xe2\x80\xa6CmWe didn\xe2\x80\x99t receive a path response in time after opening \xe2\x80\xa6CaPath could not be validated and will be abandonedBcA Version Negotiation packet headerCiRecord that the token was used and, ideally, return a \xe2\x80\xa60CjCurrent state of the congestion control algorithm, for \xe2\x80\xa6CcCurrent state of this connection\xe2\x80\x99s congestion \xe2\x80\xa6BaUpdate traffic keys 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 openCiOpens a new path if no path exists yet for the remote \xe2\x80\xa6CmOpens a new path only if no path on the same network path \xe2\x80\xa6CiMaximum reordering in packet number space before FACK \xe2\x80\xa60CfGet the size of the socket receive bufferAlReceive buffer size in bytesCcGet the size of the socket send bufferAiSend buffer size in bytesBiSet a custom TransportConfig000ChConfiguration for sending and handling validation tokens0CnCreate a server config with the given certificate chain to \xe2\x80\xa60DdWrite a slice of Bytes into this stream in its entiretyCfthe application or application protocol caused the \xe2\x80\xa60AnThe peer closed the connectionBoThe path was closed locally by the application.1B`Constructs controllers on demand0BdCommon congestion controller metrics0ClThe 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 frames0BcCongestion events on the connection0AiCongestion window (bytes)0ClCompute the maximum size of datagrams that may be passed \xe2\x80\xa6BoBytes available in the outgoing datagram bufferClReplace the server configuration, affecting new incoming \xe2\x80\xa60Cmthe endpoint has reached the confidentiality or integrity \xe2\x80\xa60CkParameters for controlling the peer\xe2\x80\x99s acknowledgement \xe2\x80\xa60DjThe max_fragment_size value supplied in configuration was \xe2\x80\xa60Bmthe server refused to accept a new connection0BjParse connection id in short header packetCjOne or more application datagrams have been sent after \xe2\x80\xa6Ckreceived more data than permitted in advertised data limits0AoThe TLS handshake was confirmedBjThe connection\xe2\x80\x99s handshake data is readyCeAttempted an ordered read following an unordered readAnWe saw an invalid certificate.0BcParameters governing MTU discovery.0BnNot enough addresses to complete the operationCkdetected an error with protocol compliance that was not \xe2\x80\xa60Cjreceived a frame for a stream identifier that exceeded \xe2\x80\xa60Cnreceived a frame for a stream that was not in a state that \xe2\x80\xa60AnError from setting path statusAjTransport-level error code0ChThe local endpoint does not support the QUIC version \xe2\x80\xa6CkError indicating that the specified QUIC version is not \xe2\x80\xa61C`Packet uses a QUIC version that is not supported1CiMaximum quantity of out-of-order crypto layer data to \xe2\x80\xa60C`Create a VarInt without ensuring it\xe2\x80\x99s in range0ChCumulative number of Quic handshakes ignored on this \xe2\x80\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\xa60BkNo remote CIDs available to open a new pathCkTransport parameters used to negotiate connection-level \xe2\x80\xa6CgA stream that can be used to receive data out-of-order.CmThe certificate verifier doesn\xe2\x80\x99t support the given type \xe2\x80\xa60CfThe operation is not allowed for this endpoint\xe2\x80\x99s \xe2\x80\xa6CiCumulative number of Quic handshakes accepted by this \xe2\x80\xa6CjSpecifies the amount of time that MTU discovery should \xe2\x80\xa60CiIf the 0-RTT-encrypted data has been accepted by the peer0BgWait for the handshake to be confirmed.CgInform the connection if a path may recover after a \xe2\x80\xa60CgPeriod of inactivity before sending a keep-alive packet0CiThe amount of PLPMTUD probe packets lost on this path \xe2\x80\xa60BlPackets were deemed lost or marked congested0CgCumulative number of Quic handshakes sent from this \xe2\x80\xa6CnThe number of remotely initiated open streams of a certain \xe2\x80\xa6AhBench fns for SendBufferCiThe amount of PLPMTUD probe packets sent on this path \xe2\x80\xa60Bireceived a frame that was badly formatted0CkThis function doesn\xe2\x80\x99t work until the TLS handshake is \xe2\x80\xa60CkWe received a TLS message that isn\xe2\x80\x99t valid right now. \xe2\x80\xa60CiThe remote SocketAddr supplied was malformedCkThe remote address for the path is not supported by the \xe2\x80\xa61ChThe initial cipher suite (AES-128-GCM-SHA256) is not \xe2\x80\xa60BdPaths can only be opened client-sideDmError returned when constructing a VarInt from a value >= \xe2\x80\xa60CeA handle to some connection internals, use with care.DkSpecifies the ACK frequency config (see AckFrequencyConfig \xe2\x80\xa60CiThe number of times a black hole was detected in the path0CgMaximum number of received bytes to buffer for each \xe2\x80\xa60AeIs multipath enabled?ClMaximum number of datagrams that might be described by a \xe2\x80\xa60CkMaximum UDP payload size accepted from peers (excluding \xe2\x80\xa60AmMax UDP payload size in bytesDkSpecifies the MTU discovery config (see MtuDiscoveryConfig \xe2\x80\xa60AmReturn endpoint-facing eventsCkThe preferred IPv4 address that will be communicated to \xe2\x80\xa60CkThe preferred IPv6 address that will be communicated to \xe2\x80\xa60ChThe reordering threshold we will request the peer to use0ClDuration after a retry token was issued for which it\xe2\x80\x99s \xe2\x80\xa60C`Sets the keep_alive_interval for a specific pathDfResize the receive buffer of socket to bytesDcResize the send buffer of socket to bytesBfThe peer\xe2\x80\x99s QUIC transport parametersBdQUIC connection transport parameters1CaGenerates connection IDs for incoming connections0CdAn incoming connection did not support any known \xe2\x80\xa60BjNo default client configuration was set up0BiPath abandoned due to unstable interfaces0ClConfiguration for sending and handling validation tokens \xe2\x80\xa60CmMaximum number of packets that may be sent using a single \xe2\x80\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\xa6CgReturns whether we think the other address probably \xe2\x80\xa6CkReduction in congestion window when a new loss event is \xe2\x80\xa60DdMaximum number of datagrams that a Transmit may encode.0DeA broadcast receiver of n0_nat_traversal::Events for \xe2\x80\xa6CjGet the address observed by the remote over the given pathCiMaximum number of bytes the peer may transmit without \xe2\x80\xa600Chreceived more data in CRYPTO frames than can be buffered0B`The extension was not negotiatedCbWe failed to figure out what time it currently is.0CbWe failed to acquire random bytes from the system.0CnError indicating that this operation requires multipath to \xe2\x80\xa6BnThe extension was not negotiated with the peerAkMultipath is not negotiatedCnError when the multipath extension was not negotiated, but \xe2\x80\xa6CjThe path was opened in a NAT traversal round which was \xe2\x80\xa6BnHeader of an Initial packet, before decryptionCgDerive keying material from this connection\xe2\x80\x99s TLS \xe2\x80\xa6EdFill output with output.len() bytes of keying material \xe2\x80\xa60BnPathological case: many segments, get from endCfGet segments in the old way, using a loop of get callsClCurrent number of remotely initiated streams that may be \xe2\x80\xa6ClTrack changed on our external address as reported by the \xe2\x80\xa6ClTrack changes on our external address as reported by the \xe2\x80\xa6CmCreate a client configuration that trusts specified trust \xe2\x80\xa60BkThe peer didn\xe2\x80\x99t give us any certificates.0BkThe peer sent an oversized record/fragment.0CkThe ack-eliciting threshold we will request the peer to use0BiWhether there are any pending retransmitsCdWhether the Multipath for QUIC extension is enabled.C`Sets the keep_alive_interval for a specific pathBoSet a custom ValidationTokenConfig0BmPath abandoned at the application\xe2\x80\x99s request0CjGet the current value of max_udp_payload_size0ChConfigure how to populate the destination CID of the \xe2\x80\xa60CjConstruct an endpoint with arbitrary configuration and \xe2\x80\xa6CfWhether the socket address that is initiating this \xe2\x80\xa60CmModify the number of open paths allowed when multipath is \xe2\x80\xa6Cmthe number of connection IDs provided by the peer exceeds \xe2\x80\xa60CbError returned by Session::export_keying_material.0CiA provided certificate revocation list (CRL) was invalid.0CmPath abandoned due to no available connection IDs for the \xe2\x80\xa60Clreceived transport parameters that were badly formatted, \xe2\x80\xa60CiRegisters one address at which this endpoint might be \xe2\x80\xa6ClAdd addresses the local endpoint considers are reachable \xe2\x80\xa6ClMaximum number of outgoing application datagram bytes to \xe2\x80\xa60CiSet the client configuration used by connectBmSets the max_idle_timeout for a specific pathBfThe QUIC protocol version implemented.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\xa6CnCreate a client configuration that trusts the platform\xe2\x80\x99s \xe2\x80\xa60CkGenerates 8-byte connection IDs that can be efficiently \xe2\x80\xa6CkAn error occurred while handling Encrypted Client Hello \xe2\x80\xa60CkPath abandoned due to resource limitations in the transport0CiGenerates purely random connection IDs of a specified \xe2\x80\xa6ChWhether to use \xe2\x80\x9cGeneric Segmentation Offload\xe2\x80\x9d to \xe2\x80\xa60CmMaximum number of incoming bidirectional streams that may \xe2\x80\xa60BhUpdates this tuple\xe2\x80\x99s local address iffClMaximum number of incoming application datagram bytes to \xe2\x80\xa60BcInitiates a new nat traversal round0BdPackets were incorrectly deemed lost0CnRemoves one or more addresses from the set of addresses at \xe2\x80\xa6CgRemoves an address the endpoing no longer considers \xe2\x80\xa6C`Sets the keep_alive_interval for a specific pathDeA ConnectionIdParser implementation that assumes the \xe2\x80\xa6CjWe received a TLS handshake message that isn\xe2\x80\x99t valid \xe2\x80\xa60CiHow to construct new congestion::Controllers0BlSets a default per-path maximum idle timeout0BoThe key exchange group negotiated with the peer0CbWhether to send observed address reports to peers.0CiModify the number of remotely initiated bidirectional \xe2\x80\xa6BiEnables the Multipath Extension for QUIC.0CjModify the number of remotely initiated unidirectional \xe2\x80\xa6CeNumber of consecutive PTOs after which network is \xe2\x80\xa60BkSets a default per-path keep alive interval0CjWhether to receive observed address reports from other \xe2\x80\xa60BmGet the current local nat traversal addresses0CkGet the currently advertised nat traversal addresses by \xe2\x80\xa60AcNat traversal draftCkSets the maximum number of nat traversal addresses this \xe2\x80\xa60CjWhen multipath is required and has not been explicitly \xe2\x80\xa6") \ No newline at end of file diff --git a/pr/509/docs/search.index/entry/3e7a9e6dac3d.js b/pr/509/docs/search.index/entry/3e7a9e6dac3d.js new file mode 100644 index 000000000..96d98821d --- /dev/null +++ b/pr/509/docs/search.index/entry/3e7a9e6dac3d.js @@ -0,0 +1 @@ +rd_("Af[445,15,446,0,122,0,0]Ak[3260,15,3261,3261,123,0,0]Ag[445,15,446,0,4220,0,0]Al[3260,15,3261,1969,4221,0,0]Al[2170,15,2171,2171,4222,0,0]Al[3260,15,1874,1874,1623,0,0]Ao[1388,13,1389,1389,4065,3201,0]Am[445,13,446,3786,4878,3201,0]Al[445,13,446,1067,485,3201,0]Am[445,13,446,1067,4490,3201,0]Am[445,13,446,4169,4483,3201,0]Am[445,13,446,4169,3209,3201,0]Am[445,13,446,4169,3398,3201,0]Am[445,13,446,4169,4482,3201,0]Am[445,13,446,4174,3416,3201,0]Am[445,13,446,4174,4255,3201,0]Aj[445,13,446,0,1633,3201,0]Aj[445,13,446,0,5041,3201,0]Aj[445,13,446,0,1346,3201,0]Aj[445,13,446,0,1616,3201,0]Aj[445,13,446,0,2476,3201,0]Aj[445,13,446,0,3206,3201,0]Aj[445,13,446,0,2052,3201,0]Aj[445,13,446,0,4206,3201,0]Aj[445,13,446,0,4575,3201,0]Aj[445,13,446,0,4078,3201,0]Aj[445,13,446,0,4068,3201,0]Aj[445,13,446,0,3189,3201,0]Aj[445,13,446,0,4572,3201,0]Aj[445,13,446,0,4771,3201,0]Aj[445,13,446,0,4212,3201,0]Aj[445,13,446,0,4215,3201,0]Aj[445,13,446,0,4220,3201,0]Aj[445,13,446,0,4936,3201,0]Ai[445,13,446,0,492,3201,0]Ai[445,13,446,0,122,3201,0]Aj[445,13,446,0,2463,3201,0]Ak[445,13,1878,0,5190,3201,0]Ak[445,13,1958,0,1363,3201,0]Ao[3260,13,3261,1995,1634,3201,0]Ao[3260,13,3261,1995,5042,3201,0]Ao[3260,13,3261,3225,1347,3201,0]Ao[3260,13,3261,3234,4879,3201,0]Ao[3260,13,3261,1557,1617,3201,0]Ao[3260,13,3261,1557,3392,3201,0]Ao[3260,13,3261,1557,3205,3201,0]Ao[3260,13,3261,1557,4877,3201,0]Ao[3260,13,3261,1557,4933,3201,0]Ao[3260,13,3261,1557,3367,3201,0]Ao[3260,13,3261,1572,2477,3201,0]Ao[3260,13,3261,1572,3207,3201,0]Ao[3260,13,3261,1093,3210,3201,0]Ao[3260,13,3261,1093,4374,3201,0]Ao[3260,13,3261,1098,2053,3201,0]Ao[3260,13,3261,1098,3417,3201,0]Ao[3260,13,3261,1098,4077,3201,0]Ao[3260,13,3261,2187,4095,3201,0]Ao[3260,13,3261,2187,4484,3201,0]Ao[3260,13,3261,2187,4210,3201,0]Ao[3260,13,3261,3787,4576,3201,0]Ao[3260,13,3261,3787,3202,3201,0]Ao[3260,13,3261,3787,4450,3201,0]Ao[3260,13,3261,3354,4079,3201,0]Ao[3260,13,3261,1876,4069,3201,0]Ao[3260,13,3261,1547,3190,3201,0]Ao[3260,13,3261,1547,4465,3201,0]Ao[3260,13,3261,1547,3384,3201,0]Ao[3260,13,3261,1547,3406,3201,0]Ao[3260,13,3261,1547,4217,3201,0]Ao[3260,13,3261,1547,4574,3201,0]Ao[3260,13,3261,1547,4773,3201,0]Ao[3260,13,3261,2487,4777,3201,0]Ao[3260,13,3261,2487,4213,3201,0]Ao[3260,13,3261,1941,2447,3201,0]Ao[3260,13,3261,1941,4875,3201,0]Ao[3260,13,3261,1969,4216,3201,0]Ao[3260,13,3261,1969,4221,3201,0]Al[3260,13,3261,0,4488,3201,0]Al[3260,13,3261,0,4937,3201,0]An[3260,13,3261,3261,493,3201,0]An[3260,13,3261,3261,123,3201,0]Ao[3260,13,3261,3261,2464,3201,0]Ao[3260,13,3261,3261,3188,3201,0]Ao[3260,13,1874,1874,4380,3201,0]Ao[3260,13,5083,5083,4982,3201,0]Ao[3260,13,5083,5083,1365,3201,0]Ao[3260,13,1879,1879,5191,3201,0]Al[3260,13,1959,0,1364,3201,0]Ao[2170,13,2171,2171,4222,3201,0]Ai[445,13,446,1067,485,0,0]Aj[445,13,446,1067,4490,0,0]Aj[445,13,446,4169,3396,0,0]Aj[445,13,446,4169,4983,0,0]Aj[445,13,446,4174,3402,0,0]Ai[3260,14,3203,0,1610,0,0]Ai[3260,14,3203,0,3167,0,0]Ai[3260,14,3203,0,3184,0,0]Ai[3260,14,3203,0,4242,0,0]Ai[3260,14,3203,0,4235,0,0]Ai[3260,14,4094,0,2457,0,0]Ai[3260,14,4094,0,2479,0,0]Ai[3260,14,4094,0,2441,0,0]Ai[3260,14,4094,0,2040,0,0]Ae[1388,7,1389,0,0,0,0]Ah[3260,14,4587,0,481,0,0]Ag[445,15,446,0,3189,0,0]Al[3260,15,3261,1547,3190,0,0]Af[445,5,3781,138,0,0,0]Ag[3260,5,3782,138,0,0,0]Al[1068,15,1069,1069,4976,0,0]Ae[2535,5,2536,0,0,0,0]Af[445,6,446,3261,0,0,0]Ae[3260,6,3261,0,0,0,0]Al[3260,15,1874,1874,1623,0,0]Ag[445,19,446,0,1633,0,0]Ag[445,19,446,0,1616,0,0]Al[3260,19,3261,1995,1634,0,0]Al[3260,19,3261,1557,1617,0,0]Ae[1388,5,1389,0,0,0,0]Ae[1068,5,1868,0,0,0,0]Ae[1068,5,1964,0,0,0,0]Af[445,15,446,0,122,0,0]Ak[3260,15,3261,3261,123,0,0]Ai[445,13,446,0,1633,133,0]Ai[445,13,446,0,1616,133,0]Ai[445,13,446,0,4078,133,0]Ai[445,13,446,0,4215,133,0]Ah[445,13,446,0,492,133,0]Ah[445,13,446,0,122,133,0]Ai[445,13,446,0,2463,133,0]An[3260,13,3261,1995,1634,133,0]An[3260,13,3261,3234,4879,133,0]An[3260,13,3261,1557,1617,133,0]An[3260,13,3261,1093,3210,133,0]An[3260,13,3261,1093,4374,133,0]An[3260,13,3261,1098,3417,133,0]An[3260,13,3261,3354,4079,133,0]An[3260,13,3261,2487,4777,133,0]An[3260,13,3261,1941,4875,133,0]An[3260,13,3261,1969,4216,133,0]Am[3260,13,3261,3261,493,133,0]Am[3260,13,3261,3261,123,133,0]An[3260,13,3261,3261,2464,133,0]Ag[445,13,446,0,2463,0,0]Al[3260,13,3261,3261,2464,0,0]Ai[3260,14,4094,0,1611,0,0]Ai[3260,14,4094,0,3173,0,0]Ag[445,14,446,0,2473,0,0]Al[3260,14,3261,3261,2474,0,0]Al[2170,14,2171,2171,2461,0,0]Al[2170,14,2171,2171,2475,0,0]An[1388,13,1389,1389,130,1353,0]Ao[1388,13,1389,1389,4065,1353,0]Ao[1388,13,1571,1571,4485,1353,0]Ao[4102,13,4103,4103,5132,1353,0]Am[445,13,446,3786,3369,1353,0]Am[445,13,446,3786,3371,1353,0]Am[445,13,446,3786,5044,1353,0]Db[445,13,446,3786,4878,2012,0,\"impl-Display-for-SendDatagramError\"]D`[445,13,446,3786,4878,1353,0,\"impl-Debug-for-SendDatagramError\"]Am[445,13,446,2486,2439,1353,0]Am[445,13,446,2486,4348,1353,0]Am[445,13,446,2513,2443,1353,0]Ci[445,13,446,2513,3399,1353,0,\"impl-Debug-for-RetryError\"]Ck[445,13,446,2513,3399,2012,0,\"impl-Display-for-RetryError\"]Am[445,13,446,2513,4464,1353,0]Al[445,13,446,1067,485,1353,0]Am[445,13,446,1067,4490,1353,0]Am[445,13,446,4169,3396,1353,0]Am[445,13,446,4169,4983,1353,0]Co[445,13,446,4169,4483,2012,0,\"impl-Display-for-ReadToEndError\"]Cm[445,13,446,4169,4483,1353,0,\"impl-Debug-for-ReadToEndError\"]Ch[445,13,446,4169,3209,1353,0,\"impl-Debug-for-ReadError\"]Cj[445,13,446,4169,3209,2012,0,\"impl-Display-for-ReadError\"]Ck[445,13,446,4169,3398,2012,0,\"impl-Display-for-ResetError\"]Ci[445,13,446,4169,3398,1353,0,\"impl-Debug-for-ResetError\"]Cm[445,13,446,4169,4482,1353,0,\"impl-Debug-for-ReadExactError\"]Co[445,13,446,4169,4482,2012,0,\"impl-Display-for-ReadExactError\"]Am[445,13,446,1584,4256,1353,0]Am[445,13,446,1110,4091,1353,0]Am[445,13,446,4174,3402,1353,0]Ck[445,13,446,4174,3416,2012,0,\"impl-Display-for-WriteError\"]Ci[445,13,446,4174,3416,1353,0,\"impl-Debug-for-WriteError\"]Cm[445,13,446,4174,4255,2012,0,\"impl-Display-for-StoppedError\"]Ck[445,13,446,4174,4255,1353,0,\"impl-Debug-for-StoppedError\"]Am[445,13,446,4174,2042,1353,0]Cb[445,13,446,0,1633,1353,0,\"impl-Debug-for-VarInt\"]Cd[445,13,446,0,1633,2012,0,\"impl-Display-for-VarInt\"]D`[445,13,446,0,5041,1353,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Db[445,13,446,0,5041,2012,0,\"impl-Display-for-VarIntBoundsExceeded\"]Aj[445,13,446,0,1346,1353,0]Cd[445,13,446,0,1616,2012,0,\"impl-Display-for-PathId\"]Cb[445,13,446,0,1616,1353,0,\"impl-Debug-for-PathId\"]Aj[445,13,446,0,2476,1353,0]Aj[445,13,446,0,3377,1353,0]Aj[445,13,446,0,3206,1353,0]Aj[445,13,446,0,4579,1353,0]Aj[445,13,446,0,2052,1353,0]Ch[445,13,446,0,4206,1353,0,\"impl-Debug-for-ClosedStream\"]Cj[445,13,446,0,4206,2012,0,\"impl-Display-for-ClosedStream\"]Ck[445,13,446,0,4575,1353,0,\"impl-Debug-for-ConnectionError\"]Cm[445,13,446,0,4575,2012,0,\"impl-Display-for-ConnectionError\"]Aj[445,13,446,0,4589,1353,0]Aj[445,13,446,0,4598,1353,0]Aj[445,13,446,0,4902,1353,0]Aj[445,13,446,0,4917,1353,0]Aj[445,13,446,0,4078,1353,0]Aj[445,13,446,0,4459,1353,0]Aj[445,13,446,0,4249,1353,0]Aj[445,13,446,0,5098,1353,0]Aj[445,13,446,0,4200,1353,0]Ci[445,13,446,0,4068,2012,0,\"impl-Display-for-ConfigError\"]Cg[445,13,446,0,4068,1353,0,\"impl-Debug-for-ConfigError\"]Ce[445,13,446,0,3189,1353,0,\"impl-Debug-for-FrameType\"]Cg[445,13,446,0,3189,2012,0,\"impl-Display-for-FrameType\"]Cm[445,13,446,0,4572,2012,0,\"impl-Display-for-ConnectionClose\"]Ck[445,13,446,0,4572,1353,0,\"impl-Debug-for-ConnectionClose\"]Cl[445,13,446,0,4771,1353,0,\"impl-Debug-for-ApplicationClose\"]Cn[445,13,446,0,4771,2012,0,\"impl-Display-for-ApplicationClose\"]Ch[445,13,446,0,4212,1353,0,\"impl-Debug-for-ConnectError\"]Cj[445,13,446,0,4212,2012,0,\"impl-Display-for-ConnectError\"]Ch[445,13,446,0,4215,1353,0,\"impl-Debug-for-ConnectionId\"]Cj[445,13,446,0,4215,2012,0,\"impl-Display-for-ConnectionId\"]Aj[445,13,446,0,4220,1353,0]Cn[445,13,446,0,4936,1353,0,\"impl-Debug-for-TransportErrorCode\"]D`[445,13,446,0,4936,2012,0,\"impl-Display-for-TransportErrorCode\"]Aj[445,13,446,0,3380,1353,0]Aj[445,13,446,0,4804,1353,0]Ai[445,13,446,0,492,1353,0]C`[445,13,446,0,122,2012,0,\"impl-Display-for-Dir\"]Bn[445,13,446,0,122,1353,0,\"impl-Debug-for-Dir\"]Cf[445,13,446,0,2463,2012,0,\"impl-Display-for-StreamId\"]Cd[445,13,446,0,2463,1353,0,\"impl-Debug-for-StreamId\"]Aj[445,13,446,0,2473,1353,0]Aj[445,13,3781,0,116,1353,0]Ak[445,13,3781,0,3176,1353,0]Ak[445,13,3781,0,1350,1353,0]Ak[445,13,3781,0,4073,1353,0]Ak[445,13,3781,0,2025,1353,0]Ak[445,13,3781,0,4366,1353,0]Ak[445,13,3781,0,4866,1353,0]Ak[445,13,1878,0,5190,1353,0]Ak[445,13,1878,0,4071,1353,0]Ak[445,13,1878,0,4940,1353,0]Dc[445,13,1958,0,5037,2012,0,\"impl-Display-for-NoInitialCipherSuite\"]Da[445,13,1958,0,5037,1353,0,\"impl-Debug-for-NoInitialCipherSuite\"]Cd[445,13,1958,0,1363,2012,0,\"impl-Display-for-Error\"]Cb[445,13,1958,0,1363,1353,0,\"impl-Debug-for-Error\"]Ao[3260,13,3261,3787,3372,1353,0]Ao[3260,13,3261,2487,2440,1353,0]Cg[3260,13,3261,1995,1634,1353,0,\"impl-Debug-for-VarInt\"]Ci[3260,13,3261,1995,1634,2012,0,\"impl-Display-for-VarInt\"]De[3260,13,3261,1995,5042,1353,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Dg[3260,13,3261,1995,5042,2012,0,\"impl-Display-for-VarIntBoundsExceeded\"]Ao[3260,13,3261,3225,1347,1353,0]Dd[3260,13,3261,3234,4879,2012,0,\"impl-Display-for-SendDatagramError\"]Db[3260,13,3261,3234,4879,1353,0,\"impl-Debug-for-SendDatagramError\"]Cg[3260,13,3261,1557,1617,1353,0,\"impl-Debug-for-PathId\"]Ci[3260,13,3261,1557,1617,2012,0,\"impl-Display-for-PathId\"]Ao[3260,13,3261,1557,4245,1353,0]Ao[3260,13,3261,1557,3392,1353,0]Ao[3260,13,3261,1557,3205,1353,0]Ao[3260,13,3261,1557,4877,1353,0]De[3260,13,3261,1557,4933,2012,0,\"impl-Display-for-SetPathStatusError\"]Dc[3260,13,3261,1557,4933,1353,0,\"impl-Debug-for-SetPathStatusError\"]Ck[3260,13,3261,1557,3367,1353,0,\"impl-Debug-for-ClosedPath\"]Cm[3260,13,3261,1557,3367,2012,0,\"impl-Display-for-ClosedPath\"]Ao[3260,13,3261,1572,2477,1353,0]Ao[3260,13,3261,1572,3378,1353,0]Ao[3260,13,3261,1572,3207,1353,0]Ao[3260,13,3261,1572,4580,1353,0]Cl[3260,13,3261,1093,3210,2012,0,\"impl-Display-for-ReadError\"]Cj[3260,13,3261,1093,3210,1353,0,\"impl-Debug-for-ReadError\"]D`[3260,13,3261,1093,4374,2012,0,\"impl-Display-for-ReadableError\"]Cn[3260,13,3261,1093,4374,1353,0,\"impl-Debug-for-ReadableError\"]Ao[3260,13,3261,1098,2053,1353,0]Cm[3260,13,3261,1098,3417,2012,0,\"impl-Display-for-WriteError\"]Ck[3260,13,3261,1098,3417,1353,0,\"impl-Debug-for-WriteError\"]Cl[3260,13,3261,1098,4077,1353,0,\"impl-Debug-for-FinishError\"]Cn[3260,13,3261,1098,4077,2012,0,\"impl-Display-for-FinishError\"]Ao[3260,13,3261,2187,4095,1353,0]Ao[3260,13,3261,2187,4484,1353,0]Cm[3260,13,3261,2187,4210,1353,0,\"impl-Debug-for-ClosedStream\"]Co[3260,13,3261,2187,4210,2012,0,\"impl-Display-for-ClosedStream\"]D`[3260,13,3261,3787,4576,1353,0,\"impl-Debug-for-ConnectionError\"]Db[3260,13,3261,3787,4576,2012,0,\"impl-Display-for-ConnectionError\"]Cj[3260,13,3261,3787,3202,1353,0,\"impl-Debug-for-PathError\"]Cl[3260,13,3261,3787,3202,2012,0,\"impl-Display-for-PathError\"]Da[3260,13,3261,3787,4450,2012,0,\"impl-Display-for-ClosePathError\"]Co[3260,13,3261,3787,4450,1353,0,\"impl-Debug-for-ClosePathError\"]Di[3260,13,3261,3787,5127,2012,0,\"impl-Display-for-MultipathNotNegotiated\"]Dg[3260,13,3261,3787,5127,1353,0,\"impl-Debug-for-MultipathNotNegotiated\"]Ao[3260,13,3261,3787,1368,1353,0]Ao[3260,13,3261,1089,4590,1353,0]Ao[3260,13,3261,3354,4599,1353,0]Ao[3260,13,3261,3354,4903,1353,0]Ao[3260,13,3261,3354,4918,1353,0]Ao[3260,13,3261,3354,4079,1353,0]Ao[3260,13,3261,1876,4460,1353,0]Ao[3260,13,3261,1876,4251,1353,0]Ao[3260,13,3261,1876,5099,1353,0]Ao[3260,13,3261,1876,4202,1353,0]Cn[3260,13,3261,1876,4069,2012,0,\"impl-Display-for-ConfigError\"]Cl[3260,13,3261,1876,4069,1353,0,\"impl-Debug-for-ConfigError\"]Cl[3260,13,3261,1547,3190,2012,0,\"impl-Display-for-FrameType\"]Cj[3260,13,3261,1547,3190,1353,0,\"impl-Debug-for-FrameType\"]Co[3260,13,3261,1547,4465,1353,0,\"impl-Debug-for-InvalidFrameId\"]Da[3260,13,3261,1547,4465,2012,0,\"impl-Display-for-InvalidFrameId\"]Ck[3260,13,3261,1547,3384,1353,0,\"impl-Debug-for-MaybeFrame\"]Cm[3260,13,3261,1547,3384,2012,0,\"impl-Display-for-MaybeFrame\"]Ck[3260,13,3261,1547,3406,1353,0,\"impl-Debug-for-StreamInfo\"]Cm[3260,13,3261,1547,3406,2012,0,\"impl-Display-for-StreamInfo\"]Co[3260,13,3261,1547,4217,2012,0,\"impl-Display-for-DatagramInfo\"]Cm[3260,13,3261,1547,4217,1353,0,\"impl-Debug-for-DatagramInfo\"]D`[3260,13,3261,1547,4574,1353,0,\"impl-Debug-for-ConnectionClose\"]Db[3260,13,3261,1547,4574,2012,0,\"impl-Display-for-ConnectionClose\"]Dc[3260,13,3261,1547,4773,2012,0,\"impl-Display-for-ApplicationClose\"]Da[3260,13,3261,1547,4773,1353,0,\"impl-Debug-for-ApplicationClose\"]Ck[3260,13,3261,1547,2433,2012,0,\"impl-Display-for-Datagram\"]Ci[3260,13,3261,1547,2433,1353,0,\"impl-Debug-for-Datagram\"]Ao[3260,13,3261,2487,4777,1353,0]Ao[3260,13,3261,2487,2444,1353,0]Co[3260,13,3261,2487,4213,2012,0,\"impl-Display-for-ConnectError\"]Cm[3260,13,3261,2487,4213,1353,0,\"impl-Debug-for-ConnectError\"]Ao[3260,13,3261,2487,4063,1353,0]Cm[3260,13,3261,2487,3400,2012,0,\"impl-Display-for-RetryError\"]Ck[3260,13,3261,2487,3400,1353,0,\"impl-Debug-for-RetryError\"]Ao[3260,13,3261,1941,4369,1353,0]Ao[3260,13,3261,1941,4588,1353,0]Ao[3260,13,3261,1941,5131,1353,0]Ao[3260,13,3261,1941,2447,1353,0]Dd[3260,13,3261,1941,4875,2012,0,\"impl-Display-for-PacketDecodeError\"]Db[3260,13,3261,1941,4875,1353,0,\"impl-Debug-for-PacketDecodeError\"]Ao[3260,13,3261,1969,4578,1353,0]Ao[3260,13,3261,1969,4347,1353,0]Co[3260,13,3261,1969,4216,2012,0,\"impl-Display-for-ConnectionId\"]Cm[3260,13,3261,1969,4216,1353,0,\"impl-Debug-for-ConnectionId\"]Ao[3260,13,3261,1969,4221,1353,0]Ce[3260,13,3261,0,4488,2012,0,\"impl-Display-for-Error\"]Cc[3260,13,3261,0,4488,1353,0,\"impl-Debug-for-Error\"]Cb[3260,13,3261,0,4937,1353,0,\"impl-Debug-for-Code\"]Cd[3260,13,3261,0,4937,2012,0,\"impl-Display-for-Code\"]Ao[3260,13,3261,4387,3381,1353,0]Ao[3260,13,3261,4387,5225,1353,0]Ao[3260,13,3261,4972,4805,1353,0]An[3260,13,3261,3261,493,1353,0]Ce[3260,13,3261,3261,123,2012,0,\"impl-Display-for-Dir\"]Cc[3260,13,3261,3261,123,1353,0,\"impl-Debug-for-Dir\"]Ci[3260,13,3261,3261,2464,1353,0,\"impl-Debug-for-StreamId\"]Ck[3260,13,3261,3261,2464,2012,0,\"impl-Display-for-StreamId\"]Ao[3260,13,3261,3261,2474,1353,0]Cl[3260,13,3261,3261,3188,2012,0,\"impl-Display-for-FourTuple\"]Cj[3260,13,3261,3261,3188,1353,0,\"impl-Debug-for-FourTuple\"]D`[3260,13,1874,1874,4380,2012,0,\"impl-Display-for-UnexpectedEnd\"]Cn[3260,13,1874,1874,4380,1353,0,\"impl-Debug-for-UnexpectedEnd\"]Ao[3260,13,5083,5083,4982,1353,0]Ch[3260,13,5083,5083,1365,2012,0,\"impl-Display-for-Error\"]Cf[3260,13,5083,5083,1365,1353,0,\"impl-Debug-for-Error\"]Ao[3260,13,1879,1879,5191,1353,0]Ao[3260,13,1879,1879,4072,1353,0]Ao[3260,13,1879,1879,4943,1353,0]De[3260,13,1959,1959,5038,1353,0,\"impl-Debug-for-NoInitialCipherSuite\"]Dg[3260,13,1959,1959,5038,2012,0,\"impl-Display-for-NoInitialCipherSuite\"]Ce[3260,13,1959,0,1364,2012,0,\"impl-Display-for-Error\"]Cc[3260,13,1959,0,1364,1353,0,\"impl-Debug-for-Error\"]Am[3260,13,3782,138,117,1353,0]An[3260,13,3782,138,3177,1353,0]Ao[3260,13,3782,1543,1351,1353,0]Ao[3260,13,3782,1543,4074,1353,0]Ao[3260,13,3782,2535,2026,1353,0]Ao[3260,13,3782,2535,4367,1353,0]Ao[3260,13,3782,3782,4867,1353,0]Cf[3260,13,4835,4835,1366,1353,0,\"impl-Debug-for-Error\"]Ch[3260,13,4835,4835,1366,2012,0,\"impl-Display-for-Error\"]Ao[3260,13,4835,4835,1369,1353,0]An[2170,13,2171,399,4489,1353,0]Ao[2170,13,2171,2171,2461,1353,0]Ao[2170,13,2171,2171,2475,1353,0]Ao[2170,13,2171,2171,4222,1353,0]Al[3260,13,3261,1557,4245,0,0]Ag[445,14,446,0,2476,0,0]Al[3260,14,3261,1572,2477,0,0]Al[3260,13,3261,1941,4369,0,0]Al[3260,14,3261,1941,5131,0,0]Ah[3260,14,4587,0,481,0,0]Al[2170,14,2171,2171,2461,0,0]Ag[445,13,446,0,5098,0,0]Al[3260,13,3261,1876,5099,0,0]8Al[1388,13,1571,1571,4485,0,0]Al[4102,13,4103,4103,5132,0,0]Ag[445,13,446,0,4339,0,0]Aj[445,13,446,2486,2439,0,0]Ag[445,13,446,0,3393,0,0]Ag[445,13,446,0,4589,0,0]Ag[445,13,446,0,4459,0,0]Ag[445,13,446,0,4249,0,0]Ag[445,13,446,0,4200,0,0]Ag[445,13,446,0,4215,0,0]Ag[445,13,446,0,4804,0,0]Ag[445,13,446,0,2463,0,0]Ag[445,13,3781,0,116,0,0]Ah[445,13,3781,0,1350,0,0]Ah[445,13,3781,0,2025,0,0]Al[3260,13,3261,4608,4340,0,0]Al[3260,13,3261,1089,3394,0,0]Al[3260,13,3261,2487,2440,0,0]Al[3260,13,3261,1941,5250,0,0]Al[3260,13,3261,4387,5220,0,0]Al[3260,13,3261,1089,4590,0,0]Al[3260,13,3261,1876,4460,0,0]Al[3260,13,3261,1876,4251,0,0]Al[3260,13,3261,1876,4202,0,0]Al[3260,13,3261,1941,4369,0,0]Al[3260,13,3261,1969,4216,0,0]Ai[3260,13,3261,0,4488,0,0]Al[3260,13,3261,4387,5225,0,0]Al[3260,13,3261,4972,4805,0,0]Al[3260,13,3261,3261,2464,0,0]Aj[3260,13,3782,138,117,0,0]Al[3260,13,3782,1543,1351,0,0]Al[3260,13,3782,2535,2026,0,0]Ak[2170,13,2171,399,4489,0,0]Al[1068,13,4308,4308,5176,0,0]Al[1068,13,4308,4308,5177,0,0]Aa[445,3,0,0,0,0,0]Ah[445,13,446,0,492,129,0]Am[3260,13,3261,3261,493,129,0]Ag[445,12,446,0,3409,0,0]Aj[445,13,446,2179,2037,0,0]0Am[445,13,446,1584,4256,2037,0]Aj[445,13,446,0,4378,3409,0]Al[3260,12,3261,1876,3410,0,0]Ao[3260,13,3261,1876,4379,3410,0]Aj[445,13,446,3786,3371,0,0]Ag[445,14,446,0,3206,0,0]Al[3260,13,3261,3787,3372,0,0]Al[3260,14,3261,1572,3207,0,0]Ae[1068,7,1868,0,0,0,0]Ae[1068,7,1964,0,0,0,0]Ac[445,4,446,0,0,0,0]Ag[445,15,446,0,4220,0,0]Al[3260,15,3261,1969,4221,0,0]Al[2170,15,2171,2171,4222,0,0]210Ag[445,5,1878,1879,0,0,0]Ae[3260,5,1879,0,0,0,0]Al[3260,15,3261,1941,4588,0,0]Al[3260,15,3261,1547,3384,0,0]Af[445,5,446,1067,0,0,0]Al[3260,15,3261,3787,1368,0,0]Ag[445,15,446,0,3189,0,0]Al[3260,15,3261,1547,3190,0,0]Aj[445,15,446,4169,4483,0,0]Af[445,6,446,3261,0,0,0]Ae[3260,6,3261,0,0,0,0]Ag[445,19,446,0,1616,0,0]Al[3260,19,3261,1557,1617,0,0]Ag[445,14,446,0,3377,0,0]Al[3260,14,3261,1572,3378,0,0]Ai[3260,14,3203,0,4235,0,0]Al[2170,14,2171,2171,2461,0,0]Aa[502,3,0,0,0,0,0]Ai[3260,14,3261,0,4488,0,0]Ag[445,14,446,0,3206,0,0]Al[3260,14,3261,1572,3207,0,0]Al[3260,14,3261,1547,2433,0,0]Al[445,13,446,3786,2454,469,0]Al[445,13,446,2513,2443,469,0]Ak[445,13,446,1067,485,469,0]Al[445,13,446,1067,4490,469,0]Al[445,13,446,4169,3396,469,0]Al[445,13,446,4174,3402,469,0]An[3260,13,3261,1093,1598,469,0]Am[1388,13,1389,1389,130,476,0]An[1388,13,1389,1389,4065,476,0]An[1388,13,1571,1571,4096,476,0]An[1388,13,1571,1571,1383,476,0]An[1388,13,1571,1571,4485,476,0]Ak[502,13,503,503,4066,476,0]An[4102,13,4103,4103,5132,476,0]Al[445,13,446,3786,4603,476,0]Al[445,13,446,3786,2454,476,0]Ai[445,13,446,0,4339,476,0]Al[445,13,446,1067,2455,476,0]Al[445,13,446,1067,4769,476,0]Al[445,13,446,3786,3369,476,0]Al[445,13,446,3786,3371,476,0]Al[445,13,446,3786,2028,476,0]Al[445,13,446,3786,1609,476,0]Al[445,13,446,3786,3169,476,0]Al[445,13,446,3786,2427,476,0]Al[445,13,446,3786,4241,476,0]Al[445,13,446,3786,4246,476,0]Al[445,13,446,3786,5044,476,0]Al[445,13,446,3786,4878,476,0]0Al[445,13,446,2486,2439,476,0]Al[445,13,446,2486,4348,476,0]Al[445,13,446,2486,1590,476,0]Al[445,13,446,2513,2443,476,0]Al[445,13,446,2513,3399,476,0]Al[445,13,446,2513,4464,476,0]Ak[445,13,446,1067,485,476,0]Al[445,13,446,1067,4490,476,0]Al[445,13,446,4169,3396,476,0]Al[445,13,446,4169,4983,476,0]Al[445,13,446,4169,4483,476,0]0Al[445,13,446,4169,3209,476,0]Dk[445,13,446,4169,3209,476,0,\"impl-From%3CConnectionError%3E-for-ReadError\"]Df[445,13,446,4169,3209,476,0,\"impl-From%3CResetError%3E-for-ReadError\"]Al[445,13,446,4169,3398,476,0]0Al[445,13,446,4169,4482,476,0]0Al[445,13,446,1584,4256,476,0]Al[445,13,446,1110,4091,476,0]Al[445,13,446,4174,3402,476,0]Al[445,13,446,4174,3416,476,0]Dl[445,13,446,4174,3416,476,0,\"impl-From%3CConnectionError%3E-for-WriteError\"]Di[445,13,446,4174,3416,476,0,\"impl-From%3CClosedStream%3E-for-WriteError\"]Di[445,13,446,4174,3416,476,0,\"impl-From%3CStoppedError%3E-for-WriteError\"]Al[445,13,446,4174,4255,476,0]0Al[445,13,446,4174,2042,476,0]Ai[445,13,446,0,3393,476,0]Ai[445,13,446,0,4378,476,0]Ai[445,13,446,0,4595,476,0]Ai[445,13,446,0,4233,476,0]Ai[445,13,446,0,4473,476,0]Ci[445,13,446,0,1633,476,0,\"impl-From%3Cu16%3E-for-VarInt\"]Dh[445,13,446,0,1633,476,0,\"impl-From%3CTransportErrorCode%3E-for-VarInt\"]Cn[445,13,446,0,1633,476,0,\"impl-From%3CStreamId%3E-for-VarInt\"]Ci[445,13,446,0,1633,476,0,\"impl-From%3Cu32%3E-for-VarInt\"]Ch[445,13,446,0,1633,476,0,\"impl-From%3Cu8%3E-for-VarInt\"]Ai[445,13,446,0,1633,476,0]Ai[445,13,446,0,5041,476,0]Ai[445,13,446,0,1346,476,0]Ai[445,13,446,0,1616,476,0]0Ai[445,13,446,0,2476,476,0]Ai[445,13,446,0,3377,476,0]Ai[445,13,446,0,3206,476,0]Ai[445,13,446,0,4579,476,0]Ai[445,13,446,0,2052,476,0]Ai[445,13,446,0,4206,476,0]Ai[445,13,446,0,4575,476,0]Dj[445,13,446,0,4575,476,0,\"impl-From%3CCloseReason%3E-for-ConnectionError\"]Dd[445,13,446,0,4575,476,0,\"impl-From%3CClose%3E-for-ConnectionError\"]Dm[445,13,446,0,4575,476,0,\"impl-From%3CTransportError%3E-for-ConnectionError\"]Ai[445,13,446,0,4589,476,0]Ai[445,13,446,0,4598,476,0]Ai[445,13,446,0,4902,476,0]Ai[445,13,446,0,4917,476,0]Ai[445,13,446,0,4078,476,0]0Ai[445,13,446,0,4459,476,0]Ai[445,13,446,0,4249,476,0]Ai[445,13,446,0,5098,476,0]Ai[445,13,446,0,4200,476,0]Dj[445,13,446,0,4068,476,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]Do[445,13,446,0,4068,476,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]Ai[445,13,446,0,4068,476,0]Ai[445,13,446,0,3189,476,0]Ai[445,13,446,0,4572,476,0]0Ai[445,13,446,0,4771,476,0]Ai[445,13,446,0,4212,476,0]0Ai[445,13,446,0,4215,476,0]Ai[445,13,446,0,4220,476,0]Ai[445,13,446,0,4936,476,0]0Ai[445,13,446,0,3380,476,0]Ai[445,13,446,0,4804,476,0]Ah[445,13,446,0,492,476,0]Ah[445,13,446,0,122,476,0]Ai[445,13,446,0,2463,476,0]0Ai[445,13,446,0,2473,476,0]Ai[445,13,3781,0,116,476,0]Aj[445,13,3781,0,3176,476,0]Aj[445,13,3781,0,1350,476,0]Aj[445,13,3781,0,4073,476,0]Aj[445,13,3781,0,2025,476,0]Aj[445,13,3781,0,4366,476,0]Aj[445,13,3781,0,4866,476,0]Aj[445,13,1878,0,2020,476,0]Ai[445,13,1878,0,479,476,0]Aj[445,13,1878,0,5190,476,0]Aj[445,13,1878,0,4071,476,0]0Aj[445,13,1878,0,4940,476,0]Aj[445,13,1958,0,3411,476,0]Aj[445,13,1958,0,4350,476,0]Aj[445,13,1958,0,4800,476,0]Aj[445,13,1958,0,5037,476,0]Aj[445,13,1958,0,4802,476,0]Df[445,13,1958,0,1363,476,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]Df[445,13,1958,0,1363,476,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]D`[445,13,1958,0,1363,476,0,\"impl-From%3COtherError%3E-for-Error\"]Do[445,13,1958,0,1363,476,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]De[445,13,1958,0,1363,476,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Dd[445,13,1958,0,1363,476,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]Dd[445,13,1958,0,1363,476,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]De[445,13,1958,0,1363,476,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]Do[445,13,1958,0,1363,476,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]Dm[445,13,1958,0,1363,476,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]Df[445,13,1958,0,1363,476,0,\"impl-From%3CCertificateError%3E-for-Error\"]Aj[445,13,1958,0,1363,476,0]Am[2535,13,2536,2536,121,476,0]An[2535,13,2536,2536,2430,476,0]An[3260,13,3261,4608,4340,476,0]An[3260,13,3261,3234,3182,476,0]An[3260,13,3261,1093,1598,476,0]An[3260,13,3261,2187,2046,476,0]An[3260,13,3261,2187,3397,476,0]An[3260,13,3261,2187,3403,476,0]An[3260,13,3261,3787,3372,476,0]An[3260,13,3261,1089,3394,476,0]An[3260,13,3261,1876,4379,476,0]An[3260,13,3261,2487,2440,476,0]An[3260,13,3261,2487,4346,476,0]An[3260,13,3261,2487,4780,476,0]An[3260,13,3261,2487,4357,476,0]An[3260,13,3261,1941,5250,476,0]An[3260,13,3261,4387,5220,476,0]An[3260,13,3261,1583,4596,476,0]An[3260,13,3261,1583,4234,476,0]An[3260,13,3261,1583,4474,476,0]Co[3260,13,3261,1995,1634,476,0,\"impl-From%3CCode%3E-for-VarInt\"]Dc[3260,13,3261,1995,1634,476,0,\"impl-From%3CStreamId%3E-for-VarInt\"]Cn[3260,13,3261,1995,1634,476,0,\"impl-From%3Cu32%3E-for-VarInt\"]Cm[3260,13,3261,1995,1634,476,0,\"impl-From%3Cu8%3E-for-VarInt\"]An[3260,13,3261,1995,1634,476,0]Cn[3260,13,3261,1995,1634,476,0,\"impl-From%3Cu16%3E-for-VarInt\"]An[3260,13,3261,1995,5042,476,0]An[3260,13,3261,3225,1347,476,0]An[3260,13,3261,3234,4879,476,0]An[3260,13,3261,1557,1617,476,0]0An[3260,13,3261,1557,4245,476,0]An[3260,13,3261,1557,3392,476,0]An[3260,13,3261,1557,3205,476,0]An[3260,13,3261,1557,4877,476,0]An[3260,13,3261,1557,4933,476,0]An[3260,13,3261,1557,3367,476,0]An[3260,13,3261,1572,2477,476,0]An[3260,13,3261,1572,3378,476,0]An[3260,13,3261,1572,3207,476,0]An[3260,13,3261,1572,4580,476,0]An[3260,13,3261,1093,3210,476,0]An[3260,13,3261,1093,4374,476,0]An[3260,13,3261,1098,2053,476,0]An[3260,13,3261,1098,3417,476,0]An[3260,13,3261,1098,4077,476,0]An[3260,13,3261,2187,4095,476,0]An[3260,13,3261,2187,4484,476,0]An[3260,13,3261,2187,4210,476,0]An[3260,13,3261,3787,4576,476,0]0An[3260,13,3261,3787,3202,476,0]An[3260,13,3261,3787,4450,476,0]An[3260,13,3261,3787,5127,476,0]An[3260,13,3261,3787,1368,476,0]0An[3260,13,3261,1089,4590,476,0]An[3260,13,3261,3354,4599,476,0]An[3260,13,3261,3354,4903,476,0]An[3260,13,3261,3354,4918,476,0]An[3260,13,3261,3354,4079,476,0]0An[3260,13,3261,1876,4460,476,0]An[3260,13,3261,1876,4251,476,0]An[3260,13,3261,1876,5099,476,0]An[3260,13,3261,1876,4202,476,0]Ed[3260,13,3261,1876,4069,476,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]An[3260,13,3261,1876,4069,476,0]Do[3260,13,3261,1876,4069,476,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]An[3260,13,3261,1547,3190,476,0]An[3260,13,3261,1547,4465,476,0]An[3260,13,3261,1547,3384,476,0]An[3260,13,3261,1547,3406,476,0]An[3260,13,3261,1547,4217,476,0]An[3260,13,3261,1547,4574,476,0]0An[3260,13,3261,1547,4773,476,0]An[3260,13,3261,1547,2433,476,0]An[3260,13,3261,2487,4777,476,0]An[3260,13,3261,2487,2444,476,0]An[3260,13,3261,2487,4213,476,0]0An[3260,13,3261,2487,4063,476,0]An[3260,13,3261,2487,3400,476,0]An[3260,13,3261,1941,4369,476,0]An[3260,13,3261,1941,4588,476,0]An[3260,13,3261,1941,5131,476,0]An[3260,13,3261,1941,2447,476,0]An[3260,13,3261,1941,4875,476,0]0An[3260,13,3261,1969,4578,476,0]An[3260,13,3261,1969,4347,476,0]An[3260,13,3261,1969,4216,476,0]An[3260,13,3261,1969,4221,476,0]Ak[3260,13,3261,0,4488,476,0]0Ak[3260,13,3261,0,4937,476,0]0An[3260,13,3261,4387,3381,476,0]An[3260,13,3261,4387,5225,476,0]An[3260,13,3261,4972,4805,476,0]Am[3260,13,3261,3261,493,476,0]Am[3260,13,3261,3261,123,476,0]An[3260,13,3261,3261,2464,476,0]0An[3260,13,3261,3261,2474,476,0]An[3260,13,3261,3261,3188,476,0]An[3260,13,1874,1874,4380,476,0]An[3260,13,5083,5083,4982,476,0]An[3260,13,5083,5083,1365,476,0]0An[3260,13,1879,1879,2021,476,0]Am[3260,13,1879,1879,480,476,0]An[3260,13,1879,1879,5191,476,0]An[3260,13,1879,1879,4072,476,0]0An[3260,13,1879,1879,4943,476,0]An[3260,13,1959,1959,3412,476,0]An[3260,13,1959,1959,4351,476,0]An[3260,13,1959,1959,4801,476,0]An[3260,13,1959,1959,5038,476,0]An[3260,13,1959,1959,4803,476,0]Dn[3260,13,1959,0,1364,476,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]E`[3260,13,1959,0,1364,476,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]Df[3260,13,1959,0,1364,476,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]Ak[3260,13,1959,0,1364,476,0]Dg[3260,13,1959,0,1364,476,0,\"impl-From%3CCertificateError%3E-for-Error\"]Dg[3260,13,1959,0,1364,476,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]De[3260,13,1959,0,1364,476,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]De[3260,13,1959,0,1364,476,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]Dg[3260,13,1959,0,1364,476,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]E`[3260,13,1959,0,1364,476,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]Da[3260,13,1959,0,1364,476,0,\"impl-From%3COtherError%3E-for-Error\"]Df[3260,13,1959,0,1364,476,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Al[3260,13,3782,138,117,476,0]Am[3260,13,3782,138,3177,476,0]An[3260,13,3782,1543,1351,476,0]An[3260,13,3782,1543,4074,476,0]An[3260,13,3782,2535,2026,476,0]An[3260,13,3782,2535,4367,476,0]An[3260,13,3782,3782,4867,476,0]An[3260,13,4835,4835,1366,476,0]An[3260,13,4835,4835,1369,476,0]An[2170,13,2171,2171,3415,476,0]0Am[2170,13,2171,399,4489,476,0]An[2170,13,2171,2171,2461,476,0]An[2170,13,2171,2171,2475,476,0]An[2170,13,2171,2171,4222,476,0]An[1068,13,1069,1069,3179,476,0]An[1068,13,1069,1069,4976,476,0]Am[1068,13,1868,1868,131,476,0]An[1068,13,1573,1573,1384,476,0]An[1068,13,1573,1573,4097,476,0]An[1068,13,1573,1573,4584,476,0]An[1068,13,4308,4308,5176,476,0]An[1068,13,4308,4308,5177,476,0]Am[1068,13,1964,1964,132,476,0]Ai[445,13,446,0,1633,477,0]Ai[445,13,446,0,1616,477,0]Ai[445,13,446,0,4078,477,0]Ai[445,13,446,0,4215,477,0]Ah[445,13,446,0,492,477,0]Ah[445,13,446,0,122,477,0]Ai[445,13,446,0,2463,477,0]An[3260,13,3261,1995,1634,477,0]An[3260,13,3261,3234,4879,477,0]An[3260,13,3261,1557,1617,477,0]An[3260,13,3261,1093,3210,477,0]An[3260,13,3261,1093,4374,477,0]An[3260,13,3261,1098,3417,477,0]An[3260,13,3261,3354,4079,477,0]An[3260,13,3261,2487,4777,477,0]An[3260,13,3261,1941,4875,477,0]An[3260,13,3261,1969,4216,477,0]Am[3260,13,3261,3261,493,477,0]Am[3260,13,3261,3261,123,477,0]An[3260,13,3261,3261,2464,477,0]An[3260,13,3261,3261,3188,477,0]Am[1388,13,1389,1389,130,478,0]An[1388,13,1389,1389,4065,478,0]An[1388,13,1571,1571,4096,478,0]An[1388,13,1571,1571,1383,478,0]An[1388,13,1571,1571,4485,478,0]Ak[502,13,503,503,4066,478,0]An[4102,13,4103,4103,5132,478,0]Al[445,13,446,3786,4603,478,0]Al[445,13,446,3786,2454,478,0]Ai[445,13,446,0,4339,478,0]Al[445,13,446,1067,2455,478,0]Al[445,13,446,1067,4769,478,0]Al[445,13,446,3786,3369,478,0]Al[445,13,446,3786,3371,478,0]Al[445,13,446,3786,2028,478,0]Al[445,13,446,3786,1609,478,0]Al[445,13,446,3786,3169,478,0]Al[445,13,446,3786,2427,478,0]Al[445,13,446,3786,4241,478,0]Al[445,13,446,3786,4246,478,0]Al[445,13,446,3786,5044,478,0]Al[445,13,446,3786,4878,478,0]Al[445,13,446,2486,2439,478,0]Al[445,13,446,2486,4348,478,0]Al[445,13,446,2486,1590,478,0]Al[445,13,446,2513,2443,478,0]Al[445,13,446,2513,3399,478,0]Al[445,13,446,2513,4464,478,0]Ak[445,13,446,1067,485,478,0]Al[445,13,446,1067,4490,478,0]Al[445,13,446,4169,3396,478,0]Al[445,13,446,4169,4983,478,0]Al[445,13,446,4169,4483,478,0]Al[445,13,446,4169,3209,478,0]Al[445,13,446,4169,3398,478,0]Al[445,13,446,4169,4482,478,0]Al[445,13,446,1584,4256,478,0]Al[445,13,446,1110,4091,478,0]Al[445,13,446,4174,3402,478,0]Al[445,13,446,4174,3416,478,0]Al[445,13,446,4174,4255,478,0]Al[445,13,446,4174,2042,478,0]Ai[445,13,446,0,3393,478,0]Ai[445,13,446,0,4378,478,0]Ai[445,13,446,0,4595,478,0]Ai[445,13,446,0,4233,478,0]Ai[445,13,446,0,4473,478,0]Ai[445,13,446,0,1633,478,0]Ai[445,13,446,0,5041,478,0]Ai[445,13,446,0,1346,478,0]Ai[445,13,446,0,1616,478,0]Ai[445,13,446,0,2476,478,0]Ai[445,13,446,0,3377,478,0]Ai[445,13,446,0,3206,478,0]Ai[445,13,446,0,4579,478,0]Ai[445,13,446,0,2052,478,0]Ai[445,13,446,0,4206,478,0]Ai[445,13,446,0,4575,478,0]Ai[445,13,446,0,4589,478,0]Ai[445,13,446,0,4598,478,0]Ai[445,13,446,0,4902,478,0]Ai[445,13,446,0,4917,478,0]Ai[445,13,446,0,4078,478,0]Ai[445,13,446,0,4459,478,0]Ai[445,13,446,0,4249,478,0]Ai[445,13,446,0,5098,478,0]Ai[445,13,446,0,4200,478,0]Ai[445,13,446,0,4068,478,0]Ai[445,13,446,0,3189,478,0]Ai[445,13,446,0,4572,478,0]Ai[445,13,446,0,4771,478,0]Ai[445,13,446,0,4212,478,0]Ai[445,13,446,0,4215,478,0]Ai[445,13,446,0,4220,478,0]Ai[445,13,446,0,4936,478,0]Ai[445,13,446,0,3380,478,0]Ai[445,13,446,0,4804,478,0]Ah[445,13,446,0,492,478,0]Ah[445,13,446,0,122,478,0]Ai[445,13,446,0,2463,478,0]Ai[445,13,446,0,2473,478,0]Ai[445,13,3781,0,116,478,0]Aj[445,13,3781,0,3176,478,0]Aj[445,13,3781,0,1350,478,0]Aj[445,13,3781,0,4073,478,0]Aj[445,13,3781,0,2025,478,0]Aj[445,13,3781,0,4366,478,0]Aj[445,13,3781,0,4866,478,0]Aj[445,13,1878,0,2020,478,0]Ai[445,13,1878,0,479,478,0]Aj[445,13,1878,0,5190,478,0]Aj[445,13,1878,0,4071,478,0]Aj[445,13,1878,0,4940,478,0]Aj[445,13,1958,0,3411,478,0]Aj[445,13,1958,0,4350,478,0]Aj[445,13,1958,0,4800,478,0]Aj[445,13,1958,0,5037,478,0]Aj[445,13,1958,0,4802,478,0]Aj[445,13,1958,0,1363,478,0]Am[2535,13,2536,2536,121,478,0]An[2535,13,2536,2536,2430,478,0]An[3260,13,3261,4608,4340,478,0]An[3260,13,3261,3234,3182,478,0]An[3260,13,3261,1093,1598,478,0]An[3260,13,3261,2187,2046,478,0]An[3260,13,3261,2187,3397,478,0]An[3260,13,3261,2187,3403,478,0]An[3260,13,3261,3787,3372,478,0]An[3260,13,3261,1089,3394,478,0]An[3260,13,3261,1876,4379,478,0]An[3260,13,3261,2487,2440,478,0]An[3260,13,3261,2487,4346,478,0]An[3260,13,3261,2487,4780,478,0]An[3260,13,3261,2487,4357,478,0]An[3260,13,3261,1941,5250,478,0]An[3260,13,3261,4387,5220,478,0]An[3260,13,3261,1583,4596,478,0]An[3260,13,3261,1583,4234,478,0]An[3260,13,3261,1583,4474,478,0]An[3260,13,3261,1995,1634,478,0]An[3260,13,3261,1995,5042,478,0]An[3260,13,3261,3225,1347,478,0]An[3260,13,3261,3234,4879,478,0]An[3260,13,3261,1557,1617,478,0]An[3260,13,3261,1557,4245,478,0]An[3260,13,3261,1557,3392,478,0]An[3260,13,3261,1557,3205,478,0]An[3260,13,3261,1557,4877,478,0]An[3260,13,3261,1557,4933,478,0]An[3260,13,3261,1557,3367,478,0]An[3260,13,3261,1572,2477,478,0]An[3260,13,3261,1572,3378,478,0]An[3260,13,3261,1572,3207,478,0]An[3260,13,3261,1572,4580,478,0]An[3260,13,3261,1093,3210,478,0]An[3260,13,3261,1093,4374,478,0]An[3260,13,3261,1098,2053,478,0]An[3260,13,3261,1098,3417,478,0]An[3260,13,3261,1098,4077,478,0]An[3260,13,3261,2187,4095,478,0]An[3260,13,3261,2187,4484,478,0]An[3260,13,3261,2187,4210,478,0]An[3260,13,3261,3787,4576,478,0]An[3260,13,3261,3787,3202,478,0]An[3260,13,3261,3787,4450,478,0]An[3260,13,3261,3787,5127,478,0]An[3260,13,3261,3787,1368,478,0]An[3260,13,3261,1089,4590,478,0]An[3260,13,3261,3354,4599,478,0]An[3260,13,3261,3354,4903,478,0]An[3260,13,3261,3354,4918,478,0]An[3260,13,3261,3354,4079,478,0]An[3260,13,3261,1876,4460,478,0]An[3260,13,3261,1876,4251,478,0]An[3260,13,3261,1876,5099,478,0]An[3260,13,3261,1876,4202,478,0]An[3260,13,3261,1876,4069,478,0]An[3260,13,3261,1547,3190,478,0]An[3260,13,3261,1547,4465,478,0]An[3260,13,3261,1547,3384,478,0]An[3260,13,3261,1547,3406,478,0]An[3260,13,3261,1547,4217,478,0]An[3260,13,3261,1547,4574,478,0]An[3260,13,3261,1547,4773,478,0]An[3260,13,3261,1547,2433,478,0]An[3260,13,3261,2487,4777,478,0]An[3260,13,3261,2487,2444,478,0]An[3260,13,3261,2487,4213,478,0]An[3260,13,3261,2487,4063,478,0]An[3260,13,3261,2487,3400,478,0]An[3260,13,3261,1941,4369,478,0]An[3260,13,3261,1941,4588,478,0]An[3260,13,3261,1941,5131,478,0]An[3260,13,3261,1941,2447,478,0]An[3260,13,3261,1941,4875,478,0]An[3260,13,3261,1969,4578,478,0]An[3260,13,3261,1969,4347,478,0]An[3260,13,3261,1969,4216,478,0]An[3260,13,3261,1969,4221,478,0]Ak[3260,13,3261,0,4488,478,0]Ak[3260,13,3261,0,4937,478,0]An[3260,13,3261,4387,3381,478,0]An[3260,13,3261,4387,5225,478,0]An[3260,13,3261,4972,4805,478,0]Am[3260,13,3261,3261,493,478,0]Am[3260,13,3261,3261,123,478,0]An[3260,13,3261,3261,2464,478,0]An[3260,13,3261,3261,2474,478,0]An[3260,13,3261,3261,3188,478,0]An[3260,13,1874,1874,4380,478,0]An[3260,13,5083,5083,4982,478,0]An[3260,13,5083,5083,1365,478,0]An[3260,13,1879,1879,2021,478,0]Am[3260,13,1879,1879,480,478,0]An[3260,13,1879,1879,5191,478,0]An[3260,13,1879,1879,4072,478,0]An[3260,13,1879,1879,4943,478,0]An[3260,13,1959,1959,3412,478,0]An[3260,13,1959,1959,4351,478,0]An[3260,13,1959,1959,4801,478,0]An[3260,13,1959,1959,5038,478,0]An[3260,13,1959,1959,4803,478,0]Ak[3260,13,1959,0,1364,478,0]Al[3260,13,3782,138,117,478,0]Am[3260,13,3782,138,3177,478,0]An[3260,13,3782,1543,1351,478,0]An[3260,13,3782,1543,4074,478,0]An[3260,13,3782,2535,2026,478,0]An[3260,13,3782,2535,4367,478,0]An[3260,13,3782,3782,4867,478,0]An[3260,13,4835,4835,1366,478,0]An[3260,13,4835,4835,1369,478,0]An[2170,13,2171,2171,3415,478,0]Am[2170,13,2171,399,4489,478,0]An[2170,13,2171,2171,2461,478,0]An[2170,13,2171,2171,2475,478,0]An[2170,13,2171,2171,4222,478,0]An[1068,13,1069,1069,3179,478,0]An[1068,13,1069,1069,4976,478,0]Am[1068,13,1868,1868,131,478,0]An[1068,13,1573,1573,1384,478,0]An[1068,13,1573,1573,4097,478,0]An[1068,13,1573,1573,4584,478,0]An[1068,13,4308,4308,5176,478,0]An[1068,13,4308,4308,5177,478,0]Am[1068,13,1964,1964,132,478,0]Ac[502,7,503,0,0,0,0]Ae[4102,7,4103,0,0,0,0]Ae[4390,7,4391,0,0,0,0]Ae[2535,7,2536,0,0,0,0]Ae[4897,7,4898,0,0,0,0]Al[3260,13,3261,1093,1598,0,0]Ao[3260,13,3261,2487,4357,2445,0]Ah[445,12,1878,0,4868,0,0]Al[3260,13,3261,2187,2046,0,0]Al[3260,12,1879,1879,4869,0,0]Aj[445,13,446,3786,3371,0,0]Ab[1068,3,0,0,0,0,0]Ai[445,13,446,1067,485,0,0]Ag[445,14,446,0,3377,0,0]Al[3260,13,3261,3787,3372,0,0]Al[3260,14,3261,1572,3378,0,0]Aj[445,12,446,2179,3360,0,0]Am[445,13,446,3786,4603,1606,0]Am[445,13,446,3786,2454,1606,0]Am[445,13,446,1067,2455,1606,0]Am[445,13,446,3786,3369,1606,0]Am[445,13,446,3786,2028,1606,0]Am[445,13,446,3786,1609,1606,0]Am[445,13,446,3786,3169,1606,0]Am[445,13,446,3786,2427,1606,0]Am[445,13,446,3786,4241,1606,0]Am[445,13,446,3786,4246,1606,0]Am[445,13,446,2486,1590,1606,0]Am[445,13,446,2513,4464,1606,0]Am[445,13,446,4174,2042,1606,0]?Aj[445,13,446,4169,3396,0,0]Al[3260,13,3261,2187,3397,0,0]Al[3260,13,5083,5083,4982,0,0]Al[3260,13,3261,3234,3182,0,0]Ak[2170,13,2171,399,4489,0,0]Ah[445,12,1878,0,4868,0,0]Al[3260,12,1879,1879,4869,0,0]32Ag[445,13,446,0,5098,0,0]Al[3260,13,3261,1876,5099,0,0]Aj[445,13,446,3786,3371,0,0]Al[3260,13,3261,3787,3372,0,0]Ah[445,12,1878,0,2016,0,0]Al[3260,12,1879,1879,2017,0,0]Al[1388,14,1571,1571,4485,0,0]Ag[445,14,446,0,2473,0,0]Al[3260,14,3261,3261,2474,0,0]Ai[3260,14,4587,0,1380,0,0]Aj[445,13,446,4169,3396,0,0]Aj[445,13,446,4169,4983,0,0]Al[3260,13,3261,2187,3397,0,0]Ag[445,12,446,0,3413,0,0]Aj[445,13,446,0,4473,3413,0]Aj[445,13,446,0,4804,3413,0]Al[3260,12,3261,1583,3414,0,0]Ao[3260,13,3261,1583,4474,3414,0]Ao[3260,13,3261,4972,4805,3414,0]Am[1388,13,1389,1389,130,495,0]An[1388,13,1389,1389,4065,495,0]An[1388,13,1571,1571,4096,495,0]An[1388,13,1571,1571,1383,495,0]An[1388,13,1571,1571,4485,495,0]Ak[502,13,503,503,4066,495,0]An[4102,13,4103,4103,5132,495,0]Al[445,13,446,3786,4603,495,0]Al[445,13,446,3786,2454,495,0]Ai[445,13,446,0,4339,495,0]Al[445,13,446,1067,2455,495,0]Al[445,13,446,1067,4769,495,0]Al[445,13,446,3786,3369,495,0]Al[445,13,446,3786,3371,495,0]Al[445,13,446,3786,2028,495,0]Al[445,13,446,3786,1609,495,0]Al[445,13,446,3786,3169,495,0]Al[445,13,446,3786,2427,495,0]Al[445,13,446,3786,4241,495,0]Al[445,13,446,3786,4246,495,0]Al[445,13,446,3786,5044,495,0]Al[445,13,446,3786,4878,495,0]Al[445,13,446,2486,2439,495,0]Al[445,13,446,2486,4348,495,0]Al[445,13,446,2486,1590,495,0]Al[445,13,446,2513,2443,495,0]Al[445,13,446,2513,3399,495,0]Al[445,13,446,2513,4464,495,0]Ak[445,13,446,1067,485,495,0]Al[445,13,446,1067,4490,495,0]Al[445,13,446,4169,3396,495,0]Al[445,13,446,4169,4983,495,0]Al[445,13,446,4169,4483,495,0]Al[445,13,446,4169,3209,495,0]Al[445,13,446,4169,3398,495,0]Al[445,13,446,4169,4482,495,0]Al[445,13,446,1584,4256,495,0]Al[445,13,446,1110,4091,495,0]Al[445,13,446,4174,3402,495,0]Al[445,13,446,4174,3416,495,0]Al[445,13,446,4174,4255,495,0]Al[445,13,446,4174,2042,495,0]Ai[445,13,446,0,3393,495,0]Ai[445,13,446,0,4378,495,0]Ai[445,13,446,0,4595,495,0]Ai[445,13,446,0,4233,495,0]Ai[445,13,446,0,4473,495,0]Ai[445,13,446,0,1633,495,0]Ai[445,13,446,0,5041,495,0]Ai[445,13,446,0,1346,495,0]Ai[445,13,446,0,1616,495,0]Ai[445,13,446,0,2476,495,0]Ai[445,13,446,0,3377,495,0]Ai[445,13,446,0,3206,495,0]Ai[445,13,446,0,4579,495,0]Ai[445,13,446,0,2052,495,0]Ai[445,13,446,0,4206,495,0]Ai[445,13,446,0,4575,495,0]Ai[445,13,446,0,4589,495,0]Ai[445,13,446,0,4598,495,0]Ai[445,13,446,0,4902,495,0]Ai[445,13,446,0,4917,495,0]Ai[445,13,446,0,4078,495,0]Ai[445,13,446,0,4459,495,0]Ai[445,13,446,0,4249,495,0]Ai[445,13,446,0,5098,495,0]Ai[445,13,446,0,4200,495,0]Ai[445,13,446,0,4068,495,0]Ai[445,13,446,0,3189,495,0]Ai[445,13,446,0,4572,495,0]Ai[445,13,446,0,4771,495,0]Ai[445,13,446,0,4212,495,0]Ai[445,13,446,0,4215,495,0]Ai[445,13,446,0,4220,495,0]Ai[445,13,446,0,4936,495,0]Ai[445,13,446,0,3380,495,0]Ai[445,13,446,0,4804,495,0]Ah[445,13,446,0,492,495,0]Ah[445,13,446,0,122,495,0]Ai[445,13,446,0,2463,495,0]Ai[445,13,446,0,2473,495,0]Ai[445,13,3781,0,116,495,0]Aj[445,13,3781,0,3176,495,0]Aj[445,13,3781,0,1350,495,0]Aj[445,13,3781,0,4073,495,0]Aj[445,13,3781,0,2025,495,0]Aj[445,13,3781,0,4366,495,0]Aj[445,13,3781,0,4866,495,0]Aj[445,13,1878,0,2020,495,0]Ai[445,13,1878,0,479,495,0]Aj[445,13,1878,0,5190,495,0]Aj[445,13,1878,0,4071,495,0]Aj[445,13,1878,0,4940,495,0]Aj[445,13,1958,0,3411,495,0]Aj[445,13,1958,0,4350,495,0]Aj[445,13,1958,0,4800,495,0]Aj[445,13,1958,0,5037,495,0]Aj[445,13,1958,0,4802,495,0]Aj[445,13,1958,0,1363,495,0]Am[2535,13,2536,2536,121,495,0]An[2535,13,2536,2536,2430,495,0]An[3260,13,3261,4608,4340,495,0]An[3260,13,3261,3234,3182,495,0]An[3260,13,3261,1093,1598,495,0]An[3260,13,3261,2187,2046,495,0]An[3260,13,3261,2187,3397,495,0]An[3260,13,3261,2187,3403,495,0]An[3260,13,3261,3787,3372,495,0]An[3260,13,3261,1089,3394,495,0]An[3260,13,3261,1876,4379,495,0]An[3260,13,3261,2487,2440,495,0]An[3260,13,3261,2487,4346,495,0]An[3260,13,3261,2487,4780,495,0]An[3260,13,3261,2487,4357,495,0]An[3260,13,3261,1941,5250,495,0]An[3260,13,3261,4387,5220,495,0]An[3260,13,3261,1583,4596,495,0]An[3260,13,3261,1583,4234,495,0]An[3260,13,3261,1583,4474,495,0]An[3260,13,3261,1995,1634,495,0]An[3260,13,3261,1995,5042,495,0]An[3260,13,3261,3225,1347,495,0]An[3260,13,3261,3234,4879,495,0]An[3260,13,3261,1557,1617,495,0]An[3260,13,3261,1557,4245,495,0]An[3260,13,3261,1557,3392,495,0]An[3260,13,3261,1557,3205,495,0]An[3260,13,3261,1557,4877,495,0]An[3260,13,3261,1557,4933,495,0]An[3260,13,3261,1557,3367,495,0]An[3260,13,3261,1572,2477,495,0]An[3260,13,3261,1572,3378,495,0]An[3260,13,3261,1572,3207,495,0]An[3260,13,3261,1572,4580,495,0]An[3260,13,3261,1093,3210,495,0]An[3260,13,3261,1093,4374,495,0]An[3260,13,3261,1098,2053,495,0]An[3260,13,3261,1098,3417,495,0]An[3260,13,3261,1098,4077,495,0]An[3260,13,3261,2187,4095,495,0]An[3260,13,3261,2187,4484,495,0]An[3260,13,3261,2187,4210,495,0]An[3260,13,3261,3787,4576,495,0]An[3260,13,3261,3787,3202,495,0]An[3260,13,3261,3787,4450,495,0]An[3260,13,3261,3787,5127,495,0]An[3260,13,3261,3787,1368,495,0]An[3260,13,3261,1089,4590,495,0]An[3260,13,3261,3354,4599,495,0]An[3260,13,3261,3354,4903,495,0]An[3260,13,3261,3354,4918,495,0]An[3260,13,3261,3354,4079,495,0]An[3260,13,3261,1876,4460,495,0]An[3260,13,3261,1876,4251,495,0]An[3260,13,3261,1876,5099,495,0]An[3260,13,3261,1876,4202,495,0]An[3260,13,3261,1876,4069,495,0]An[3260,13,3261,1547,3190,495,0]An[3260,13,3261,1547,4465,495,0]An[3260,13,3261,1547,3384,495,0]An[3260,13,3261,1547,3406,495,0]An[3260,13,3261,1547,4217,495,0]An[3260,13,3261,1547,4574,495,0]An[3260,13,3261,1547,4773,495,0]An[3260,13,3261,1547,2433,495,0]An[3260,13,3261,2487,4777,495,0]An[3260,13,3261,2487,2444,495,0]An[3260,13,3261,2487,4213,495,0]An[3260,13,3261,2487,4063,495,0]An[3260,13,3261,2487,3400,495,0]An[3260,13,3261,1941,4369,495,0]An[3260,13,3261,1941,4588,495,0]An[3260,13,3261,1941,5131,495,0]An[3260,13,3261,1941,2447,495,0]An[3260,13,3261,1941,4875,495,0]An[3260,13,3261,1969,4578,495,0]An[3260,13,3261,1969,4347,495,0]An[3260,13,3261,1969,4216,495,0]An[3260,13,3261,1969,4221,495,0]Ak[3260,13,3261,0,4488,495,0]Ak[3260,13,3261,0,4937,495,0]An[3260,13,3261,4387,3381,495,0]An[3260,13,3261,4387,5225,495,0]An[3260,13,3261,4972,4805,495,0]Am[3260,13,3261,3261,493,495,0]Am[3260,13,3261,3261,123,495,0]An[3260,13,3261,3261,2464,495,0]An[3260,13,3261,3261,2474,495,0]An[3260,13,3261,3261,3188,495,0]An[3260,13,1874,1874,4380,495,0]An[3260,13,5083,5083,4982,495,0]An[3260,13,5083,5083,1365,495,0]An[3260,13,1879,1879,2021,495,0]Am[3260,13,1879,1879,480,495,0]An[3260,13,1879,1879,5191,495,0]An[3260,13,1879,1879,4072,495,0]An[3260,13,1879,1879,4943,495,0]An[3260,13,1959,1959,3412,495,0]An[3260,13,1959,1959,4351,495,0]An[3260,13,1959,1959,4801,495,0]An[3260,13,1959,1959,5038,495,0]An[3260,13,1959,1959,4803,495,0]Ak[3260,13,1959,0,1364,495,0]Al[3260,13,3782,138,117,495,0]Am[3260,13,3782,138,3177,495,0]An[3260,13,3782,1543,1351,495,0]An[3260,13,3782,1543,4074,495,0]An[3260,13,3782,2535,2026,495,0]An[3260,13,3782,2535,4367,495,0]An[3260,13,3782,3782,4867,495,0]An[3260,13,4835,4835,1366,495,0]An[3260,13,4835,4835,1369,495,0]An[1068,13,1069,1069,3179,495,0]An[1068,13,1069,1069,4976,495,0]Am[1068,13,1868,1868,131,495,0]An[1068,13,1573,1573,1384,495,0]An[1068,13,1573,1573,4097,495,0]An[1068,13,1573,1573,4584,495,0]An[1068,13,4308,4308,5176,495,0]An[1068,13,4308,4308,5177,495,0]Am[1068,13,1964,1964,132,495,0]Af[445,5,446,3225,0,0,0]Ah[3260,5,3261,3225,0,0,0]Ag[445,5,3781,1543,0,0,0]Ah[3260,5,3782,1543,0,0,0]Al[1068,15,1069,1069,4976,0,0]Ag[445,6,1958,1546,0,0,0]Ah[3260,6,1959,1546,0,0,0]Ae[3260,6,5083,0,0,0,0]Ae[3260,6,4835,0,0,0,0]Ah[3260,6,3261,3787,0,0,0]1Al[3260,15,3261,1547,3384,0,0]Ah[445,15,1958,0,1363,0,0]Ai[3260,15,1959,0,1364,0,0]Aj[445,15,446,4169,3209,0,0]Ag[445,15,446,0,4575,0,0]Al[3260,15,3261,1093,3210,0,0]Al[3260,15,3261,3787,4576,0,0]Al[3260,15,3261,1941,4588,0,0]0Ae[1388,5,1571,0,0,0,0]Ae[1068,5,1573,0,0,0,0]Al[3260,13,3261,2487,4780,0,0]Ab[1388,3,0,0,0,0,0]Ah[445,12,3781,0,4864,0,0]Ak[445,13,3781,0,3176,4864,0]Ak[445,13,3781,0,4073,4864,0]Ak[445,13,3781,0,4366,4864,0]Al[3260,12,3782,3782,4865,0,0]An[3260,13,3782,138,3177,4865,0]Ao[3260,13,3782,1543,4074,4865,0]Ao[3260,13,3782,2535,4367,4865,0]Al[1068,13,1069,1069,4976,0,0]Ag[445,14,446,0,1346,0,0]Ag[445,14,446,0,2476,0,0]Ag[445,14,446,0,2052,0,0]Al[3260,14,3261,3225,1347,0,0]Al[3260,14,3261,1572,2477,0,0]Al[3260,14,3261,1098,2053,0,0]Al[3260,14,3261,2487,4063,0,0]An[1388,13,1389,1389,130,1348,0]Ao[1388,13,1389,1389,4065,1348,0]Am[445,13,446,3786,3371,1348,0]Am[445,13,446,3786,5044,1348,0]Am[445,13,446,3786,4878,1348,0]Am[445,13,446,2486,2439,1348,0]Am[445,13,446,2486,4348,1348,0]Al[445,13,446,1067,485,1348,0]Am[445,13,446,1067,4490,1348,0]Am[445,13,446,4169,4483,1348,0]Am[445,13,446,4169,3209,1348,0]Am[445,13,446,4169,3398,1348,0]Am[445,13,446,4169,4482,1348,0]Am[445,13,446,4174,3416,1348,0]Am[445,13,446,4174,4255,1348,0]Aj[445,13,446,0,1633,1348,0]Aj[445,13,446,0,5041,1348,0]Aj[445,13,446,0,1616,1348,0]Aj[445,13,446,0,2476,1348,0]Aj[445,13,446,0,3377,1348,0]Aj[445,13,446,0,3206,1348,0]Aj[445,13,446,0,4579,1348,0]Aj[445,13,446,0,2052,1348,0]Aj[445,13,446,0,4206,1348,0]Aj[445,13,446,0,4575,1348,0]Aj[445,13,446,0,4598,1348,0]Aj[445,13,446,0,4902,1348,0]Aj[445,13,446,0,4917,1348,0]Aj[445,13,446,0,4078,1348,0]Aj[445,13,446,0,4459,1348,0]Aj[445,13,446,0,4249,1348,0]Aj[445,13,446,0,5098,1348,0]Aj[445,13,446,0,4200,1348,0]Aj[445,13,446,0,4068,1348,0]Aj[445,13,446,0,3189,1348,0]Aj[445,13,446,0,4572,1348,0]Aj[445,13,446,0,4771,1348,0]Aj[445,13,446,0,4212,1348,0]Aj[445,13,446,0,4215,1348,0]Aj[445,13,446,0,4220,1348,0]Aj[445,13,446,0,4936,1348,0]Aj[445,13,446,0,3380,1348,0]Ai[445,13,446,0,492,1348,0]Ai[445,13,446,0,122,1348,0]Aj[445,13,446,0,2463,1348,0]Aj[445,13,3781,0,116,1348,0]Ak[445,13,3781,0,3176,1348,0]Ak[445,13,3781,0,1350,1348,0]Ak[445,13,3781,0,4073,1348,0]Ak[445,13,3781,0,2025,1348,0]Ak[445,13,3781,0,4366,1348,0]Ak[445,13,1958,0,4800,1348,0]Ak[445,13,1958,0,5037,1348,0]Ak[445,13,1958,0,4802,1348,0]Ak[445,13,1958,0,1363,1348,0]Ao[3260,13,3261,1995,1634,1348,0]Ao[3260,13,3261,1995,5042,1348,0]Ao[3260,13,3261,3234,4879,1348,0]Ao[3260,13,3261,1557,1617,1348,0]Ao[3260,13,3261,1557,4245,1348,0]Ao[3260,13,3261,1557,3392,1348,0]Ao[3260,13,3261,1557,3205,1348,0]Ao[3260,13,3261,1557,4877,1348,0]Ao[3260,13,3261,1557,4933,1348,0]Ao[3260,13,3261,1557,3367,1348,0]Ao[3260,13,3261,1572,2477,1348,0]Ao[3260,13,3261,1572,3378,1348,0]Ao[3260,13,3261,1572,3207,1348,0]Ao[3260,13,3261,1572,4580,1348,0]Ao[3260,13,3261,1093,3210,1348,0]Ao[3260,13,3261,1093,4374,1348,0]Ao[3260,13,3261,1098,2053,1348,0]Ao[3260,13,3261,1098,3417,1348,0]Ao[3260,13,3261,1098,4077,1348,0]Ao[3260,13,3261,2187,4484,1348,0]Ao[3260,13,3261,2187,4210,1348,0]Ao[3260,13,3261,3787,4576,1348,0]Ao[3260,13,3261,3787,3202,1348,0]Ao[3260,13,3261,3787,4450,1348,0]Ao[3260,13,3261,3787,5127,1348,0]Ao[3260,13,3261,3354,4599,1348,0]Ao[3260,13,3261,3354,4903,1348,0]Ao[3260,13,3261,3354,4918,1348,0]Ao[3260,13,3261,3354,4079,1348,0]Ao[3260,13,3261,1876,4460,1348,0]Ao[3260,13,3261,1876,4251,1348,0]Ao[3260,13,3261,1876,5099,1348,0]Ao[3260,13,3261,1876,4202,1348,0]Ao[3260,13,3261,1876,4069,1348,0]Ao[3260,13,3261,1547,3190,1348,0]Ao[3260,13,3261,1547,3384,1348,0]Ao[3260,13,3261,1547,3406,1348,0]Ao[3260,13,3261,1547,4217,1348,0]Ao[3260,13,3261,1547,4574,1348,0]Ao[3260,13,3261,1547,4773,1348,0]Ao[3260,13,3261,1547,2433,1348,0]Ao[3260,13,3261,2487,4777,1348,0]Ao[3260,13,3261,2487,4213,1348,0]Ao[3260,13,3261,1941,4588,1348,0]Ao[3260,13,3261,1941,5131,1348,0]Ao[3260,13,3261,1941,2447,1348,0]Ao[3260,13,3261,1941,4875,1348,0]Ao[3260,13,3261,1969,4216,1348,0]Ao[3260,13,3261,1969,4221,1348,0]Al[3260,13,3261,0,4488,1348,0]Al[3260,13,3261,0,4937,1348,0]Ao[3260,13,3261,4387,3381,1348,0]Ao[3260,13,3261,4387,5225,1348,0]An[3260,13,3261,3261,493,1348,0]An[3260,13,3261,3261,123,1348,0]Ao[3260,13,3261,3261,2464,1348,0]Ao[3260,13,3261,3261,3188,1348,0]Ao[3260,13,1874,1874,4380,1348,0]Ao[3260,13,5083,5083,4982,1348,0]Ao[3260,13,5083,5083,1365,1348,0]Ao[3260,13,1959,1959,4801,1348,0]Ao[3260,13,1959,1959,5038,1348,0]Ao[3260,13,1959,1959,4803,1348,0]Al[3260,13,1959,0,1364,1348,0]Am[3260,13,3782,138,117,1348,0]An[3260,13,3782,138,3177,1348,0]Ao[3260,13,3782,1543,1351,1348,0]Ao[3260,13,3782,1543,4074,1348,0]Ao[3260,13,3782,2535,2026,1348,0]Ao[3260,13,3782,2535,4367,1348,0]Ao[3260,13,4835,4835,1369,1348,0]Ao[2170,13,2171,2171,2461,1348,0]Ao[2170,13,2171,2171,2475,1348,0]Ao[2170,13,2171,2171,4222,1348,0]Ao[1068,13,1069,1069,4976,1348,0]Ao[1068,13,1573,1573,4584,1348,0]Ao[1068,13,4308,4308,5176,1348,0]Ao[1068,13,4308,4308,5177,1348,0]Aj[445,13,446,3786,3371,0,0]Aj[445,13,446,2486,2439,0,0]Ai[445,13,446,1067,485,0,0]Al[3260,13,3261,3787,3372,0,0]Aj[445,13,446,0,4215,1354,0]Ao[3260,13,3261,1969,4216,1354,0]Ai[3260,14,3261,0,4488,0,0]Ag[445,13,446,0,2463,0,0]Al[3260,13,3261,3261,2464,0,0]Ag[445,13,446,0,4220,0,0]Al[3260,13,3261,1969,4221,0,0]Ah[445,14,1878,0,2020,0,0]Al[3260,14,1879,1879,2021,0,0]Al[3260,12,3261,1941,4908,0,0]Ao[3260,13,3261,1941,5250,4908,0];Al[1388,13,1571,1571,1383,0,0]Ai[502,13,503,503,4066,0,0]Al[1068,13,1573,1573,1384,0,0]Aj[445,12,446,2179,3360,0,0]Aj[445,13,446,4174,3402,0,0]Al[3260,13,3261,2187,3403,0,0]Aj[445,13,446,2513,2443,0,0]Al[3260,13,3261,2487,2440,0,0]Aj[445,12,446,2179,2037,0,0]Am[445,13,446,1584,4256,2037,0]Am[445,13,446,1110,4091,2037,0]Ae[1388,2,1389,0,0,0,0]Ak[1388,14,1389,1389,130,0,0]Aj[445,13,446,3786,3371,0,0]Aj[445,13,446,2486,2439,0,0]Ai[445,13,446,1067,485,0,0]Al[3260,13,3261,3787,3372,0,0]Ae[1068,2,1069,0,0,0,0]Ag[445,13,446,0,3393,0,0]Al[3260,13,3261,1089,3394,0,0]?>Al[3260,13,5083,5083,4982,0,0]Af[445,5,446,2486,0,0,0]Ag[445,15,446,0,3189,0,0]Al[3260,15,3261,1547,3190,0,0]Al[1388,15,1389,1389,4065,0,0]0Al[3260,15,3261,1557,3392,0,0]Ah[3260,5,3261,1093,0,0,0]Af[445,15,446,0,492,0,0]Al[2535,15,2536,2536,2430,0,0]Ak[3260,15,3261,3261,493,0,0]Al[3260,15,4835,4835,1366,0,0]87Af[445,5,446,3786,0,0,0]Al[3260,15,3261,1557,3205,0,0]Al[3260,15,3261,2187,4095,0,0]Af[445,5,446,1557,0,0,0]Ah[3260,5,3261,1557,0,0,0]Ae[3260,8,1874,0,0,0,0]987>Al[3260,15,3261,3787,1368,0,0]>Af[445,5,446,1995,0,0,0]Ah[3260,5,3261,1995,0,0,0]Aj[445,13,446,2486,2439,0,0]Aj[445,13,446,2513,2443,0,0]Al[3260,13,3261,2187,2046,0,0]Al[3260,13,3261,2487,2440,0,0]An[1388,13,1389,1389,130,1596,0]Ao[1388,13,1389,1389,4065,1596,0]Ao[1388,13,1571,1571,4096,1596,0]Ao[1388,13,1571,1571,1383,1596,0]Ao[1388,13,1571,1571,4485,1596,0]Al[502,13,503,503,4066,1596,0]Ao[4102,13,4103,4103,5132,1596,0]Am[445,13,446,3786,4603,1596,0]Am[445,13,446,3786,2454,1596,0]Aj[445,13,446,0,4339,1596,0]Am[445,13,446,1067,2455,1596,0]Am[445,13,446,1067,4769,1596,0]Am[445,13,446,3786,3369,1596,0]Am[445,13,446,3786,3371,1596,0]Am[445,13,446,3786,2028,1596,0]Am[445,13,446,3786,1609,1596,0]Am[445,13,446,3786,3169,1596,0]Am[445,13,446,3786,2427,1596,0]Am[445,13,446,3786,4241,1596,0]Am[445,13,446,3786,4246,1596,0]Am[445,13,446,3786,5044,1596,0]Am[445,13,446,3786,4878,1596,0]Am[445,13,446,2486,2439,1596,0]Am[445,13,446,2486,4348,1596,0]Am[445,13,446,2486,1590,1596,0]Am[445,13,446,2513,2443,1596,0]Am[445,13,446,2513,3399,1596,0]Am[445,13,446,2513,4464,1596,0]Al[445,13,446,1067,485,1596,0]Am[445,13,446,1067,4490,1596,0]Am[445,13,446,4169,3396,1596,0]Am[445,13,446,4169,4983,1596,0]Am[445,13,446,4169,4483,1596,0]Am[445,13,446,4169,3209,1596,0]Am[445,13,446,4169,3398,1596,0]Am[445,13,446,4169,4482,1596,0]Am[445,13,446,1584,4256,1596,0]Am[445,13,446,1110,4091,1596,0]Am[445,13,446,4174,3402,1596,0]Am[445,13,446,4174,3416,1596,0]Am[445,13,446,4174,4255,1596,0]Am[445,13,446,4174,2042,1596,0]Aj[445,13,446,0,3393,1596,0]Aj[445,13,446,0,4378,1596,0]Aj[445,13,446,0,4595,1596,0]Aj[445,13,446,0,4233,1596,0]Aj[445,13,446,0,4473,1596,0]Aj[445,13,446,0,1633,1596,0]Aj[445,13,446,0,5041,1596,0]Aj[445,13,446,0,1346,1596,0]Aj[445,13,446,0,1616,1596,0]Aj[445,13,446,0,2476,1596,0]Aj[445,13,446,0,3377,1596,0]Aj[445,13,446,0,3206,1596,0]Aj[445,13,446,0,4579,1596,0]Aj[445,13,446,0,2052,1596,0]Aj[445,13,446,0,4206,1596,0]Aj[445,13,446,0,4575,1596,0]Aj[445,13,446,0,4589,1596,0]Aj[445,13,446,0,4598,1596,0]Aj[445,13,446,0,4902,1596,0]Aj[445,13,446,0,4917,1596,0]Aj[445,13,446,0,4078,1596,0]Aj[445,13,446,0,4459,1596,0]Aj[445,13,446,0,4249,1596,0]Aj[445,13,446,0,5098,1596,0]Aj[445,13,446,0,4200,1596,0]Aj[445,13,446,0,4068,1596,0]Aj[445,13,446,0,3189,1596,0]Aj[445,13,446,0,4572,1596,0]Aj[445,13,446,0,4771,1596,0]Aj[445,13,446,0,4212,1596,0]Aj[445,13,446,0,4215,1596,0]Aj[445,13,446,0,4220,1596,0]Aj[445,13,446,0,4936,1596,0]Aj[445,13,446,0,3380,1596,0]Aj[445,13,446,0,4804,1596,0]Ai[445,13,446,0,492,1596,0]Ai[445,13,446,0,122,1596,0]Aj[445,13,446,0,2463,1596,0]Aj[445,13,446,0,2473,1596,0]Aj[445,13,3781,0,116,1596,0]Ak[445,13,3781,0,3176,1596,0]Ak[445,13,3781,0,1350,1596,0]Ak[445,13,3781,0,4073,1596,0]Ak[445,13,3781,0,2025,1596,0]Ak[445,13,3781,0,4366,1596,0]Ak[445,13,3781,0,4866,1596,0]Ak[445,13,1878,0,2020,1596,0]Aj[445,13,1878,0,479,1596,0]Ak[445,13,1878,0,5190,1596,0]Ak[445,13,1878,0,4071,1596,0]Ak[445,13,1878,0,4940,1596,0]Ak[445,13,1958,0,3411,1596,0]Ak[445,13,1958,0,4350,1596,0]Ak[445,13,1958,0,4800,1596,0]Ak[445,13,1958,0,5037,1596,0]Ak[445,13,1958,0,4802,1596,0]Ak[445,13,1958,0,1363,1596,0]An[2535,13,2536,2536,121,1596,0]Ao[2535,13,2536,2536,2430,1596,0]Ao[3260,13,3261,4608,4340,1596,0]Ao[3260,13,3261,3234,3182,1596,0]Ao[3260,13,3261,1093,1598,1596,0]Ao[3260,13,3261,2187,2046,1596,0]Ao[3260,13,3261,2187,3397,1596,0]Ao[3260,13,3261,2187,3403,1596,0]Ao[3260,13,3261,3787,3372,1596,0]Ao[3260,13,3261,1089,3394,1596,0]Ao[3260,13,3261,1876,4379,1596,0]Ao[3260,13,3261,2487,2440,1596,0]Ao[3260,13,3261,2487,4346,1596,0]Ao[3260,13,3261,2487,4780,1596,0]Ao[3260,13,3261,2487,4357,1596,0]Ao[3260,13,3261,1941,5250,1596,0]Ao[3260,13,3261,4387,5220,1596,0]Ao[3260,13,3261,1583,4596,1596,0]Ao[3260,13,3261,1583,4234,1596,0]Ao[3260,13,3261,1583,4474,1596,0]Ao[3260,13,3261,1995,1634,1596,0]Ao[3260,13,3261,1995,5042,1596,0]Ao[3260,13,3261,3225,1347,1596,0]Ao[3260,13,3261,3234,4879,1596,0]Ao[3260,13,3261,1557,1617,1596,0]Ao[3260,13,3261,1557,4245,1596,0]Ao[3260,13,3261,1557,3392,1596,0]Ao[3260,13,3261,1557,3205,1596,0]Ao[3260,13,3261,1557,4877,1596,0]Ao[3260,13,3261,1557,4933,1596,0]Ao[3260,13,3261,1557,3367,1596,0]Ao[3260,13,3261,1572,2477,1596,0]Ao[3260,13,3261,1572,3378,1596,0]Ao[3260,13,3261,1572,3207,1596,0]Ao[3260,13,3261,1572,4580,1596,0]Ao[3260,13,3261,1093,3210,1596,0]Ao[3260,13,3261,1093,4374,1596,0]Ao[3260,13,3261,1098,2053,1596,0]Ao[3260,13,3261,1098,3417,1596,0]Ao[3260,13,3261,1098,4077,1596,0]Ao[3260,13,3261,2187,4095,1596,0]Ao[3260,13,3261,2187,4484,1596,0]Ao[3260,13,3261,2187,4210,1596,0]Ao[3260,13,3261,3787,4576,1596,0]Ao[3260,13,3261,3787,3202,1596,0]Ao[3260,13,3261,3787,4450,1596,0]Ao[3260,13,3261,3787,5127,1596,0]Ao[3260,13,3261,3787,1368,1596,0]Ao[3260,13,3261,1089,4590,1596,0]Ao[3260,13,3261,3354,4599,1596,0]Ao[3260,13,3261,3354,4903,1596,0]Ao[3260,13,3261,3354,4918,1596,0]Ao[3260,13,3261,3354,4079,1596,0]Ao[3260,13,3261,1876,4460,1596,0]Ao[3260,13,3261,1876,4251,1596,0]Ao[3260,13,3261,1876,5099,1596,0]Ao[3260,13,3261,1876,4202,1596,0]Ao[3260,13,3261,1876,4069,1596,0]Ao[3260,13,3261,1547,3190,1596,0]Ao[3260,13,3261,1547,4465,1596,0]Ao[3260,13,3261,1547,3384,1596,0]Ao[3260,13,3261,1547,3406,1596,0]Ao[3260,13,3261,1547,4217,1596,0]Ao[3260,13,3261,1547,4574,1596,0]Ao[3260,13,3261,1547,4773,1596,0]Ao[3260,13,3261,1547,2433,1596,0]Ao[3260,13,3261,2487,4777,1596,0]Ao[3260,13,3261,2487,2444,1596,0]Ao[3260,13,3261,2487,4213,1596,0]Ao[3260,13,3261,2487,4063,1596,0]Ao[3260,13,3261,2487,3400,1596,0]Ao[3260,13,3261,1941,4369,1596,0]Ao[3260,13,3261,1941,4588,1596,0]Ao[3260,13,3261,1941,5131,1596,0]Ao[3260,13,3261,1941,2447,1596,0]Ao[3260,13,3261,1941,4875,1596,0]Ao[3260,13,3261,1969,4578,1596,0]Ao[3260,13,3261,1969,4347,1596,0]Ao[3260,13,3261,1969,4216,1596,0]Ao[3260,13,3261,1969,4221,1596,0]Al[3260,13,3261,0,4488,1596,0]Al[3260,13,3261,0,4937,1596,0]Ao[3260,13,3261,4387,3381,1596,0]Ao[3260,13,3261,4387,5225,1596,0]Ao[3260,13,3261,4972,4805,1596,0]An[3260,13,3261,3261,493,1596,0]An[3260,13,3261,3261,123,1596,0]Ao[3260,13,3261,3261,2464,1596,0]Ao[3260,13,3261,3261,2474,1596,0]Ao[3260,13,3261,3261,3188,1596,0]Ao[3260,13,1874,1874,4380,1596,0]Ao[3260,13,5083,5083,4982,1596,0]Ao[3260,13,5083,5083,1365,1596,0]Ao[3260,13,1879,1879,2021,1596,0]An[3260,13,1879,1879,480,1596,0]Ao[3260,13,1879,1879,5191,1596,0]Ao[3260,13,1879,1879,4072,1596,0]Ao[3260,13,1879,1879,4943,1596,0]Ao[3260,13,1959,1959,3412,1596,0]Ao[3260,13,1959,1959,4351,1596,0]Ao[3260,13,1959,1959,4801,1596,0]Ao[3260,13,1959,1959,5038,1596,0]Ao[3260,13,1959,1959,4803,1596,0]Al[3260,13,1959,0,1364,1596,0]Am[3260,13,3782,138,117,1596,0]An[3260,13,3782,138,3177,1596,0]Ao[3260,13,3782,1543,1351,1596,0]Ao[3260,13,3782,1543,4074,1596,0]Ao[3260,13,3782,2535,2026,1596,0]Ao[3260,13,3782,2535,4367,1596,0]Ao[3260,13,3782,3782,4867,1596,0]Ao[3260,13,4835,4835,1366,1596,0]Ao[3260,13,4835,4835,1369,1596,0]Ao[2170,13,2171,2171,3415,1596,0]An[2170,13,2171,399,4489,1596,0]Ao[2170,13,2171,2171,2461,1596,0]Ao[2170,13,2171,2171,2475,1596,0]Ao[2170,13,2171,2171,4222,1596,0]Ao[1068,13,1069,1069,3179,1596,0]Ao[1068,13,1069,1069,4976,1596,0]An[1068,13,1868,1868,131,1596,0]Ao[1068,13,1573,1573,1384,1596,0]Ao[1068,13,1573,1573,4097,1596,0]Ao[1068,13,1573,1573,4584,1596,0]Ao[1068,13,4308,4308,5176,1596,0]Ao[1068,13,4308,4308,5177,1596,0]An[1068,13,1964,1964,132,1596,0]Ag[445,14,446,0,2052,0,0]Al[3260,14,3261,1098,2053,0,0]Ak[1388,14,1389,1389,130,0,0]Ac[502,7,503,0,0,0,0]Aj[445,13,446,2486,2439,0,0]Ae[1068,2,1069,0,0,0,0]Ae[4897,7,4898,0,0,0,0]Aj[445,13,446,3786,3371,0,0]Ae[3260,2,3261,0,0,0,0]Al[3260,13,3261,2487,2440,0,0]Af[445,2,446,3261,0,0,0]Ag[445,13,446,0,4936,0,0]Ag[445,14,446,0,3377,0,0]Ag[445,14,446,0,4249,0,0]5Ai[3260,13,3261,0,4937,0,0]Al[3260,14,3261,1572,3378,0,0]Al[3260,14,3261,1876,4251,0,0]Ai[3260,14,3261,0,4488,0,0]Aj[445,13,446,0,1633,3183,0]Aj[445,13,446,0,1616,3183,0]Aj[445,13,446,0,3189,3183,0]Aj[445,13,446,0,4936,3183,0]Aj[445,13,446,0,2463,3183,0]Ao[3260,13,3261,1995,1634,3183,0]Ao[3260,13,3261,1557,1617,3183,0]Ao[3260,13,3261,1547,3190,3183,0]Ao[3260,13,3261,1547,3384,3183,0]Al[3260,13,3261,1941,4588,0,0]Al[3260,13,3261,0,4937,3183,0]Ao[3260,13,3261,3261,2464,3183,0]Al[3260,12,1874,1874,3183,0,0]Al[2170,14,2171,2171,2461,0,0]Aj[445,13,446,0,1633,3186,0]Aj[445,13,446,0,1616,3186,0]Aj[445,13,446,0,3189,3186,0]Aj[445,13,446,0,4936,3186,0]Aj[445,13,446,0,2463,3186,0]Ao[3260,13,3261,1995,1634,3186,0]Ao[3260,13,3261,1557,1617,3186,0]Ao[3260,13,3261,1547,3190,3186,0]Ao[3260,13,3261,1547,3384,3186,0]Ao[3260,13,3261,1547,2433,3186,0]Al[3260,13,3261,0,4937,3186,0]Ao[3260,13,3261,3261,2464,3186,0]Al[3260,12,1874,1874,3186,0,0]Aj[445,13,446,4174,3402,0,0]Al[3260,13,3261,2187,3403,0,0]Al[1068,13,1573,1573,4097,0,0]Al[3260,13,3261,2487,2440,0,0]Ag[445,14,1878,0,479,0,0]Ak[3260,14,1879,1879,480,0,0]Aj[445,13,446,2513,2443,0,0]3Ag[445,12,446,0,3413,0,0]Aj[445,13,446,0,4473,3413,0]Aj[445,13,446,0,4804,3413,0]Al[3260,12,3261,1583,3414,0,0]Ao[3260,13,3261,1583,4474,3414,0]Ao[3260,13,3261,4972,4805,3414,0]Al[1068,14,1069,1069,3179,0,0]Ag[445,14,446,0,1346,0,0]Al[3260,14,3261,3225,1347,0,0]Ah[445,13,3781,0,3373,0,0]Aj[445,13,3781,0,116,3373,0]Ak[445,13,3781,0,1350,3373,0]Ak[445,13,3781,0,2025,3373,0]Al[3260,13,3782,3782,3374,0,0]Am[3260,13,3782,138,117,3374,0]Ao[3260,13,3782,1543,1351,3374,0]Ao[3260,13,3782,2535,2026,3374,0]Ag[445,14,1878,0,479,0,0]Ak[3260,14,1879,1879,480,0,0]Ai[3260,14,4587,0,4809,0,0]Ag[445,14,446,0,4572,0,0]Ag[445,14,446,0,4771,0,0]Al[3260,14,3261,1547,4574,0,0]Al[3260,14,3261,1547,4773,0,0]Ai[3260,14,3261,0,4488,0,0]Ai[3260,14,1367,0,4452,0,0]Ai[3260,14,3203,0,3167,0,0]Aj[445,13,446,2486,2439,0,0]Aj[445,13,446,2513,2443,0,0]Al[3260,13,3261,2487,2440,0,0]Ah[445,14,1878,0,2020,0,0]Al[3260,14,3261,3261,3188,0,0]Al[3260,14,1879,1879,2021,0,0]Ac[445,4,446,0,0,0,0]Ag[445,2,1878,1879,0,0,0]Ae[3260,4,3261,0,0,0,0]Ae[3260,2,1879,0,0,0,0]Ac[502,7,503,0,0,0,0]:Ae[1068,2,1069,0,0,0,0]Ae[4897,7,4898,0,0,0,0]Am[445,13,446,3786,4878,1356,0]Am[445,13,446,4169,4483,1356,0]Am[445,13,446,4169,3209,1356,0]Am[445,13,446,4169,3398,1356,0]Am[445,13,446,4169,4482,1356,0]Am[445,13,446,4174,3416,1356,0]Am[445,13,446,4174,4255,1356,0]Aj[445,13,446,0,4575,1356,0]Ao[3260,13,3261,3787,4576,1356,0]Ag[445,14,446,0,2473,0,0]Al[3260,14,3261,3261,2474,0,0]Al[2170,14,2171,2171,2475,0,0]Ai[445,13,446,1067,485,0,0]Ai[3260,14,3203,0,4242,0,0]Ag[445,14,446,0,3377,0,0]Al[3260,14,3261,1572,3378,0,0]Al[2170,14,2171,2171,2461,0,0]Ag[445,14,446,0,3206,0,0]Ag[445,14,446,0,4579,0,0]Al[3260,14,3261,1572,3207,0,0]Al[3260,14,3261,1572,4580,0,0]3210Ah[445,12,1878,0,2016,0,0]Al[3260,12,1879,1879,2017,0,0]Ah[445,12,3781,0,3373,0,0]Aj[445,13,3781,0,116,3373,0]Ak[445,13,3781,0,1350,3373,0]Ak[445,13,3781,0,2025,3373,0]Al[3260,12,3782,3782,3374,0,0]Am[3260,13,3782,138,117,3374,0]Ao[3260,13,3782,1543,1351,3374,0]Ao[3260,13,3782,2535,2026,3374,0]Al[3260,15,3261,3234,4879,0,0]Al[3260,15,3261,1093,3210,0,0]Al[3260,15,3261,1098,3417,0,0]Ah[445,15,1958,0,1363,0,0]Ai[3260,15,1959,0,1364,0,0]Ah[445,10,1878,1879,0,0,0]Af[3260,10,1879,0,0,0,0]Al[3260,15,3261,1941,4588,0,0]Ag[445,5,1878,1879,0,0,0]Ae[3260,5,1879,0,0,0,0]Ag[445,15,446,0,3189,0,0]Al[3260,15,3261,1547,3190,0,0]Ag[445,5,3781,2535,0,0,0]Ah[3260,5,3782,2535,0,0,0]Al[1068,15,1069,1069,4976,0,0]Af[445,5,446,3786,0,0,0]5454Ag[445,10,446,2179,0,0,0];:Aj[445,15,446,4174,3416,0,0]Af[445,5,446,4174,0,0,0]Al[3260,15,3261,1098,3417,0,0]Al[3260,15,3261,1098,4077,0,0]Al[3260,15,3261,2187,4095,0,0]Ah[3260,5,3261,2187,0,0,0]Aj[445,15,446,4169,4483,0,0]Al[3260,15,3261,1547,3384,0,0]Af[445,5,446,1098,0,0,0]Ah[3260,5,3261,1098,0,0,0]Al[3260,15,3261,1941,2447,0,0]Ag[445,12,446,0,5089,0,0]Al[3260,12,3261,4387,5090,0,0]Ao[3260,13,3261,4387,5220,5090,0]Ao[3260,13,3261,4387,5225,5090,0]Ak[1388,14,1389,1389,130,0,0]An[1388,13,1389,1389,130,4451,0]An[2535,13,2536,2536,121,4451,0]Ak[2535,14,2536,2536,121,0,0]Ao[1068,13,1069,1069,3179,4451,0]An[1068,13,1868,1868,131,4451,0]An[1068,13,1964,1964,132,4451,0]Aj[445,13,446,0,1633,3368,0]Aj[445,13,446,0,1616,3368,0]Aj[445,13,446,0,4078,3368,0]Aj[445,13,446,0,4215,3368,0]Ai[445,13,446,0,492,3368,0]Ai[445,13,446,0,122,3368,0]Aj[445,13,446,0,2463,3368,0]Ao[3260,13,3261,1995,1634,3368,0]Ao[3260,13,3261,3234,4879,3368,0]Ao[3260,13,3261,1557,1617,3368,0]Ao[3260,13,3261,1093,3210,3368,0]Ao[3260,13,3261,1093,4374,3368,0]Ao[3260,13,3261,1098,3417,3368,0]Ao[3260,13,3261,3354,4079,3368,0]Ao[3260,13,3261,2487,4777,3368,0]Ao[3260,13,3261,1941,4875,3368,0]Ao[3260,13,3261,1969,4216,3368,0]An[3260,13,3261,3261,493,3368,0]An[3260,13,3261,3261,123,3368,0]Ao[3260,13,3261,3261,2464,3368,0]Aj[445,13,446,2486,2439,0,0]Al[3260,13,3261,2487,2440,0,0]Aj[445,13,446,2513,2443,0,0]Ah[445,12,1878,0,3199,0,0]Ah[445,12,1878,0,3192,0,0]Al[3260,13,3261,2487,2444,0,0]Al[3260,12,1879,1879,3200,0,0]Al[3260,12,1879,1879,3193,0,0]Ao[1388,13,1571,1571,4096,2011,0]Ao[1388,13,1571,1571,1383,2011,0]Al[502,13,503,503,4066,2011,0]Aj[445,13,446,0,4339,2011,0]Am[445,13,446,2486,4348,2011,0]Aj[445,13,446,0,1633,2011,0]Aj[445,13,446,0,1616,2011,0]Aj[445,13,446,0,2476,2011,0]Aj[445,13,446,0,3377,2011,0]Aj[445,13,446,0,3206,2011,0]Aj[445,13,446,0,4579,2011,0]Aj[445,13,446,0,2052,2011,0]Aj[445,13,446,0,4206,2011,0]Aj[445,13,446,0,4598,2011,0]Aj[445,13,446,0,4902,2011,0]Aj[445,13,446,0,4917,2011,0]Aj[445,13,446,0,4078,2011,0]Aj[445,13,446,0,4459,2011,0]Aj[445,13,446,0,5098,2011,0]Aj[445,13,446,0,4804,2011,0]Ak[445,13,3781,0,3176,2011,0]Ak[445,13,3781,0,4073,2011,0]Ak[445,13,3781,0,4366,2011,0]Ak[445,13,3781,0,4866,2011,0]Ao[3260,13,3261,4608,4340,2011,0]Ao[3260,13,3261,4387,5220,2011,0]Ao[3260,13,3261,1995,1634,2011,0]Ao[3260,13,3261,1557,1617,2011,0]Ao[3260,13,3261,1557,3392,2011,0]Ao[3260,13,3261,1557,3367,2011,0]Ao[3260,13,3261,1572,2477,2011,0]Ao[3260,13,3261,1572,3378,2011,0]Ao[3260,13,3261,1572,3207,2011,0]Ao[3260,13,3261,1572,4580,2011,0]Ao[3260,13,3261,1098,2053,2011,0]Ao[3260,13,3261,2187,4484,2011,0]Ao[3260,13,3261,2187,4210,2011,0]Ao[3260,13,3261,3354,4599,2011,0]Ao[3260,13,3261,3354,4903,2011,0]Ao[3260,13,3261,3354,4918,2011,0]Ao[3260,13,3261,3354,4079,2011,0]Ao[3260,13,3261,1876,4460,2011,0]Ao[3260,13,3261,1876,5099,2011,0]Ao[3260,13,3261,4387,5225,2011,0]Ao[3260,13,3261,4972,4805,2011,0]An[3260,13,3782,138,3177,2011,0]Ao[3260,13,3782,1543,4074,2011,0]Ao[3260,13,3782,2535,4367,2011,0]Ao[3260,13,3782,3782,4867,2011,0]Ao[2170,13,2171,2171,2461,2011,0]Ao[1068,13,1573,1573,1384,2011,0]Ao[1068,13,1573,1573,4584,2011,0]Al[3260,13,3261,1969,4347,0,0]Al[3260,13,3261,1941,4369,0,0]Al[3260,13,3261,1941,4588,0,0]Al[3260,14,3261,1941,5131,0,0]Ai[3260,14,4874,0,4938,0,0]Ah[3260,14,4587,0,481,0,0]Ai[3260,14,4587,0,1378,0,0]Ai[3260,14,4587,0,1380,0,0]Ai[3260,14,4587,0,4809,0,0]Ah[445,12,1878,0,3199,0,0]Ah[445,12,1878,0,3192,0,0]Al[3260,12,1879,1879,3200,0,0]Al[3260,12,1879,1879,3193,0,0]Aj[445,13,446,4169,3396,0,0]Aj[445,13,446,4169,4983,0,0]Ah[445,13,3781,0,3373,0,0]Aj[445,13,3781,0,116,3373,0]Ak[445,13,3781,0,1350,3373,0]Ak[445,13,3781,0,2025,3373,0]Al[3260,13,3782,3782,3374,0,0]Am[3260,13,3782,138,117,3374,0]Ao[3260,13,3782,1543,1351,3374,0]Ao[3260,13,3782,2535,2026,3374,0]Ag[445,13,446,0,4598,0,0]Al[3260,13,3261,3354,4599,0,0]Ab[2170,3,0,0,0,0,0]:965Aj[445,13,446,3786,3371,0,0]Aj[445,13,446,1067,2455,0,0]Al[3260,14,3261,1941,5131,0,0]Ai[3260,14,4874,0,4938,0,0]Ah[3260,14,4587,0,481,0,0]Ai[3260,14,4587,0,1378,0,0]Ai[3260,14,4587,0,4809,0,0]Aj[445,13,446,4174,3402,0,0]Al[3260,13,3261,2187,3403,0,0]Ak[1388,14,1389,1389,130,0,0]Al[1388,14,1571,1571,1383,0,0]Al[3260,13,3261,3787,3372,0,0]Ah[445,12,1878,0,3199,0,0]Al[3260,12,1879,1879,3200,0,0]Am[1388,13,1389,1389,130,114,0]An[1388,13,1389,1389,4065,114,0]An[1388,13,1571,1571,4096,114,0]An[1388,13,1571,1571,1383,114,0]An[1388,13,1571,1571,4485,114,0]Ak[502,13,503,503,4066,114,0]An[4102,13,4103,4103,5132,114,0]Al[445,13,446,3786,4603,114,0]Al[445,13,446,3786,2454,114,0]Ai[445,13,446,0,4339,114,0]Al[445,13,446,1067,2455,114,0]Al[445,13,446,1067,4769,114,0]Al[445,13,446,3786,3369,114,0]Al[445,13,446,3786,3371,114,0]Al[445,13,446,3786,2028,114,0]Al[445,13,446,3786,1609,114,0]Al[445,13,446,3786,3169,114,0]Al[445,13,446,3786,2427,114,0]Al[445,13,446,3786,4241,114,0]Al[445,13,446,3786,4246,114,0]Al[445,13,446,3786,5044,114,0]Al[445,13,446,3786,4878,114,0]Al[445,13,446,2486,2439,114,0]Al[445,13,446,2486,4348,114,0]Al[445,13,446,2486,1590,114,0]Al[445,13,446,2513,2443,114,0]Al[445,13,446,2513,3399,114,0]Al[445,13,446,2513,4464,114,0]Ak[445,13,446,1067,485,114,0]Al[445,13,446,1067,4490,114,0]Al[445,13,446,4169,3396,114,0]Al[445,13,446,4169,4983,114,0]Al[445,13,446,4169,4483,114,0]Al[445,13,446,4169,3209,114,0]Al[445,13,446,4169,3398,114,0]Al[445,13,446,4169,4482,114,0]Al[445,13,446,1584,4256,114,0]Al[445,13,446,1110,4091,114,0]Al[445,13,446,4174,3402,114,0]Al[445,13,446,4174,3416,114,0]Al[445,13,446,4174,4255,114,0]Al[445,13,446,4174,2042,114,0]Ai[445,13,446,0,3393,114,0]Ai[445,13,446,0,4378,114,0]Ai[445,13,446,0,4595,114,0]Ai[445,13,446,0,4233,114,0]Ai[445,13,446,0,4473,114,0]Ai[445,13,446,0,1633,114,0]Ai[445,13,446,0,5041,114,0]Ai[445,13,446,0,1346,114,0]Ai[445,13,446,0,1616,114,0]Ai[445,13,446,0,2476,114,0]Ai[445,13,446,0,3377,114,0]Ai[445,13,446,0,3206,114,0]Ai[445,13,446,0,4579,114,0]Ai[445,13,446,0,2052,114,0]Ai[445,13,446,0,4206,114,0]Ai[445,13,446,0,4575,114,0]Ai[445,13,446,0,4589,114,0]Ai[445,13,446,0,4598,114,0]Ai[445,13,446,0,4902,114,0]Ai[445,13,446,0,4917,114,0]Ai[445,13,446,0,4078,114,0]Ai[445,13,446,0,4459,114,0]Ai[445,13,446,0,4249,114,0]Ai[445,13,446,0,5098,114,0]Ai[445,13,446,0,4200,114,0]Ai[445,13,446,0,4068,114,0]Ai[445,13,446,0,3189,114,0]Ai[445,13,446,0,4572,114,0]Ai[445,13,446,0,4771,114,0]Ai[445,13,446,0,4212,114,0]Ai[445,13,446,0,4215,114,0]Ai[445,13,446,0,4220,114,0]Ai[445,13,446,0,4936,114,0]Ai[445,13,446,0,3380,114,0]Ai[445,13,446,0,4804,114,0]Ah[445,13,446,0,492,114,0]Ah[445,13,446,0,122,114,0]Ai[445,13,446,0,2463,114,0]Ai[445,13,446,0,2473,114,0]Ai[445,13,3781,0,116,114,0]Aj[445,13,3781,0,3176,114,0]Aj[445,13,3781,0,1350,114,0]Aj[445,13,3781,0,4073,114,0]Aj[445,13,3781,0,2025,114,0]Aj[445,13,3781,0,4366,114,0]Aj[445,13,3781,0,4866,114,0]Aj[445,13,1878,0,2020,114,0]Ai[445,13,1878,0,479,114,0]Aj[445,13,1878,0,5190,114,0]Aj[445,13,1878,0,4071,114,0]Aj[445,13,1878,0,4940,114,0]Aj[445,13,1958,0,3411,114,0]Aj[445,13,1958,0,4350,114,0]Aj[445,13,1958,0,4800,114,0]Aj[445,13,1958,0,5037,114,0]Aj[445,13,1958,0,4802,114,0]Aj[445,13,1958,0,1363,114,0]Am[2535,13,2536,2536,121,114,0]An[2535,13,2536,2536,2430,114,0]An[3260,13,3261,4608,4340,114,0]An[3260,13,3261,3234,3182,114,0]An[3260,13,3261,1093,1598,114,0]An[3260,13,3261,2187,2046,114,0]An[3260,13,3261,2187,3397,114,0]An[3260,13,3261,2187,3403,114,0]An[3260,13,3261,3787,3372,114,0]An[3260,13,3261,1089,3394,114,0]An[3260,13,3261,1876,4379,114,0]An[3260,13,3261,2487,2440,114,0]An[3260,13,3261,2487,4346,114,0]An[3260,13,3261,2487,4780,114,0]An[3260,13,3261,2487,4357,114,0]An[3260,13,3261,1941,5250,114,0]An[3260,13,3261,4387,5220,114,0]An[3260,13,3261,1583,4596,114,0]An[3260,13,3261,1583,4234,114,0]An[3260,13,3261,1583,4474,114,0]An[3260,13,3261,1995,1634,114,0]An[3260,13,3261,1995,5042,114,0]An[3260,13,3261,3225,1347,114,0]An[3260,13,3261,3234,4879,114,0]An[3260,13,3261,1557,1617,114,0]An[3260,13,3261,1557,4245,114,0]An[3260,13,3261,1557,3392,114,0]An[3260,13,3261,1557,3205,114,0]An[3260,13,3261,1557,4877,114,0]An[3260,13,3261,1557,4933,114,0]An[3260,13,3261,1557,3367,114,0]An[3260,13,3261,1572,2477,114,0]An[3260,13,3261,1572,3378,114,0]An[3260,13,3261,1572,3207,114,0]An[3260,13,3261,1572,4580,114,0]An[3260,13,3261,1093,3210,114,0]An[3260,13,3261,1093,4374,114,0]An[3260,13,3261,1098,2053,114,0]An[3260,13,3261,1098,3417,114,0]An[3260,13,3261,1098,4077,114,0]An[3260,13,3261,2187,4095,114,0]An[3260,13,3261,2187,4484,114,0]An[3260,13,3261,2187,4210,114,0]An[3260,13,3261,3787,4576,114,0]An[3260,13,3261,3787,3202,114,0]An[3260,13,3261,3787,4450,114,0]An[3260,13,3261,3787,5127,114,0]An[3260,13,3261,3787,1368,114,0]An[3260,13,3261,1089,4590,114,0]An[3260,13,3261,3354,4599,114,0]An[3260,13,3261,3354,4903,114,0]An[3260,13,3261,3354,4918,114,0]An[3260,13,3261,3354,4079,114,0]An[3260,13,3261,1876,4460,114,0]An[3260,13,3261,1876,4251,114,0]An[3260,13,3261,1876,5099,114,0]An[3260,13,3261,1876,4202,114,0]An[3260,13,3261,1876,4069,114,0]An[3260,13,3261,1547,3190,114,0]An[3260,13,3261,1547,4465,114,0]An[3260,13,3261,1547,3384,114,0]An[3260,13,3261,1547,3406,114,0]An[3260,13,3261,1547,4217,114,0]An[3260,13,3261,1547,4574,114,0]An[3260,13,3261,1547,4773,114,0]An[3260,13,3261,1547,2433,114,0]An[3260,13,3261,2487,4777,114,0]An[3260,13,3261,2487,2444,114,0]An[3260,13,3261,2487,4213,114,0]An[3260,13,3261,2487,4063,114,0]An[3260,13,3261,2487,3400,114,0]An[3260,13,3261,1941,4369,114,0]An[3260,13,3261,1941,4588,114,0]An[3260,13,3261,1941,5131,114,0]An[3260,13,3261,1941,2447,114,0]An[3260,13,3261,1941,4875,114,0]An[3260,13,3261,1969,4578,114,0]An[3260,13,3261,1969,4347,114,0]An[3260,13,3261,1969,4216,114,0]An[3260,13,3261,1969,4221,114,0]Ak[3260,13,3261,0,4488,114,0]Ak[3260,13,3261,0,4937,114,0]An[3260,13,3261,4387,3381,114,0]An[3260,13,3261,4387,5225,114,0]An[3260,13,3261,4972,4805,114,0]Am[3260,13,3261,3261,493,114,0]Am[3260,13,3261,3261,123,114,0]An[3260,13,3261,3261,2464,114,0]An[3260,13,3261,3261,2474,114,0]An[3260,13,3261,3261,3188,114,0]An[3260,13,1874,1874,4380,114,0]An[3260,13,5083,5083,4982,114,0]An[3260,13,5083,5083,1365,114,0]An[3260,13,1879,1879,2021,114,0]Am[3260,13,1879,1879,480,114,0]An[3260,13,1879,1879,5191,114,0]An[3260,13,1879,1879,4072,114,0]An[3260,13,1879,1879,4943,114,0]An[3260,13,1959,1959,3412,114,0]An[3260,13,1959,1959,4351,114,0]An[3260,13,1959,1959,4801,114,0]An[3260,13,1959,1959,5038,114,0]An[3260,13,1959,1959,4803,114,0]Ak[3260,13,1959,0,1364,114,0]Al[3260,13,3782,138,117,114,0]Am[3260,13,3782,138,3177,114,0]An[3260,13,3782,1543,1351,114,0]An[3260,13,3782,1543,4074,114,0]An[3260,13,3782,2535,2026,114,0]An[3260,13,3782,2535,4367,114,0]An[3260,13,3782,3782,4867,114,0]An[3260,13,4835,4835,1366,114,0]An[3260,13,4835,4835,1369,114,0]An[2170,13,2171,2171,3415,114,0]Am[2170,13,2171,399,4489,114,0]An[2170,13,2171,2171,2461,114,0]An[2170,13,2171,2171,2475,114,0]An[2170,13,2171,2171,4222,114,0]An[1068,13,1069,1069,3179,114,0]An[1068,13,1069,1069,4976,114,0]Am[1068,13,1868,1868,131,114,0]An[1068,13,1573,1573,1384,114,0]An[1068,13,1573,1573,4097,114,0]An[1068,13,1573,1573,4584,114,0]An[1068,13,4308,4308,5176,114,0]An[1068,13,4308,4308,5177,114,0]Am[1068,13,1964,1964,132,114,0]Aj[445,13,446,3786,5044,0,0]Aj[445,13,446,1067,4490,0,0]Ag[445,13,446,0,4200,0,0]Al[3260,13,3261,1876,4202,0,0]Al[3260,14,3261,1941,5131,0,0]Ai[3260,14,4874,0,4938,0,0]Ah[3260,14,4587,0,481,0,0]Ai[3260,14,4587,0,1378,0,0]Af[445,5,446,3786,0,0,0]Al[1388,15,1389,1389,4065,0,0]Ae[2535,6,2536,0,0,0,0]Ag[445,15,446,0,3189,0,0]Al[3260,15,3261,1547,3190,0,0]Ah[3260,5,3261,1547,0,0,0]Aj[445,15,446,3786,4878,0,0]Al[3260,15,3261,3234,4879,0,0]Af[445,5,446,2486,0,0,0]Ah[3260,5,3261,2487,0,0,0]Al[3260,15,3261,2187,4095,0,0]Af[445,5,446,2513,0,0,0]2Ah[3260,6,3261,1941,0,0,0]Ag[445,19,446,0,1633,0,0]Al[3260,19,3261,1995,1634,0,0]Ag[445,19,446,0,4936,0,0]Ai[3260,19,3261,0,4937,0,0]=Ah[445,12,1878,0,4250,0,0]>Ak[445,13,1958,0,4802,4250,0]>Al[3260,12,1879,1879,4252,0,0]>Ao[3260,13,1959,1959,4803,4252,0]Ao[1068,13,4308,4308,5177,4252,0]An[2535,13,2536,2536,121,4080,0]Ao[2535,13,2536,2536,2430,4080,0]Ag[445,14,446,0,3206,0,0]Al[3260,14,3261,1572,3207,0,0]Ag[445,13,446,0,4249,0,0]Al[3260,13,3261,1876,4251,0,0]Aj[445,13,446,2179,4447,0,0]0>=Al[1068,13,1573,1573,4584,0,0]Ae[1068,2,1069,0,0,0,0]Aj[445,13,446,2513,2443,0,0]Al[3260,13,3261,2487,2444,0,0]Ag[445,14,446,0,3377,0,0]Al[3260,14,3261,1572,3378,0,0]Al[3260,13,3261,3787,3372,0,0]Ag[445,13,446,0,4598,0,0]Al[3260,13,3261,3354,4599,0,0]43Ag[445,14,446,0,2473,0,0]Al[3260,14,3261,3261,2474,0,0]Al[2170,14,2171,2171,2475,0,0]Al[3260,13,3261,2187,2046,0,0]Al[3260,13,3261,4387,5220,0,0]Al[3260,13,3261,4387,5225,0,0]Aj[445,13,446,4174,3402,0,0]Al[3260,13,3261,2187,3403,0,0]<;Al[1388,14,1571,1571,1383,0,0]Ai[502,14,503,503,4066,0,0]Ah[445,13,1958,0,4800,0,0]Ah[445,13,1958,0,4802,0,0]Al[3260,13,1959,1959,4801,0,0]Al[3260,13,1959,1959,4803,0,0]76Ah[445,15,1958,0,1363,0,0]Ai[3260,15,1959,0,1364,0,0]Af[445,5,446,4608,0,0,0]Ah[3260,5,3261,4608,0,0,0]Ag[445,15,446,0,4575,0,0]Ag[445,15,446,0,4212,0,0]Al[3260,15,3261,3787,4576,0,0]Al[3260,15,3261,2487,4213,0,0]Ah[3260,6,3261,2487,0,0,0]Ah[3260,5,3261,1969,0,0,0]Af[445,5,446,2486,0,0,0]Aj[445,15,446,4169,4482,0,0]Ag[445,5,1958,1959,0,0,0]Ae[3260,5,1959,0,0,0,0]Ag[445,15,446,0,3189,0,0]Al[3260,15,3261,1547,3190,0,0]Ag[445,19,446,0,4936,0,0]Ai[3260,19,3261,0,4937,0,0]Ah[3260,5,3261,2487,0,0,0]Al[3260,15,3261,1941,4875,0,0]?=5454Al[3260,15,3261,2487,4346,0,0]Ag[445,5,3781,2535,0,0,0]Ah[3260,5,3782,2535,0,0,0]Ah[3260,5,3261,1941,0,0,0]98Ah[3260,6,3261,1093,0,0,0]:9Af[445,5,446,1876,0,0,0]Ah[3260,5,3261,1876,0,0,0]Ae[3260,5,1874,0,0,0,0]Al[3260,13,3261,3787,3372,0,0]Ag[445,14,446,0,3377,0,0]Al[3260,14,3261,1572,3378,0,0]Al[1068,14,1069,1069,3179,0,0]Ae[3260,2,3261,0,0,0,0]Ag[445,13,446,0,4459,0,0]Al[3260,13,3261,1876,4460,0,0]Aj[445,12,446,2179,4447,0,0]Ab[4390,3,0,0,0,0,0]Ak[1388,14,1389,1389,130,0,0]Al[1388,14,1571,1571,4096,0,0]98Aj[445,13,446,2513,3399,0,0]Al[3260,13,3261,2487,3400,0,0]Ag[445,13,446,0,4902,0,0]Al[3260,13,3261,3354,4903,0,0];=Al[1388,14,1571,1571,1383,0,0]Ao[1068,13,1069,1069,4976,3214,0]Ag[445,15,446,0,3189,0,0]Af[445,5,446,1547,0,0,0]Al[3260,15,3261,1547,3190,0,0]Ah[3260,5,3261,1547,0,0,0]Af[445,6,446,3787,0,0,0]Ah[3260,6,3261,3787,0,0,0]Al[3260,15,3261,2487,4346,0,0]Ah[3260,5,3261,1969,0,0,0]Af[445,5,446,1572,0,0,0]Ah[3260,5,3261,1572,0,0,0]97Ae[1068,5,1573,0,0,0,0]:8Ah[3260,6,3261,1941,0,0,0]Af[445,5,446,1089,0,0,0]Ah[3260,5,3261,1089,0,0,0]Al[3260,15,3261,1557,4877,0,0]Af[445,5,446,1583,0,0,0]Ah[3260,5,3261,1583,0,0,0]Af[445,5,446,3354,0,0,0]Ah[3260,5,3261,3354,0,0,0]Ag[445,15,446,0,4575,0,0]Al[3260,15,3261,3787,4576,0,0]Af[445,5,446,3786,0,0,0]Aj[445,15,446,4169,3209,0,0]Aj[445,15,446,4169,3398,0,0]Aj[445,15,446,4174,3416,0,0]Aj[445,15,446,4174,4255,0,0]An[1388,13,1389,1389,130,4345,0]Ao[1388,13,1389,1389,4065,4345,0]Am[445,13,446,3786,3371,4345,0]Am[445,13,446,3786,5044,4345,0]Am[445,13,446,3786,4878,4345,0]Am[445,13,446,2486,2439,4345,0]Am[445,13,446,2486,4348,4345,0]Al[445,13,446,1067,485,4345,0]Am[445,13,446,1067,4490,4345,0]Am[445,13,446,4169,4483,4345,0]Am[445,13,446,4169,3209,4345,0]Am[445,13,446,4169,3398,4345,0]Am[445,13,446,4169,4482,4345,0]Am[445,13,446,4174,3416,4345,0]Am[445,13,446,4174,4255,4345,0]Aj[445,13,446,0,1633,4345,0]Aj[445,13,446,0,5041,4345,0]Aj[445,13,446,0,1616,4345,0]Aj[445,13,446,0,2476,4345,0]Aj[445,13,446,0,3377,4345,0]Aj[445,13,446,0,3206,4345,0]Aj[445,13,446,0,4579,4345,0]Aj[445,13,446,0,2052,4345,0]Aj[445,13,446,0,4206,4345,0]Aj[445,13,446,0,4575,4345,0]Aj[445,13,446,0,4598,4345,0]Aj[445,13,446,0,4902,4345,0]Aj[445,13,446,0,4917,4345,0]Aj[445,13,446,0,4078,4345,0]Aj[445,13,446,0,4459,4345,0]Aj[445,13,446,0,4249,4345,0]Aj[445,13,446,0,5098,4345,0]Aj[445,13,446,0,4200,4345,0]Aj[445,13,446,0,4068,4345,0]Aj[445,13,446,0,3189,4345,0]Aj[445,13,446,0,4572,4345,0]Aj[445,13,446,0,4771,4345,0]Aj[445,13,446,0,4212,4345,0]Aj[445,13,446,0,4215,4345,0]Aj[445,13,446,0,4220,4345,0]Aj[445,13,446,0,4936,4345,0]Aj[445,13,446,0,3380,4345,0]Ai[445,13,446,0,492,4345,0]Ai[445,13,446,0,122,4345,0]Aj[445,13,446,0,2463,4345,0]Aj[445,13,3781,0,116,4345,0]Ak[445,13,3781,0,3176,4345,0]Ak[445,13,3781,0,1350,4345,0]Ak[445,13,3781,0,4073,4345,0]Ak[445,13,3781,0,2025,4345,0]Ak[445,13,3781,0,4366,4345,0]Ak[445,13,1958,0,4800,4345,0]Ak[445,13,1958,0,5037,4345,0]Ak[445,13,1958,0,4802,4345,0]Ak[445,13,1958,0,1363,4345,0]Ao[3260,13,3261,1995,1634,4345,0]Ao[3260,13,3261,1995,5042,4345,0]Ao[3260,13,3261,3234,4879,4345,0]Ao[3260,13,3261,1557,1617,4345,0]Ao[3260,13,3261,1557,4245,4345,0]Ao[3260,13,3261,1557,3392,4345,0]Ao[3260,13,3261,1557,3205,4345,0]Ao[3260,13,3261,1557,4877,4345,0]Ao[3260,13,3261,1557,4933,4345,0]Ao[3260,13,3261,1557,3367,4345,0]Ao[3260,13,3261,1572,2477,4345,0]Ao[3260,13,3261,1572,3378,4345,0]Ao[3260,13,3261,1572,3207,4345,0]Ao[3260,13,3261,1572,4580,4345,0]Ao[3260,13,3261,1093,3210,4345,0]Ao[3260,13,3261,1093,4374,4345,0]Ao[3260,13,3261,1098,2053,4345,0]Ao[3260,13,3261,1098,3417,4345,0]Ao[3260,13,3261,1098,4077,4345,0]Ao[3260,13,3261,2187,4484,4345,0]Ao[3260,13,3261,2187,4210,4345,0]Ao[3260,13,3261,3787,4576,4345,0]Ao[3260,13,3261,3787,3202,4345,0]Ao[3260,13,3261,3787,4450,4345,0]Ao[3260,13,3261,3787,5127,4345,0]Ao[3260,13,3261,3354,4599,4345,0]Ao[3260,13,3261,3354,4903,4345,0]Ao[3260,13,3261,3354,4918,4345,0]Ao[3260,13,3261,3354,4079,4345,0]Ao[3260,13,3261,1876,4460,4345,0]Ao[3260,13,3261,1876,4251,4345,0]Ao[3260,13,3261,1876,5099,4345,0]Ao[3260,13,3261,1876,4202,4345,0]Ao[3260,13,3261,1876,4069,4345,0]Ao[3260,13,3261,1547,3190,4345,0]Ao[3260,13,3261,1547,3384,4345,0]Ao[3260,13,3261,1547,3406,4345,0]Ao[3260,13,3261,1547,4217,4345,0]Ao[3260,13,3261,1547,4574,4345,0]Ao[3260,13,3261,1547,4773,4345,0]Ao[3260,13,3261,1547,2433,4345,0]Ao[3260,13,3261,2487,4777,4345,0]Ao[3260,13,3261,2487,4213,4345,0]Ao[3260,13,3261,1941,4588,4345,0]Ao[3260,13,3261,1941,5131,4345,0]Ao[3260,13,3261,1941,2447,4345,0]Ao[3260,13,3261,1941,4875,4345,0]Ao[3260,13,3261,1969,4216,4345,0]Ao[3260,13,3261,1969,4221,4345,0]Al[3260,13,3261,0,4488,4345,0]Al[3260,13,3261,0,4937,4345,0]Ao[3260,13,3261,4387,3381,4345,0]Ao[3260,13,3261,4387,5225,4345,0]An[3260,13,3261,3261,493,4345,0]An[3260,13,3261,3261,123,4345,0]Ao[3260,13,3261,3261,2464,4345,0]Ao[3260,13,3261,3261,3188,4345,0]Ao[3260,13,1874,1874,4380,4345,0]Ao[3260,13,5083,5083,4982,4345,0]Ao[3260,13,5083,5083,1365,4345,0]Ao[3260,13,1959,1959,4801,4345,0]Ao[3260,13,1959,1959,5038,4345,0]Ao[3260,13,1959,1959,4803,4345,0]Al[3260,13,1959,0,1364,4345,0]Am[3260,13,3782,138,117,4345,0]An[3260,13,3782,138,3177,4345,0]Ao[3260,13,3782,1543,1351,4345,0]Ao[3260,13,3782,1543,4074,4345,0]Ao[3260,13,3782,2535,2026,4345,0]Ao[3260,13,3782,2535,4367,4345,0]Ao[3260,13,4835,4835,1369,4345,0]Ao[2170,13,2171,2171,2461,4345,0]Ao[2170,13,2171,2171,2475,4345,0]Ao[2170,13,2171,2171,4222,4345,0]Ao[1068,13,1069,1069,4976,4345,0]Ao[1068,13,1573,1573,4584,4345,0]Ao[1068,13,4308,4308,5176,4345,0]Ao[1068,13,4308,4308,5177,4345,0]Af[445,7,446,2179,0,0,0]Ag[445,13,446,0,4459,0,0]Al[3260,13,3261,1876,4460,0,0]Ah[445,12,1878,0,3199,0,0]Al[3260,12,1879,1879,3200,0,0]Al[2170,14,2171,2171,2461,0,0]Ag[445,14,446,0,3377,0,0]Al[3260,14,3261,1572,3378,0,0]10Ae[1068,7,1069,0,0,0,0]Aj[445,13,446,2486,2439,0,0]Ae[4390,7,4391,0,0,0,0]Ae[1388,7,1389,0,0,0,0]Al[3260,13,3261,3787,3372,0,0]Aj[445,13,446,3786,3371,0,0]1Al[3260,13,3261,2187,4484,0,0]Al[1388,13,1571,1571,1383,0,0]Al[1388,14,1571,1571,4096,0,0]Aj[445,12,446,2179,2037,0,0]Am[445,13,446,1584,4256,2037,0]Am[445,13,446,1110,4091,2037,0]Ah[445,12,1878,0,2038,0,0]Ak[445,13,1958,0,3411,2038,0]Al[3260,12,1879,1879,2039,0,0]Ao[3260,13,1959,1959,3412,2039,0]Af[445,5,446,1067,0,0,0]Ag[445,15,446,0,3189,0,0]Af[445,5,446,1547,0,0,0]Al[3260,15,3261,1547,3190,0,0]Ah[3260,5,3261,1547,0,0,0]Ag[445,15,446,0,4575,0,0]Al[3260,15,3261,3787,4576,0,0]Ah[3260,5,3261,2487,0,0,0]Al[3260,15,3261,3787,1368,0,0]1Ag[445,15,446,0,4212,0,0]Al[3260,15,3261,2487,4213,0,0]Ag[445,19,446,0,4936,0,0]Ai[3260,19,3261,0,4937,0,0]Ah[445,15,1958,0,1363,0,0]Ai[3260,15,1959,0,1364,0,0]32Al[3260,15,3261,3787,3202,0,0]><><><21Ag[445,5,1958,1959,0,0,0]Ae[3260,5,1959,0,0,0,0]10Af[445,5,446,4972,0,0,0]Ah[3260,5,3261,4972,0,0,0]Al[3260,15,4835,4835,1366,0,0]Al[3260,15,3261,1557,4877,0,0]6Al[3260,15,3261,1941,4588,0,0]Ag[445,12,446,0,2469,0,0]Aj[445,13,446,0,4339,2469,0]Aj[445,13,446,0,4233,2469,0]Al[3260,12,3261,1583,2470,0,0]Ao[3260,13,3261,4608,4340,2470,0]Ao[3260,13,3261,1583,4234,2470,0]Ae[4102,7,4103,0,0,0,0]Aj[445,13,446,3786,3371,0,0]Al[3260,13,3261,3787,3372,0,0]Ag[445,14,446,0,3377,0,0]Al[3260,14,3261,1572,3378,0,0]32An[1388,13,1389,1389,130,4461,0]An[2535,13,2536,2536,121,4461,0]Ao[2535,13,2536,2536,2430,4461,0]Ao[1068,13,1069,1069,3179,4461,0]An[1068,13,1868,1868,131,4461,0]An[1068,13,1964,1964,132,4461,0]Ak[2170,13,2171,399,4489,0,0]Ag[445,13,446,0,4598,0,0]Al[3260,13,3261,3354,4599,0,0]:9Ae[3260,2,3261,0,0,0,0]Aj[445,13,446,2486,2439,0,0]Al[3260,13,3261,2487,2440,0,0]?>43Ae[4390,7,4391,0,0,0,0]6Al[1068,14,1069,1069,3179,0,0]70Ae[1388,7,1389,0,0,0,0]Ag[445,13,446,0,4249,0,0]Ag[445,13,446,0,4200,0,0]Al[3260,13,3261,1876,4251,0,0]Al[3260,13,3261,1876,4202,0,0]Ag[445,14,446,0,4249,0,0]Al[3260,14,3261,1876,4251,0,0]53Aj[445,13,446,4174,3402,0,0]Ag[445,19,446,0,4936,0,0]Ai[3260,19,3261,0,4937,0,0]Ag[445,15,446,0,4575,0,0]Al[3260,15,3261,1557,4877,0,0]Al[3260,15,3261,3787,4576,0,0]Ah[445,10,3781,3782,0,0,0]Af[3260,10,3782,0,0,0,0]Ag[445,5,3781,3782,0,0,0]Ae[3260,5,3782,0,0,0,0]Ah[445,10,1878,1879,0,0,0]Af[3260,10,1879,0,0,0,0]Ag[445,15,446,0,4212,0,0]Al[3260,15,3261,2487,4213,0,0]Ag[445,10,446,3787,0,0,0]Ai[3260,10,3261,3787,0,0,0]Ah[3260,6,3261,1941,0,0,0]Ah[3260,6,3261,1557,0,0,0]Af[445,6,446,3786,0,0,0]Ah[3260,6,3261,3234,0,0,0]Ag[445,15,446,0,3189,0,0]Al[3260,15,3261,1547,3190,0,0]10Aj[445,15,446,3786,4878,0,0]Al[3260,15,3261,3234,4879,0,0]Ag[445,14,446,0,3206,0,0]Al[3260,14,3261,1572,3207,0,0]Ah[445,14,3781,0,4866,0,0]Al[3260,14,3782,3782,4867,0,0]Aj[445,13,446,3786,3371,0,0]Ag[445,14,446,0,3377,0,0]Al[3260,14,3261,1572,3378,0,0]10Al[3260,13,3261,3234,3182,0,0]Aj[445,13,446,2486,2439,0,0]Al[3260,13,3261,2487,2440,0,0]Ab[4897,3,0,0,0,0,0]Ao[1068,13,1069,1069,4976,3214,0]Ag[445,19,446,0,4936,0,0]Ai[3260,19,3261,0,4937,0,0]Af[445,5,446,3354,0,0,0]Ah[3260,5,3261,3354,0,0,0]Ah[445,15,1958,0,1363,0,0]Ai[3260,15,1959,0,1364,0,0]54Ai[3260,10,3261,1941,0,0,0]Al[3260,15,3261,3787,1368,0,0]7600Al[3260,15,3261,1093,4374,0,0]4365Al[3260,15,4835,4835,1366,0,0]Ae[1068,9,1069,0,0,0,0]:9Ag[445,15,446,0,3189,0,0]Al[3260,15,3261,1547,3190,0,0]<;<;Ah[3260,6,3261,1557,0,0,0]21Ac[445,5,446,0,0,0,0]Ae[3260,5,3261,0,0,0,0]Ag[445,15,446,0,4212,0,0]Ag[445,5,1878,1879,0,0,0]Al[3260,15,3261,2487,4213,0,0]Al[3260,15,3261,1941,4875,0,0]Ae[3260,5,1879,0,0,0,0]An[1388,13,1389,1389,130,4451,0]An[2535,13,2536,2536,121,4451,0]Ao[1068,13,1069,1069,3179,4451,0]An[1068,13,1868,1868,131,4451,0]An[1068,13,1964,1964,132,4451,0]Ag[445,13,446,0,4598,0,0]Al[3260,13,3261,3354,4599,0,0]Ag[445,13,446,0,1633,0,0]Al[3260,13,3261,1995,1634,0,0]Aj[445,14,446,2486,4348,0,0]Aj[445,13,446,3786,5044,0,0]Ag[445,13,446,0,4459,0,0]Al[3260,13,3261,1876,4460,0,0]Ag[445,13,446,0,4339,0,0]Al[3260,13,3261,4608,4340,0,0]Ag[445,14,446,0,3377,0,0]Al[3260,14,3261,1572,3378,0,0]7Al[3260,13,3261,3787,3372,0,0]Aj[445,13,446,3786,3371,0,0]Ah[445,13,1958,0,4800,0,0]Ah[445,13,1958,0,4802,0,0]Al[3260,13,1959,1959,4801,0,0]Al[3260,13,1959,1959,4803,0,0]45;:Ao[4102,13,4103,4103,5132,4931,0]Ag[445,13,446,0,4589,0,0]Al[3260,13,3261,1089,4590,0,0]Ae[1068,6,1069,0,0,0,0]Ag[445,15,446,0,3189,0,0]Al[3260,15,3261,1547,3190,0,0]10Al[3260,15,3261,3787,3202,0,0]Ae[3260,5,5083,0,0,0,0]Af[445,5,446,4169,0,0,0]Ah[445,15,1958,0,1363,0,0]Ai[3260,15,1959,0,1364,0,0]Al[3260,15,4835,4835,1366,0,0]Aj[445,14,446,2486,4348,0,0]Ai[445,13,446,0,492,3170,0]Ai[445,13,446,0,122,3170,0]An[3260,13,3261,3261,493,3170,0]An[3260,13,3261,3261,123,3170,0]Ao[2535,13,2536,2536,2430,3407,0]Ag[445,13,446,0,4917,0,0]Al[3260,13,3261,3354,4918,0,0]Ah[445,12,1878,0,2038,0,0]Ak[445,13,1958,0,3411,2038,0]Al[3260,12,1879,1879,2039,0,0]Ao[3260,13,1959,1959,3412,2039,0]Aj[445,13,446,3786,3371,0,0]Ag[445,12,446,0,4872,0,0]Al[3260,12,3261,3787,4873,0,0]Ag[445,13,446,0,4598,0,0]Al[3260,13,3261,3354,4599,0,0]Ag[445,14,446,0,3206,0,0]Al[3260,14,3261,1572,3207,0,0]Ah[445,12,3781,0,3373,0,0]Aj[445,13,3781,0,116,3373,0]Ak[445,13,3781,0,1350,3373,0]Ak[445,13,3781,0,2025,3373,0]Al[3260,12,3782,3782,3374,0,0]Am[3260,13,3782,138,117,3374,0]Ao[3260,13,3782,1543,1351,3374,0]Ao[3260,13,3782,2535,2026,3374,0]Aj[445,14,446,2486,4348,0,0]Al[3260,13,3261,2187,2046,0,0]Ah[3260,2,4385,4173,0,0,0]Ae[1388,7,1389,0,0,0,0]==Ai[445,13,446,1067,485,0,0]Ak[2170,13,2171,399,4489,0,0]032Ah[445,12,1878,0,2038,0,0]Ak[445,13,1958,0,3411,2038,0]Ae[3260,2,3261,0,0,0,0]Al[3260,12,1879,1879,2039,0,0]Ao[3260,13,1959,1959,3412,2039,0]Ag[445,10,446,4387,0,0,0]Ai[3260,10,3261,4387,0,0,0]Ah[445,15,1958,0,1363,0,0]Ai[3260,15,1959,0,1364,0,0]Ag[445,15,446,0,4212,0,0]Al[3260,15,3261,2487,4213,0,0]Ag[445,19,446,0,4936,0,0]Ai[3260,19,3261,0,4937,0,0]Af[445,5,446,1876,0,0,0]Ah[3260,5,3261,1876,0,0,0]Ah[445,12,1878,0,3199,0,0]Al[3260,12,1879,1879,3200,0,0]Aj[445,13,446,2486,2439,0,0]Al[3260,13,3261,3787,3372,0,0]Al[3260,13,3261,2487,2440,0,0]Al[3260,13,3261,3261,3188,0,0]Ah[445,13,3781,0,4366,0,0]Al[3260,13,3782,2535,4367,0,0]Aj[445,13,446,2179,3212,0,0]0Aj[445,13,446,3786,3371,0,0]6Ag[445,14,446,0,3377,0,0]Al[3260,14,3261,1572,3378,0,0]Ag[445,13,446,0,4598,0,0]Al[3260,13,3261,3354,4599,0,0]Al[1068,14,1069,1069,3179,0,0]Ag[445,19,446,0,4936,0,0]Ai[3260,19,3261,0,4937,0,0]Al[3260,15,4835,4835,1366,0,0]Ah[445,15,1958,0,1363,0,0]Ai[3260,15,1959,0,1364,0,0]10Al[3260,15,3261,1557,4933,0,0]Al[3260,15,3261,3787,3202,0,0]Al[3260,15,3261,3787,4450,0,0]Ah[3260,5,3261,3787,0,0,0]Al[3260,15,3261,1557,4877,0,0]Ag[445,15,446,0,3189,0,0]Al[3260,15,3261,1547,3190,0,0]Ah[3260,5,3261,1941,0,0,0]Ae[4102,5,4103,0,0,0,0]Al[1068,13,1069,1069,3179,0,0]Aj[445,13,446,3786,3371,0,0]Ah[445,12,1878,0,2038,0,0]Ak[445,13,1958,0,3411,2038,0]Al[3260,12,1879,1879,2039,0,0]Ao[3260,13,1959,1959,3412,2039,0]Ah[3260,7,5017,5016,0,0,0]0Al[3260,13,3261,3787,3372,0,0]6Ai[445,13,446,1067,485,0,0]Ag[445,14,446,0,3377,0,0]Al[3260,14,3261,1572,3378,0,0]Ao[4102,13,4103,4103,5132,4931,0]0Ag[445,13,446,0,4200,0,0]Al[3260,13,3261,1876,4202,0,0]Ah[445,15,1958,0,1363,0,0]Ai[3260,15,1959,0,1364,0,0]10Ag[445,13,446,0,4902,0,0]Al[3260,13,3261,3354,4903,0,0]Am[1388,13,1389,1389,130,467,0]Am[2535,13,2536,2536,121,467,0]An[1068,13,1069,1069,3179,467,0]Am[1068,13,1868,1868,131,467,0]Am[1068,13,1964,1964,132,467,0]??>An[1388,13,1389,1389,130,4461,0]An[2535,13,2536,2536,121,4461,0]Ao[2535,13,2536,2536,2430,4461,0]Ao[1068,13,1069,1069,3179,4461,0]An[1068,13,1868,1868,131,4461,0]An[1068,13,1964,1964,132,4461,0]Ag[445,13,446,0,4249,0,0]Al[3260,13,3261,1876,4251,0,0]Ag[445,19,446,0,4936,0,0]Ai[3260,19,3261,0,4937,0,0]Ae[1068,5,4308,0,0,0,0]0Ag[445,13,446,0,4459,0,0]Al[3260,13,3261,1876,4460,0,0]Ag[445,13,446,0,4200,0,0]Al[3260,13,3261,1876,4202,0,0]Aj[445,13,446,2486,2439,0,0]Aj[445,13,446,2513,2443,0,0]Al[3260,13,3261,2487,2444,0,0]Al[3260,13,3261,3787,3372,0,0]Ao[4102,13,4103,4103,5132,4931,0];:Ag[445,5,1878,1879,0,0,0]Ae[3260,5,1879,0,0,0,0]Ah[445,15,1958,0,1363,0,0]Ai[3260,15,1959,0,1364,0,0]?>?>Aj[445,13,446,3786,3371,0,0]6Ag[445,13,446,0,4598,0,0]Al[3260,13,3261,3354,4599,0,0]Ae[4102,7,4103,0,0,0,0]Ae[4390,7,4391,0,0,0,0]Ag[445,14,446,0,3377,0,0]Al[3260,14,3261,1572,3378,0,0]?434343>=>=Al[3260,14,5083,5083,4982,0,0]54Ai[3260,18,3261,3354,0,0,0]") \ No newline at end of file diff --git a/pr/509/docs/search.index/function/c234980f56b0.js b/pr/509/docs/search.index/function/c234980f56b0.js new file mode 100644 index 000000000..831016fe3 --- /dev/null +++ b/pr/509/docs/search.index/function/c234980f56b0.js @@ -0,0 +1 @@ +rd_("Bf[\"{{{AIIh{AOLb}}{AIIh{AOLb}}}CNl}\",[]]Bf[\"{{{AIIh{BFAl}}{AIIh{BFAl}}}CNl}\",[]]Bd[\"{{{AIIh{CLj}}{AIIh{CLj}}}CNl}\",[]]Bf[\"{{{AIIh{BCAd}}{AIIh{BCAd}}}CNl}\",[]]Bf[\"{{{AIIh{BC@f}}{AIIh{BC@f}}}CNl}\",[]]Bf[\"{{{AIIh{AIAb}}{AIIh{AIAb}}}CNl}\",[]]Bf[\"{{{AIIh{AJHl}}{AIIh{AJHl}}}CNl}\",[]]Bf[\"{{{AIIh{BC@d}}{AIIh{BC@d}}}CNl}\",[]]Bf[\"{{{AIIh{AJK`}}{AIIh{AJK`}}}CNl}\",[]]Bf[\"{{{AIIh{BACn}}{AIIh{BACn}}}CNl}\",[]]Bd[\"{{{AIIh{LLb}}{AIIh{LLb}}}CNl}\",[]]Bf[\"{{{AIIh{BGFb}}{AIIh{BGFb}}}CNl}\",[]]Bd[\"{{{AIIh{JHd}}{AIIh{JHd}}}CNl}\",[]]Bd[\"{{{AIIh{LJ`}}{AIIh{LJ`}}}CNl}\",[]]Bf[\"{{{AIIh{ACEh}}{AIIh{ACEh}}}CNl}\",[]]Bf[\"{{{AIIh{AI@l}}{AIIh{AI@l}}}CNl}\",[]]Bf[\"{{{AIIh{A@@h}}{AIIh{A@@h}}}CNl}\",[]]Bf[\"{{{AIIh{B@Ml}}{AIIh{B@Ml}}}CNl}\",[]]Bf[\"{{{AIIh{BCKn}}{AIIh{BCKn}}}CNl}\",[]]Bf[\"{{{AIIh{AOMl}}{AIIh{AOMl}}}CNl}\",[]]Bf[\"{{{AIIh{AOLh}}{AIIh{AOLh}}}CNl}\",[]]Bf[\"{{{AIIh{AHNj}}{AIIh{AHNj}}}CNl}\",[]]Bf[\"{{{AIIh{BCKh}}{AIIh{BCKh}}}CNl}\",[]]Bf[\"{{{AIIh{BEDf}}{AIIh{BEDf}}}CNl}\",[]]Bf[\"{{{AIIh{B@Nh}}{AIIh{B@Nh}}}CNl}\",[]]Bf[\"{{{AIIh{B@Nn}}{AIIh{B@Nn}}}CNl}\",[]]Bf[\"{{{AIIh{B@Oh}}{AIIh{B@Oh}}}CNl}\",[]]Bf[\"{{{AIIh{BFI`}}{AIIh{BFI`}}}CNl}\",[]]Bd[\"{{{AIIh{CMh}}{AIIh{CMh}}}CNl}\",[]]Bb[\"{{{AIIh{Od}}{AIIh{Od}}}CNl}\",[]]Bf[\"{{{AIIh{ACCn}}{AIIh{ACCn}}}CNl}\",[]]Bf[\"{{{AIIh{BHHl}}{AIIh{BHHl}}}CNl}\",[]]Bd[\"{{{AIIh{JJf}}{AIIh{JJf}}}CNl}\",[]]Bd[\"{{{AIIh{LLd}}{AIIh{LLd}}}CNl}\",[]]Bf[\"{{{AIIh{BGFd}}{AIIh{BGFd}}}CNl}\",[]]Bd[\"{{{AIIh{JHf}}{AIIh{JHf}}}CNl}\",[]]Bf[\"{{{AIIh{BFAn}}{AIIh{BFAn}}}CNl}\",[]]Bd[\"{{{AIIh{LJb}}{AIIh{LJb}}}CNl}\",[]]Bf[\"{{{AIIh{AJH`}}{AIIh{AJH`}}}CNl}\",[]]Bf[\"{{{AIIh{AI@j}}{AIIh{AI@j}}}CNl}\",[]]Bf[\"{{{AIIh{BFAj}}{AIIh{BFAj}}}CNl}\",[]]Bf[\"{{{AIIh{BFHj}}{AIIh{BFHj}}}CNl}\",[]]Bf[\"{{{AIIh{AJDn}}{AIIh{AJDn}}}CNl}\",[]]Bf[\"{{{AIIh{ACEj}}{AIIh{ACEj}}}CNl}\",[]]Bf[\"{{{AIIh{AI@n}}{AIIh{AI@n}}}CNl}\",[]]Bf[\"{{{AIIh{AIAd}}{AIIh{AIAd}}}CNl}\",[]]Bf[\"{{{AIIh{BBBl}}{AIIh{BBBl}}}CNl}\",[]]Bf[\"{{{AIIh{A@@j}}{AIIh{A@@j}}}CNl}\",[]]Bf[\"{{{AIIh{AJKb}}{AIIh{AJKb}}}CNl}\",[]]Bf[\"{{{AIIh{AOMj}}{AIIh{AOMj}}}CNl}\",[]]Bf[\"{{{AIIh{AOOn}}{AIIh{AOOn}}}CNl}\",[]]Bf[\"{{{AIIh{BC@h}}{AIIh{BC@h}}}CNl}\",[]]Bf[\"{{{AIIh{B@Nd}}{AIIh{B@Nd}}}CNl}\",[]]Bf[\"{{{AIIh{BCL`}}{AIIh{BCL`}}}CNl}\",[]]Bf[\"{{{AIIh{AI@d}}{AIIh{AI@d}}}CNl}\",[]]Bf[\"{{{AIIh{BBLd}}{AIIh{BBLd}}}CNl}\",[]]Bf[\"{{{AIIh{AOMn}}{AIIh{AOMn}}}CNl}\",[]]Bf[\"{{{AIIh{AOLj}}{AIIh{AOLj}}}CNl}\",[]]Bf[\"{{{AIIh{AHNl}}{AIIh{AHNl}}}CNl}\",[]]Bf[\"{{{AIIh{BBNb}}{AIIh{BBNb}}}CNl}\",[]]Bf[\"{{{AIIh{AJG`}}{AIIh{AJG`}}}CNl}\",[]]Bf[\"{{{AIIh{AJIl}}{AIIh{AJIl}}}CNl}\",[]]Bf[\"{{{AIIh{B@Ob}}{AIIh{B@Ob}}}CNl}\",[]]Bf[\"{{{AIIh{BCKl}}{AIIh{BCKl}}}CNl}\",[]]Bf[\"{{{AIIh{BEDj}}{AIIh{BEDj}}}CNl}\",[]]Bf[\"{{{AIIh{BEEb}}{AIIh{BEEb}}}CNl}\",[]]Bf[\"{{{AIIh{B@Nj}}{AIIh{B@Nj}}}CNl}\",[]]Bf[\"{{{AIIh{ACAn}}{AIIh{ACAn}}}CNl}\",[]]Bf[\"{{{AIIh{BFAf}}{AIIh{BFAf}}}CNl}\",[]]Bf[\"{{{AIIh{B@O`}}{AIIh{B@O`}}}CNl}\",[]]Bf[\"{{{AIIh{B@Oj}}{AIIh{B@Oj}}}CNl}\",[]]Bf[\"{{{AIIh{BCA`}}{AIIh{BCA`}}}CNl}\",[]]Bf[\"{{{AIIh{BFIb}}{AIIh{BFIb}}}CNl}\",[]]Bd[\"{{{AIIh{CMj}}{AIIh{CMj}}}CNl}\",[]]Bb[\"{{{AIIh{Of}}{AIIh{Of}}}CNl}\",[]]Bf[\"{{{AIIh{ACD`}}{AIIh{ACD`}}}CNl}\",[]]Bf[\"{{{AIIh{AHNh}}{AIIh{AHNh}}}CNl}\",[]]Bf[\"{{{AIIh{BBCh}}{AIIh{BBCh}}}CNl}\",[]]Bf[\"{{{AIIh{BFNl}}{AIIh{BFNl}}}CNl}\",[]]Bd[\"{{{AIIh{JJj}}{AIIh{JJj}}}CNl}\",[]]Bf[\"{{{AIIh{BHHn}}{AIIh{BHHn}}}CNl}\",[]]Bd[\"{{{AIIh{JJh}}{AIIh{JJh}}}CNl}\",[]]Bf[\"{{{AIIh{B@Ol}}{AIIh{B@Ol}}}CNl}\",[]]Ai[\"{{{AIIh{CLj}}}LJ`}\",[]]Aj[\"{{{AIIh{BCAd}}}LJ`}\",[]]Ak[\"{{{AIIh{AJHh}}}ACCn}\",[]]Ak[\"{{{AIIh{BFNn}}}ACCn}\",[]]Ak[\"{{{AIIh{AJId}}}ACCn}\",[]]o[\"{LIdLJb}\",[]]A`[\"{AHKnLJb}\",[]]A`[\"{AHN`LJb}\",[]]A`[\"{BABdLJb}\",[]]A`[\"{BAAfLJb}\",[]]Aa[\"{ACCbACD`}\",[]]Aa[\"{ACEnACD`}\",[]]Aa[\"{ACAbACD`}\",[]]A`[\"{OO`ACD`}\",[]]n[\"{{}ONh}\",[]]A`[\"{CLbACAn}\",[]]Be[\"{{{AIIh{LLb}}{AIIh{LLb}}}ACC`}\",[]]Be[\"{{{AIIh{LJ`}}{AIIh{LJ`}}}ACC`}\",[]]Bg[\"{{{AIIh{AOMl}}{AIIh{AOMl}}}ACC`}\",[]]Bg[\"{{{AIIh{B@Nn}}{AIIh{B@Nn}}}ACC`}\",[]]Be[\"{{{AIIh{CMh}}{AIIh{CMh}}}ACC`}\",[]]Bc[\"{{{AIIh{Od}}{AIIh{Od}}}ACC`}\",[]]Bg[\"{{{AIIh{ACCn}}{AIIh{ACCn}}}ACC`}\",[]]Be[\"{{{AIIh{LLd}}{AIIh{LLd}}}ACC`}\",[]]Bg[\"{{{AIIh{BFAn}}{AIIh{BFAn}}}ACC`}\",[]]Be[\"{{{AIIh{LJb}}{AIIh{LJb}}}ACC`}\",[]]Bg[\"{{{AIIh{AIAd}}{AIIh{AIAd}}}ACC`}\",[]]Bg[\"{{{AIIh{BBBl}}{AIIh{BBBl}}}ACC`}\",[]]Bg[\"{{{AIIh{AJKb}}{AIIh{AJKb}}}ACC`}\",[]]Bg[\"{{{AIIh{AOMn}}{AIIh{AOMn}}}ACC`}\",[]]Bg[\"{{{AIIh{BEEb}}{AIIh{BEEb}}}ACC`}\",[]]Bg[\"{{{AIIh{BFAf}}{AIIh{BFAf}}}ACC`}\",[]]Bg[\"{{{AIIh{B@O`}}{AIIh{B@O`}}}ACC`}\",[]]Be[\"{{{AIIh{CMj}}{AIIh{CMj}}}ACC`}\",[]]Bc[\"{{{AIIh{Of}}{AIIh{Of}}}ACC`}\",[]]Bg[\"{{{AIIh{ACD`}}{AIIh{ACD`}}}ACC`}\",[]]o[\"{ACCnOd}\",[]]o[\"{ACD`Of}\",[]]n[\"{LIfOf}\",[]]o[\"{AHLjOf}\",[]]A`[\"{ACEbLIl}\",[]]A`[\"{ACEdLIl}\",[]]A`[\"{ACCjLIl}\",[]]A`[\"{ACEfLIl}\",[]]Bh[\"{{{AIIh{A@d}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AOLb}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BC@j}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BHAh}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AJEb}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AJEf}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BGFh}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BFAl}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{AC@n}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BAOh}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{ACAf}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AJHn}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BBN`}}{AIIh{CCbAHNf}}}LJd}\",[]]Bh[\"{{{AIIh{CLj}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BCAd}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AJHh}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BFNn}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BC@f}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{AIAb}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{AJHl}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BC@d}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BAD`}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AOOf}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AJId}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AJK`}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BACn}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bh[\"{{{AIIh{OOd}}{AIIh{CCbAHNf}}}LJd}\",[]]Cd[\"{{{AIIh{LLb}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Ce[\"{{{AIIh{BGFb}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Cd[\"{{{AIIh{JHd}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Cd[\"{{{AIIh{LJ`}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Ce[\"{{{AIIh{ACEh}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{AJFb}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{AI@l}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{BCLf}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{A@@h}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{B@Ml}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Ce[\"{{{AIIh{BCKn}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Ce[\"{{{AIIh{BCMj}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{BCNl}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{BFDl}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{BFFj}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{AOMl}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{BBMf}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{BACb}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{BGMd}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{B@M`}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{AOLh}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Ce[\"{{{AIIh{AHNj}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Ce[\"{{{AIIh{BCKh}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Ce[\"{{{AIIh{BEDf}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Ce[\"{{{AIIh{B@Nh}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Ce[\"{{{AIIh{B@Nn}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Ce[\"{{{AIIh{B@Oh}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{BFI`}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Ce[\"{{{AIIh{AJFh}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{BEHh}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Cd[\"{{{AIIh{CMh}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Cc[\"{{{AIIh{Od}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Ce[\"{{{AIIh{ACCn}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Ce[\"{{{AIIh{ACEb}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Cc[\"{{{AIIh{Nh}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{AHM`}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Cd[\"{{{AIIh{JHl}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{AOMb}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Cd[\"{{{AIIh{OMb}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{BBAl}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{BF@d}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{BHHl}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{AOLn}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{BFIh}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]Ce[\"{{{AIIh{BGEj}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Cd[\"{{{AIIh{JJf}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Bi[\"{{{AIIh{AJEh}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{ACA`}}{AIIh{CCbAHNf}}}LJd}\",[]]Bh[\"{{{AIIh{LLd}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BGFd}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bh[\"{{{AIIh{JHf}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BFAn}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bh[\"{{{AIIh{LJb}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BABj}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AJH`}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AI@j}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BFAj}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BFHj}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{AJDn}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{ACEj}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AJFd}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AI@n}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BCLh}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AIAd}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BBBl}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{A@@j}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AJKb}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{AOMj}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{AOOn}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BC@h}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{B@Nd}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BCL`}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{AI@d}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BBLd}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BH@n}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bh[\"{{{AIIh{JK`}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BCMl}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BCNn}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BFDn}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BFFl}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AOMn}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BBMh}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BACf}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BGMf}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{B@Md}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AOLj}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{AHNl}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BBNb}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{AJG`}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{AJIl}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{B@Ob}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BCKl}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BEDj}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{AC@b}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BEEb}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{ACAh}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{B@Nj}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{AOKn}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AJI`}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BBBb}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BCMh}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BHAf}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{ACAn}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BFAf}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BCLd}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BAOf}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{B@O`}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{B@Oj}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BCA`}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BFIb}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{AJFj}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BHMb}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BEHj}}{AIIh{CCbAHNf}}}LJd}\",[]]Bh[\"{{{AIIh{CMj}}{AIIh{CCbAHNf}}}LJd}\",[]]Bg[\"{{{AIIh{Of}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{ACD`}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{ACEd}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AHNh}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BBCh}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BFNl}}{AIIh{CCbAHNf}}}LJd}\",[]]Bh[\"{{{AIIh{JJj}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bi[\"{{{AIIh{BHHn}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AOM`}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BFIn}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BGEl}}{AIIh{CCbAHNf}}}LJd}\",[]]0Cd[\"{{{AIIh{JJh}}{AIIh{CCbAHNf}}}{{LJf{HLdJIf}}}}\",[]]0Bg[\"{{{AIIh{Nj}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AHMb}}{AIIh{CCbAHNf}}}LJd}\",[]]Bh[\"{{{AIIh{JHn}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{AOMd}}{AIIh{CCbAHNf}}}LJd}\",[]]Bh[\"{{{AIIh{OMd}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BBAn}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BF@f}}{AIIh{CCbAHNf}}}LJd}\",[]]Bh[\"{{{AIIh{JJl}}{AIIh{CCbAHNf}}}LJd}\",[]]0Bh[\"{{{AIIh{JKb}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{BCAb}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{ACCj}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{ACEf}}{AIIh{CCbAHNf}}}LJd}\",[]]Bi[\"{{{AIIh{B@Ol}}{AIIh{CCbAHNf}}}LJd}\",[]]Ak[\"{{{AIIh{BABj}}}AC@j}\",[]]A`[\"{ACEhCJb}\",[]]A`[\"{ACEjCJb}\",[]]Aj[\"{{{AIIh{BBBb}}}LFd}\",[]]A`[\"{BHAfCJb}\",[]]o[\"{CLbCJb}\",[]]A`[\"{ACCjLFd}\",[]]Ce[\"{{{AIIh{CCbBGMd}}{Nf{ACDj}}}{{AIIh{CCbBGMd}}}}\",[]]Ce[\"{{{AIIh{CCbBGMf}}{Nf{ACDl}}}{{AIIh{CCbBGMf}}}}\",[]]8Af[\"{{AC@jCJb}BC@j}\",[]]Ag[\"{{}{{Nf{BHAh}}}}\",[]]Ae[\"{{LFdCJ`}BANf}\",[]]Cd[\"{{BBMf{LIl{BACb}}AIAj{Nf{ONj}}}{{LJh{AC@n}}}}\",[]]Ah[\"{{{Nn{JMd}}}AJHb}\",[]]A`[\"{ONbBCMj}\",[]]Ah[\"{{{Nf{OL`}}}BBMf}\",[]]Bc[\"{{{Nf{BACd}}{Nf{BF@h}}}BACb}\",[]]Ai[\"{{{Nf{B@Mb}}}B@M`}\",[]]B`[\"{{{AIIh{{LCn{Mn}}}}}B@Nn}\",[]]Ae[\"{{CJ`LFd}BEHh}\",[]]Ag[\"{{CMhOdCJb}ACCn}\",[]]Aj[\"{{{Nf{AHM`}}CIn}Nh}\",[]]An[\"{{{Nf{AOMb}}OLfCIn}JHl}\",[]]An[\"{{{Nf{BBAl}}OLfCIn}OMb}\",[]]Ae[\"{{LFdCJ`}BANh}\",[]]Ah[\"{{{Nn{JMd}}}AJHd}\",[]]Bm[\"{{{Nf{BBMh}}{LIl{{Nf{BACf}}}}CNl}ACA`}\",[]]A`[\"{LFdBI@d}\",[]]o[\"{{}BHLh}\",[]]A`[\"{ONbBCMl}\",[]]Ah[\"{{{Nf{OLb}}}BBMh}\",[]]Bc[\"{{{Nf{BACh}}{Nf{BF@j}}}BACf}\",[]]Ai[\"{{{Nf{B@Mf}}}B@Md}\",[]]Ek[\"{{ABOh{AIIh{c}}{AIIh{{LCn{CJ`}}}}CNl}{{LJf{{LFb{BBBb{LIl{ABOh}}}}BFAf}}}{BFEhJLl}}\",[\"\"]]B`[\"{{{AIIh{{LCn{Mn}}}}}B@O`}\",[]]Af[\"{{BFIbLKn}BCA`}\",[]]A`[\"{LFdBHMb}\",[]]Ae[\"{{CJ`LFd}BEHj}\",[]]Ag[\"{{CMjOfCJb}ACD`}\",[]]Aj[\"{{{Nf{AHMb}}CIn}Nj}\",[]]An[\"{{{Nf{AOMd}}OLfCIn}JHn}\",[]]An[\"{{{Nf{BBAn}}OLfCIn}OMd}\",[]]Aj[\"{AJJn{{LJh{BCAb}}}}\",[]]Aa[\"{BEHbBHG`}\",[]]Aa[\"{BEHfBHGb}\",[]]o[\"{CMhCMh}\",[]]o[\"{CMjCMj}\",[]]Ak[\"{{{AIIh{AJJb}}}AJJ`}\",[]]Ai[\"{{{AIIh{ONj}}}OLf}\",[]]0Aj[\"{{{AIIh{BAD`}}}OLf}\",[]]Ak[\"{{{AIIh{BBCd}}}AJJ`}\",[]]Ak[\"{{{AIIh{AJJd}}}AJJ`}\",[]]Ak[\"{{{AIIh{BBCf}}}AJJ`}\",[]]Bg[\"{{{AIIh{AJEf}}LJ`}{{LIl{AC@j}}}}\",[]]Aa[\"{AI@lAC@j}\",[]]Bg[\"{{{AIIh{AJEh}}LJb}{{LIl{AC@j}}}}\",[]]Aa[\"{AI@nAC@j}\",[]]Ah[\"{A@f{{LJj{HLd}}}}\",[]]Ah[\"{A@h{{LJj{HLd}}}}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]Aa[\"{BAAfAJIj}\",[]]Aa[\"{ACCjAJIj}\",[]]Aa[\"{BCA`BFIb}\",[]]A`[\"{AI@lCJb}\",[]]A`[\"{AI@nCJb}\",[]]A`[\"{AC@bJHb}\",[]]Am[\"{{{AIIh{CCbACBl}}}HLd}\",[]]Am[\"{{{AIIh{CCbACAf}}}HLd}\",[]]Al[\"{{{AIIh{CCbCLj}}}HLd}\",[]]Am[\"{{{AIIh{CCbBCAd}}}HLd}\",[]]Am[\"{{{AIIh{CCbAJHh}}}HLd}\",[]]Am[\"{{{AIIh{CCbAJId}}}HLd}\",[]]Al[\"{{{AIIh{CCbLGl}}}HLd}\",[]]A`[\"{cc{}}\",[\"T\"]]000000000000000000000Aa[\"{BCKnBFAl}\",[]]1111111111Aa[\"{AIAbBC@f}\",[]]22Aa[\"{BCKnAIAb}\",[]]Aa[\"{AJHlAIAb}\",[]]4Aa[\"{BCKnAJHl}\",[]]5Aa[\"{AIAbBC@d}\",[]]6666Aa[\"{BCKnAJK`}\",[]]Aa[\"{B@MlAJK`}\",[]]Aa[\"{BACnAJK`}\",[]]Aa[\"{BCKnBACn}\",[]]:::::::o[\"{CInLLb}\",[]]A`[\"{BFI`LLb}\",[]]A`[\"{ACCnLLb}\",[]]o[\"{CJ`LLb}\",[]]n[\"{MnLLb}\",[]]???Al[\"{cLJ`{{CKl{CJ`}}}}\",[\"T\"]]A`[\"{cc{}}\",[\"T\"]]0000000Aa[\"{AOLfBCKn}\",[]]A`[\"{JHjBCKn}\",[]]A`[\"{JJ`BCKn}\",[]]33333A`[\"{LLbAOMl}\",[]]4444Aa[\"{BCO`AOLh}\",[]]Aa[\"{BGFbAOLh}\",[]]66A`[\"{JJ`BCKh}\",[]]777Aa[\"{BFIhB@Nh}\",[]]88A`[\"{LLbBFI`}\",[]]999999A`[\"{LLbACCn}\",[]]::::::::::::Aa[\"{B@@dAOLn}\",[]];;;;;;A`[\"{BEFbJJf}\",[]]A`[\"{BEGjJJf}\",[]]A`[\"{AJGfJJf}\",[]]A`[\"{BHIlJJf}\",[]]A`[\"{BCLjJJf}\",[]]A`[\"{BBNdJJf}\",[]]A`[\"{BBOfJJf}\",[]]A`[\"{BCNdJJf}\",[]]A`[\"{BHHjJJf}\",[]]A`[\"{BHClJJf}\",[]]A`[\"{BEDlJJf}\",[]]A`[\"{cc{}}\",[\"T\"]]00000000000000000000A`[\"{BFIbLLd}\",[]]A`[\"{ACD`LLd}\",[]]o[\"{CJ`LLd}\",[]]n[\"{MnLLd}\",[]]4o[\"{CInLLd}\",[]]555Al[\"{cLJb{{CKl{CJ`}}}}\",[\"T\"]]6666666666666666666Aa[\"{BCA`BCL`}\",[]]7777A`[\"{AI@jJK`}\",[]]88888A`[\"{LLdAOMn}\",[]]99999Aa[\"{BGFdAOLj}\",[]]:Aa[\"{BCO`AOLj}\",[]];;;;;;Aa[\"{BCA`BCKl}\",[]]<<<>>>A`[\"{JJjBCA`}\",[]]??A`[\"{LLdBFIb}\",[]]A`[\"{cc{}}\",[\"T\"]]0000A`[\"{LLdACD`}\",[]]111111A`[\"{BBChJJj}\",[]]2222Aa[\"{B@@dAOM`}\",[]]333333A`[\"{BHClJJh}\",[]]A`[\"{BHHjJJh}\",[]]A`[\"{BCNdJJh}\",[]]6A`[\"{BEDlJJh}\",[]]A`[\"{BEGjJJh}\",[]]A`[\"{BBOfJJh}\",[]]A`[\"{BBNdJJh}\",[]]A`[\"{BEFbJJh}\",[]]A`[\"{BHIlJJh}\",[]]A`[\"{AJGfJJh}\",[]]A`[\"{BCLjJJh}\",[]]>>>>>>>>>An[\"{{{AIIh{c}}}AJJnCJh}\",[\"S\"]]??????????????Bm[\"{{{AIIh{LLb}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bk[\"{{{AIIh{LJ`}}{AIIh{CCbc}}}HLdLHn}\",[\"H\"]]Bn[\"{{{AIIh{AOMl}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bn[\"{{{AIIh{B@Nn}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bm[\"{{{AIIh{CMh}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bl[\"{{{AIIh{Od}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bn[\"{{{AIIh{ACCn}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bm[\"{{{AIIh{LLd}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bn[\"{{{AIIh{BFAn}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bk[\"{{{AIIh{LJb}}{AIIh{CCbc}}}HLdLHn}\",[\"H\"]]Bn[\"{{{AIIh{AIAd}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bn[\"{{{AIIh{BBBl}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bn[\"{{{AIIh{AJKb}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bn[\"{{{AIIh{AOMn}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bn[\"{{{AIIh{BEEb}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bn[\"{{{AIIh{BFAf}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bn[\"{{{AIIh{B@O`}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bm[\"{{{AIIh{CMj}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bl[\"{{{AIIh{Of}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bn[\"{{{AIIh{ACD`}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Bn[\"{{{AIIh{AHNh}}{AIIh{CCbc}}}HLdLHn}\",[\"__H\"]]Aa[\"{{}c{}}\",[\"U\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n[\"{{}HLd}\",[]]0000Cc[\"{{{AIIh{CCbLGl}}LFd}{{LJf{{LIl{JHf}}AIAd}}}}\",[]]Cf[\"{{{AIIh{CCbBB@j}}}{{LIl{c}}}{}}\",[\"Iterator::Item\"]]Dl[\"{{{AIIh{BF@h}}HLb{AIIh{CCb{LCn{Mn}}}}}{{LJf{{AIIh{{LCn{Mn}}}}AOLn}}}}\",[]]Bh[\"{{{AIIh{CCbOOl}}Of}{{LIl{ACD`}}}}\",[]]Dl[\"{{{AIIh{BF@j}}HLb{AIIh{CCb{LCn{Mn}}}}}{{LJf{{AIIh{{LCn{Mn}}}}AOM`}}}}\",[]]Bf[\"{{{AIIh{AJEf}}LJ`}{{LIl{CLj}}}}\",[]]Bf[\"{{{AIIh{CLj}}}{{LJf{HLdAJDh}}}}\",[]]A`[\"{AJFbCJb}\",[]]Am[\"{{{AIIh{CCbAJEh}}}HLd}\",[]]A`[\"{AJFdCJb}\",[]]Ck[\"{{{A@l{{AIIh{CCbAJD`}}}}{AIIh{CCbOKd}}}{{CMb{HLd}}}}\",[]]Dk[\"{{{A@l{{AIIh{CCbBCOf}}}}{AIIh{CCbOKd}}}{{CMb{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@l{{AIIh{CCbACBl}}}}{AIIh{CCbOKd}}}{{CMb{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@l{{AIIh{CCbACBn}}}}{AIIh{CCbOKd}}}{{CMb{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@l{{AIIh{CCbAJEb}}}}{AIIh{CCbOKd}}}{{CMb{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{A@l{{AIIh{CCbOMh}}}}{AIIh{CCbOKd}}}{{CMb{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{A@l{{AIIh{CCbLIb}}}}{AIIh{CCbOKd}}}{{CMb{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@l{{AIIh{CCbAHLb}}}}{AIIh{CCbOKd}}}{{CMb{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@l{{AIIh{CCbABOf}}}}{AIIh{CCbOKd}}}{{CMb{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@l{{AIIh{CCbBABb}}}}{AIIh{CCbOKd}}}{{CMb{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@l{{AIIh{CCbBABl}}}}{AIIh{CCbOKd}}}{{CMb{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{A@l{{AIIh{CCbLFl}}}}{AIIh{CCbOKd}}}{{CMb{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@l{{AIIh{CCbBBN`}}}}{AIIh{CCbOKd}}}{{CMb{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{A@l{{AIIh{CCbOOd}}}}{AIIh{CCbOKd}}}{{CMb{c}}}{}}\",[\"Future::Output\"]]Bf[\"{{{AIIh{CCbAJEh}}}{{LIl{JK`}}}}\",[]]De[\"{{{AIIh{CCbAJHh}}{AIIh{CCb{LCn{Mn}}}}}{{LJf{{LIl{LFd}}AIAb}}}}\",[]]Bm[\"{{{AIIh{CCbAJHj}}CNl}{{LJf{LGlBBBl}}}}\",[]]Bo[\"{{CMj{AIIh{CCbc}}}{{LJf{BFNlJJj}}}O`}\",[\"R\"]]Bf[\"{{{AIIh{CCbAHMl}}}{{LIl{JHb}}}}\",[]]Ec[\"{{{AIIh{BCAb}}AJJn{AIIh{CCb{LCn{AJFl}}}}{AIIh{CCb{LCn{ACCj}}}}}{{LJh{LFd}}}}\",[]]Cn[\"{{{AIIh{BF@h}}HLb{AIIh{CCb{AAb{Mn}}}}}{{LJf{HLdAOLn}}}}\",[]]Cn[\"{{{AIIh{BF@j}}HLb{AIIh{CCb{AAb{Mn}}}}}{{LJf{HLdAOM`}}}}\",[]]C`[\"{{{AIIh{CCbAHMl}}JHbCNl}{{LJf{HLdBFAn}}}}\",[]]Cc[\"{{{AIIh{BCAb}}AJJn{AIIh{ACEf}}}{{LJh{HLd}}}}\",[]]Bn[\"{{{AIIh{CCbBGMd}}CJ`}{{AIIh{CCbBGMd}}}}\",[]]Bn[\"{{{AIIh{CCbBGMf}}CJ`}{{AIIh{CCbBGMf}}}}\",[]]Aj[\"{{{AIIh{AJEf}}}CMh}\",[]]Aj[\"{{{AIIh{AJEh}}}CMj}\",[]]Cn[\"{{{AIIh{OL`}}{AIIh{{LCn{Mn}}}}{AIIh{CCb{LCn{Mn}}}}}HLd}\",[]]Cn[\"{{{AIIh{OLb}}{AIIh{{LCn{Mn}}}}{AIIh{CCb{LCn{Mn}}}}}HLd}\",[]]A`[\"{BC@jCJb}\",[]]A`[\"{ACEbLFd}\",[]]A`[\"{ACEdLFd}\",[]]o[\"{JLhCNl}\",[]]Bm[\"{{{AIIh{CCbAJHh}}LLb}{{LJf{HLdB@Ml}}}}\",[]]Bm[\"{{{AIIh{CCbBFNn}}LLb}{{LJf{HLdB@Ml}}}}\",[]]Bm[\"{{{AIIh{CCbAJHj}}LLd}{{LJf{HLdB@Nd}}}}\",[]]Bn[\"{{{AIIh{AJJj}}{AIIh{CIl}}}{{LIl{JHb}}}}\",[]]Bn[\"{{{AIIh{BBOb}}{AIIh{CIl}}}{{LIl{JHb}}}}\",[]]Bn[\"{{{AIIh{BEHh}}{AIIh{CIl}}}{{LIl{JHb}}}}\",[]]Bn[\"{{{AIIh{AJJl}}{AIIh{CIl}}}{{LIl{JHb}}}}\",[]]Bn[\"{{{AIIh{BBOd}}{AIIh{CIl}}}{{LIl{JHb}}}}\",[]]Bn[\"{{{AIIh{BEHj}}{AIIh{CIl}}}{{LIl{JHb}}}}\",[]]Aa[\"{{}c{}}\",[\"V\"]]0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bd[\"{{{AIIh{BEEh}}}{{LIl{BB@j}}}}\",[]]Bg[\"{{{Nf{BF@`}}OLfCIn}{{Nn{AJEj}}}}\",[]]Bg[\"{{{Nf{AHM`}}OLfCIn}{{Nn{AJEj}}}}\",[]]Bg[\"{{{Nf{AOMb}}OLfCIn}{{Nn{AJEj}}}}\",[]]Bg[\"{{{Nf{BBAl}}OLfCIn}{{Nn{AJEj}}}}\",[]]Bg[\"{{{Nf{BF@b}}OLfCIn}{{Nn{AJEl}}}}\",[]]Bg[\"{{{Nf{AHMb}}OLfCIn}{{Nn{AJEl}}}}\",[]]Bg[\"{{{Nf{AOMd}}OLfCIn}{{Nn{AJEl}}}}\",[]]Bg[\"{{{Nf{BBAn}}OLfCIn}{{Nn{AJEl}}}}\",[]]Ai[\"{BFN`{{Nf{BF@b}}}}\",[]]o[\"{JHdJHb}\",[]]A`[\"{ACEhCJb}\",[]]A`[\"{A@@hLFd}\",[]]o[\"{JHfJHb}\",[]]A`[\"{ACEjCJb}\",[]]A`[\"{A@@jLFd}\",[]]Aa[\"{AOKnBCL`}\",[]]Ai[\"{{{AIIh{A@d}}}A@d}\",[]]Ak[\"{{{AIIh{AOLb}}}AOLb}\",[]]Ak[\"{{{AIIh{AJEf}}}AJEf}\",[]]Ak[\"{{{AIIh{BGFh}}}BGFh}\",[]]Ak[\"{{{AIIh{BFAl}}}BFAl}\",[]]Ak[\"{{{AIIh{AC@n}}}AC@n}\",[]]Ak[\"{{{AIIh{BAOh}}}BAOh}\",[]]Ai[\"{{{AIIh{CLj}}}CLj}\",[]]Ak[\"{{{AIIh{BCAd}}}BCAd}\",[]]Ak[\"{{{AIIh{BC@f}}}BC@f}\",[]]Ak[\"{{{AIIh{AIAb}}}AIAb}\",[]]Ak[\"{{{AIIh{AJHl}}}AJHl}\",[]]Ak[\"{{{AIIh{BC@d}}}BC@d}\",[]]Ak[\"{{{AIIh{AJK`}}}AJK`}\",[]]Ak[\"{{{AIIh{BACn}}}BACn}\",[]]Ai[\"{{{AIIh{LLb}}}LLb}\",[]]Ak[\"{{{AIIh{BGFb}}}BGFb}\",[]]Ai[\"{{{AIIh{LJ`}}}LJ`}\",[]]Ak[\"{{{AIIh{ACEh}}}ACEh}\",[]]Ak[\"{{{AIIh{AJFb}}}AJFb}\",[]]Ak[\"{{{AIIh{AI@l}}}AI@l}\",[]]Ak[\"{{{AIIh{BCLf}}}BCLf}\",[]]Ak[\"{{{AIIh{A@@h}}}A@@h}\",[]]Ak[\"{{{AIIh{B@Ml}}}B@Ml}\",[]]Ak[\"{{{AIIh{BCKn}}}BCKn}\",[]]Ak[\"{{{AIIh{BCNl}}}BCNl}\",[]]Ak[\"{{{AIIh{BFDl}}}BFDl}\",[]]Ak[\"{{{AIIh{BFFj}}}BFFj}\",[]]Ak[\"{{{AIIh{AOMl}}}AOMl}\",[]]Ak[\"{{{AIIh{BBMf}}}BBMf}\",[]]Ak[\"{{{AIIh{BACb}}}BACb}\",[]]Ak[\"{{{AIIh{BGMd}}}BGMd}\",[]]Ak[\"{{{AIIh{B@M`}}}B@M`}\",[]]Ak[\"{{{AIIh{AOLh}}}AOLh}\",[]]Ak[\"{{{AIIh{AHNj}}}AHNj}\",[]]Ak[\"{{{AIIh{BCKh}}}BCKh}\",[]]Ak[\"{{{AIIh{BEDf}}}BEDf}\",[]]Ak[\"{{{AIIh{B@Nh}}}B@Nh}\",[]]Ak[\"{{{AIIh{B@Nn}}}B@Nn}\",[]]Ak[\"{{{AIIh{B@Oh}}}B@Oh}\",[]]Ak[\"{{{AIIh{BFI`}}}BFI`}\",[]]Ak[\"{{{AIIh{AJFh}}}AJFh}\",[]]Ai[\"{{{AIIh{CMh}}}CMh}\",[]]Ag[\"{{{AIIh{Od}}}Od}\",[]]Ak[\"{{{AIIh{ACCn}}}ACCn}\",[]]Ag[\"{{{AIIh{Nh}}}Nh}\",[]]Ak[\"{{{AIIh{AHM`}}}AHM`}\",[]]Ai[\"{{{AIIh{JHl}}}JHl}\",[]]Ak[\"{{{AIIh{AOMb}}}AOMb}\",[]]Ai[\"{{{AIIh{OMb}}}OMb}\",[]]Ak[\"{{{AIIh{BBAl}}}BBAl}\",[]]Ak[\"{{{AIIh{BEH`}}}BEH`}\",[]]Ak[\"{{{AIIh{BGEj}}}BGEj}\",[]]Ak[\"{{{AIIh{BEHd}}}BEHd}\",[]]Ai[\"{{{AIIh{JJf}}}JJf}\",[]]Ai[\"{{{AIIh{LLd}}}LLd}\",[]]Ak[\"{{{AIIh{BGFd}}}BGFd}\",[]]Ak[\"{{{AIIh{BFAn}}}BFAn}\",[]]Ai[\"{{{AIIh{LJb}}}LJb}\",[]]Ak[\"{{{AIIh{BABj}}}BABj}\",[]]Ak[\"{{{AIIh{AJH`}}}AJH`}\",[]]Ak[\"{{{AIIh{AI@j}}}AI@j}\",[]]Ak[\"{{{AIIh{BFAj}}}BFAj}\",[]]Ak[\"{{{AIIh{BFHj}}}BFHj}\",[]]Ak[\"{{{AIIh{AJDn}}}AJDn}\",[]]Ak[\"{{{AIIh{ACEj}}}ACEj}\",[]]Ak[\"{{{AIIh{AJFd}}}AJFd}\",[]]Ak[\"{{{AIIh{AI@n}}}AI@n}\",[]]Ak[\"{{{AIIh{BCLh}}}BCLh}\",[]]Ak[\"{{{AIIh{AIAd}}}AIAd}\",[]]Ak[\"{{{AIIh{BBBl}}}BBBl}\",[]]Ak[\"{{{AIIh{A@@j}}}A@@j}\",[]]Ak[\"{{{AIIh{AJKb}}}AJKb}\",[]]Ak[\"{{{AIIh{AOMj}}}AOMj}\",[]]Ak[\"{{{AIIh{BC@h}}}BC@h}\",[]]Ak[\"{{{AIIh{B@Nd}}}B@Nd}\",[]]Ak[\"{{{AIIh{BCL`}}}BCL`}\",[]]Ak[\"{{{AIIh{AI@d}}}AI@d}\",[]]Ak[\"{{{AIIh{BBLd}}}BBLd}\",[]]Ak[\"{{{AIIh{BH@n}}}BH@n}\",[]]Ak[\"{{{AIIh{BCNn}}}BCNn}\",[]]Ak[\"{{{AIIh{BFDn}}}BFDn}\",[]]Ak[\"{{{AIIh{BFFl}}}BFFl}\",[]]Ak[\"{{{AIIh{AOMn}}}AOMn}\",[]]Ak[\"{{{AIIh{BBMh}}}BBMh}\",[]]Ak[\"{{{AIIh{BACf}}}BACf}\",[]]Ak[\"{{{AIIh{BGMf}}}BGMf}\",[]]Ak[\"{{{AIIh{B@Md}}}B@Md}\",[]]Ak[\"{{{AIIh{AOLj}}}AOLj}\",[]]Ak[\"{{{AIIh{AHNl}}}AHNl}\",[]]Ak[\"{{{AIIh{AJG`}}}AJG`}\",[]]Ak[\"{{{AIIh{AJIl}}}AJIl}\",[]]Ak[\"{{{AIIh{B@Ob}}}B@Ob}\",[]]Ak[\"{{{AIIh{BCKl}}}BCKl}\",[]]Ak[\"{{{AIIh{BEDj}}}BEDj}\",[]]Ak[\"{{{AIIh{AC@b}}}AC@b}\",[]]Ak[\"{{{AIIh{BEEb}}}BEEb}\",[]]Ak[\"{{{AIIh{B@Nj}}}B@Nj}\",[]]Ak[\"{{{AIIh{BCMh}}}BCMh}\",[]]Ak[\"{{{AIIh{BHAf}}}BHAf}\",[]]Ak[\"{{{AIIh{ACAn}}}ACAn}\",[]]Ak[\"{{{AIIh{BFAf}}}BFAf}\",[]]Ak[\"{{{AIIh{B@O`}}}B@O`}\",[]]Ak[\"{{{AIIh{B@Oj}}}B@Oj}\",[]]Ak[\"{{{AIIh{BCA`}}}BCA`}\",[]]Ak[\"{{{AIIh{BFIb}}}BFIb}\",[]]Ak[\"{{{AIIh{AJFj}}}AJFj}\",[]]Ak[\"{{{AIIh{BHMb}}}BHMb}\",[]]Ai[\"{{{AIIh{CMj}}}CMj}\",[]]Ag[\"{{{AIIh{Of}}}Of}\",[]]Ak[\"{{{AIIh{ACD`}}}ACD`}\",[]]Ak[\"{{{AIIh{AHNh}}}AHNh}\",[]]Ak[\"{{{AIIh{BBCh}}}BBCh}\",[]]Ak[\"{{{AIIh{BFNl}}}BFNl}\",[]]Ai[\"{{{AIIh{JJj}}}JJj}\",[]]Ak[\"{{{AIIh{BEHb}}}BEHb}\",[]]Ak[\"{{{AIIh{BGEl}}}BGEl}\",[]]Ak[\"{{{AIIh{BEHf}}}BEHf}\",[]]Ai[\"{{{AIIh{JJh}}}JJh}\",[]]Ag[\"{{{AIIh{Nj}}}Nj}\",[]]Ak[\"{{{AIIh{AHMb}}}AHMb}\",[]]Ai[\"{{{AIIh{JHn}}}JHn}\",[]]Ak[\"{{{AIIh{AOMd}}}AOMd}\",[]]Ai[\"{{{AIIh{OMd}}}OMd}\",[]]Ak[\"{{{AIIh{BBAn}}}BBAn}\",[]]Ai[\"{{{AIIh{JKb}}}JKb}\",[]]Ak[\"{{{AIIh{ACCj}}}ACCj}\",[]]Ak[\"{{{AIIh{ACEf}}}ACEf}\",[]]Ak[\"{{{AIIh{B@Ol}}}B@Ol}\",[]]Ak[\"{{{AIIh{BFN`}}}BFN`}\",[]]Ak[\"{{{AIIh{BCM`}}}BCM`}\",[]]Ak[\"{{{AIIh{BHG`}}}BHG`}\",[]]Ak[\"{{{AIIh{BHGb}}}BHGb}\",[]]Bn[\"{{{AIIh{AJEf}}LLb{AIIh{{LCn{Mn}}}}}HLd}\",[]]Bn[\"{{{AIIh{AC@n}}LLb{AIIh{{LCn{Mn}}}}}HLd}\",[]]Bf[\"{{{AIIh{CLj}}}{{LJf{HLdBBLb}}}}\",[]]Bf[\"{{{AIIh{CCbAJEh}}OLfLLdJHb}HLd}\",[]]Bj[\"{{{AIIh{B@Nn}}}{{AIIh{{LCn{Mn}}}}}}\",[]]Bj[\"{{{AIIh{B@O`}}}{{AIIh{{LCn{Mn}}}}}}\",[]]Aa[\"{BCA`AJG`}\",[]]A`[\"{ACCnCJb}\",[]]A`[\"{ACD`CJb}\",[]]A`[\"{B@OhCNl}\",[]]A`[\"{B@OjCNl}\",[]]l[\"{OLh}\",[]]l[\"{OLj}\",[]]Ce[\"{{{AIIh{BFEh}}{AIIh{CCbO`}}}{{LJf{B@O`BFAf}}}}\",[]]Ce[\"{{{AIIh{BI@d}}{AIIh{CCbO`}}}{{LJf{B@O`BFAf}}}}\",[]]Bc[\"{{{AIIh{AJEh}}}{{AAb{LJb}}}}\",[]]Bd[\"{{{AIIh{JLn}}{AIIh{CIl}}}HLd}\",[]]Am[\"{{{AIIh{AOLd}}LFd}HLd}\",[]]Ai[\"{{{AIIh{JM`}}}HLd}\",[]]Bg[\"{{{A@l{{AIIh{CCbAJD`}}}}OLf}HLd}\",[]]Bm[\"{{{AIIh{CCbAJId}}LLb}{{LJf{HLdB@Ml}}}}\",[]]Bm[\"{{{AIIh{CCbAJIf}}LLd}{{LJf{HLdB@Nd}}}}\",[]]Am[\"{ACAf{{LJf{HLdAJHn}}}}\",[]]Dc[\"{{{AIIh{CCbACA`}}ACAh{AIIh{CCb{AAb{Mn}}}}}{{LJf{ACEdAJI`}}}}\",[]]Bi[\"{{{AIIh{ONj}}{A@l{{Nn{LHl}}}}}HLd}\",[]]Bj[\"{{{AIIh{BAD`}}{A@l{{Nn{LHl}}}}}HLd}\",[]]Bj[\"{{{AIIh{AOOf}}{A@l{{Nn{LHl}}}}}HLd}\",[]]o[\"{A@dCNl}\",[]]Ak[\"{{{AIIh{AJEf}}}BCLf}\",[]]Ak[\"{{{AIIh{AC@n}}}BAOh}\",[]]Aj[\"{{{AIIh{CLj}}}AI@l}\",[]]An[\"{{{AIIh{CCbAJEh}}}BCLh}\",[]]Ce[\"{{{AIIh{CCbAJHb}}{LIl{LKn}}}{{AIIh{CCbAJHb}}}}\",[]]Ce[\"{{{AIIh{CCbAJHd}}{LIl{LKn}}}{{AIIh{CCbAJHd}}}}\",[]]Ck[\"{{{AIIh{CCbAJId}}{AIIh{{LCn{Mn}}}}}{{LJf{LFdAJK`}}}}\",[]]Ck[\"{{{AIIh{CCbAJIf}}{AIIh{{LCn{Mn}}}}}{{LJf{LFdAJKb}}}}\",[]]Bl[\"{{{AIIh{BFNl}}{AIIh{CCbc}}}HLdLGj}\",[\"W\"]]Aj[\"{{{AIIh{AC@n}}}LFl}\",[]]An[\"{ACAf{{LJf{AJEbBCKn}}}}\",[]]Bh[\"{{{AIIh{CCbOOl}}Of}{{LIl{ACD`}}}}\",[]]Fh[\"{{{AIIh{CCbACA`}}ACAhOLf{AIIh{CCb{AAb{Mn}}}}{LIl{{Nf{BACf}}}}}{{LJf{{LFb{BEEbAJEh}}{Nn{AOKn}}}}}}\",[]]Am[\"{AIIh{{AIIh{c}}}{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A`[\"{A@@hLFd}\",[]]A`[\"{A@@jLFd}\",[]]A`[\"{A@dAOLb}\",[]]Bc[\"{{AJIjBBL`A@d}{{LJj{AOLd}}}}\",[]]Aj[\"{AJIj{{LJh{AC@n}}}}\",[]]B`[\"{{}{{LJf{HLd{Nn{JIh}}}}}}\",[]]Ak[\"{{{AIIh{AJEf}}}BCKn}\",[]]Be[\"{{{AIIh{ACA`}}}{{AIIh{BBMh}}}}\",[]]o[\"{MnBFI`}\",[]]A`[\"{AJFbCJb}\",[]]o[\"{BACbNf}\",[]]o[\"{MnBFIb}\",[]]A`[\"{AJFdCJb}\",[]]o[\"{BACfNf}\",[]]A`[\"{BCA`LIl}\",[]]Bl[\"{{{AIIh{CCbc}}}{{LJf{LLbBBCh}}}O`}\",[\"B\"]]Bl[\"{{{AIIh{CCbc}}}{{LJf{LJ`BBCh}}}O`}\",[\"B\"]]Bm[\"{{{AIIh{CCbc}}}{{LJf{AHNjBBCh}}}O`}\",[\"B\"]]Bm[\"{{{AIIh{CCbc}}}{{LJf{BFI`BBCh}}}O`}\",[\"B\"]]Bm[\"{{{AIIh{CCbc}}}{{LJf{ACCnBBCh}}}O`}\",[\"B\"]]Bh[\"{{{AIIh{CCbc}}}{{LJn{LLd}}}O`}\",[\"B\"]]Bh[\"{{{AIIh{CCbc}}}{{LJn{LJb}}}O`}\",[\"B\"]]Bi[\"{{{AIIh{CCbc}}}{{LJn{AHNl}}}O`}\",[\"B\"]]Bi[\"{{{AIIh{CCbc}}}{{LJn{AJG`}}}O`}\",[\"B\"]]Ek[\"{{{AIIh{CCb{LHj{ABOh}}}}{AIIh{c}}{AIIh{{LCn{CJ`}}}}CNl}{{LJf{BCMhBFAf}}}{BFEhJLl}}\",[\"\"]]Bi[\"{{{AIIh{CCbc}}}{{LJn{BFIb}}}O`}\",[\"B\"]]Bi[\"{{{AIIh{CCbc}}}{{LJn{ACD`}}}O`}\",[\"B\"]]Bi[\"{{{AIIh{CCbc}}}{{LJn{AHMn}}}O`}\",[\"B\"]]A`[\"{ACCjLIl}\",[]]Bk[\"{{{AIIh{LLb}}{AIIh{CCbc}}}HLdLGj}\",[\"B\"]]Bk[\"{{{AIIh{LJ`}}{AIIh{CCbc}}}HLdLGj}\",[\"B\"]]Bl[\"{{{AIIh{AHNj}}{AIIh{CCbc}}}HLdLGj}\",[\"B\"]]Bl[\"{{{AIIh{BFI`}}{AIIh{CCbc}}}HLdLGj}\",[\"B\"]]Bl[\"{{{AIIh{ACCn}}{AIIh{CCbc}}}HLdLGj}\",[\"B\"]]Bk[\"{{{AIIh{LLd}}{AIIh{CCbc}}}HLdLGj}\",[\"B\"]]Bk[\"{{{AIIh{LJb}}{AIIh{CCbc}}}HLdLGj}\",[\"B\"]]Bl[\"{{{AIIh{AHNl}}{AIIh{CCbc}}}HLdLGj}\",[\"B\"]]Bl[\"{{{AIIh{AJG`}}{AIIh{CCbc}}}HLdLGj}\",[\"B\"]]Bl[\"{{{AIIh{AC@b}}{AIIh{CCbc}}}HLdLGj}\",[\"B\"]]Bl[\"{{{AIIh{BFIb}}{AIIh{CCbc}}}HLdLGj}\",[\"B\"]]Bl[\"{{{AIIh{ACD`}}{AIIh{CCbc}}}HLdLGj}\",[\"B\"]]Bl[\"{{{AIIh{AHNd}}{AIIh{CCbc}}}HLdLGj}\",[\"B\"]]Bj[\"{{{AIIh{CCbAJId}}}{{LJf{HLdB@Ml}}}}\",[]]Bj[\"{{{AIIh{CCbAJIf}}}{{LJf{HLdAOMj}}}}\",[]]An[\"{{{AIIh{B@@b}}AC@j}HLd}\",[]]Ea[\"{{{AIIh{CCbACA`}}OLfAHNh{LIl{B@Oj}}ABOh{AIIh{CCb{AAb{Mn}}}}}{{LIl{BAOd}}}}\",[]]o[\"{CKnOLh}\",[]]o[\"{CL`OLj}\",[]]A`[\"{ACAfHLd}\",[]]Ba[\"{{{AIIh{CCbACA`}}ACAh}HLd}\",[]]Bh[\"{{{AIIh{AJJj}}{AIIh{CIl}}JHb}HLd}\",[]]Bh[\"{{{AIIh{BBOb}}{AIIh{CIl}}JHb}HLd}\",[]]Bh[\"{{{AIIh{BEHh}}{AIIh{CIl}}JHb}HLd}\",[]]Bh[\"{{{AIIh{AJJl}}{AIIh{CIl}}JHb}HLd}\",[]]Bh[\"{{{AIIh{BBOd}}{AIIh{CIl}}JHb}HLd}\",[]]Bh[\"{{{AIIh{BEHj}}{AIIh{CIl}}JHb}HLd}\",[]]A`[\"{AHMfCNl}\",[]]o[\"{JHdCJb}\",[]]o[\"{JHfCJb}\",[]]Ce[\"{{{AIIh{CCbAJEj}}OLfOLfCJbCNl{AIIh{BABh}}}HLd}\",[]]Cc[\"{{{AIIh{CCbNh}}OLfOLfCJbCNl{AIIh{BABh}}}HLd}\",[]]Cd[\"{{{AIIh{CCbJHl}}OLfOLfCJbCNl{AIIh{BABh}}}HLd}\",[]]Cd[\"{{{AIIh{CCbOMb}}OLfOLfCJbCNl{AIIh{BABh}}}HLd}\",[]]Ce[\"{{{AIIh{CCbAJEl}}OLfOLfCJbCNl{AIIh{BABj}}}HLd}\",[]]Cc[\"{{{AIIh{CCbNj}}OLfOLfCJbCNl{AIIh{BABj}}}HLd}\",[]]Cd[\"{{{AIIh{CCbJHn}}OLfOLfCJbCNl{AIIh{BABj}}}HLd}\",[]]Cd[\"{{{AIIh{CCbOMd}}OLfOLfCJbCNl{AIIh{BABj}}}HLd}\",[]]o[\"{CKnOLh}\",[]]o[\"{CL`OLj}\",[]]o[\"{BEIbMn}\",[]]A`[\"{BCKhJHb}\",[]]A`[\"{BEDfJHb}\",[]]A`[\"{BCKlJHb}\",[]]A`[\"{BEDjJHb}\",[]]A`[\"{BCA`LKn}\",[]]Aa[\"{BBLhBCL`}\",[]]Aa[\"{AHKnBFAj}\",[]]Bg[\"{{{AIIh{AC@n}}AIAj}{{LJh{HLd}}}}\",[]]A`[\"{ACAfHLd}\",[]]Cf[\"{{{AIIh{CCbACA`}}ACAh{AIIh{CCb{AAb{Mn}}}}}ACEd}\",[]]l[\"{OLh}\",[]]Aa[\"{AHNhAJIj}\",[]]l[\"{OLj}\",[]]An[\"{{AC@lA@d}{{LJj{HLd}}}}\",[]]B`[\"{{BACbAJIj}{{LJh{AC@n}}}}\",[]]Bb[\"{BAC`{{LJf{HLd{Nn{JIh}}}}}}\",[]]Bk[\"{{{AIIh{BFAl}}}{{LIl{{AIIh{JIh}}}}}}\",[]]Bk[\"{{{AIIh{BC@f}}}{{LIl{{AIIh{JIh}}}}}}\",[]]Bk[\"{{{AIIh{AIAb}}}{{LIl{{AIIh{JIh}}}}}}\",[]]Bk[\"{{{AIIh{AJHl}}}{{LIl{{AIIh{JIh}}}}}}\",[]]Bk[\"{{{AIIh{BC@d}}}{{LIl{{AIIh{JIh}}}}}}\",[]]Bk[\"{{{AIIh{AJK`}}}{{LIl{{AIIh{JIh}}}}}}\",[]]Bk[\"{{{AIIh{BACn}}}{{LIl{{AIIh{JIh}}}}}}\",[]]Bk[\"{{{AIIh{BCKn}}}{{LIl{{AIIh{JIh}}}}}}\",[]]Bk[\"{{{AIIh{BCL`}}}{{LIl{{AIIh{JIh}}}}}}\",[]]A`[\"{ACEbLIl}\",[]]A`[\"{ACEdLIl}\",[]]A`[\"{ACEfLIl}\",[]]Bg[\"{{{AIIh{CLj}}}{{LJf{AJGnAJDh}}}}\",[]]Aa[\"{BABdAJH`}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]A`[\"{ACCjLFd}\",[]]Aa[\"{AI@lACEh}\",[]]Aa[\"{BCLfACEh}\",[]]Aa[\"{AI@nACEj}\",[]]Aa[\"{BCLhACEj}\",[]]3210Dh[\"{{{AIIh{OL`}}{AIIh{{LCn{Mn}}}}{AIIh{{LCn{Mn}}}}}{{LJf{HLdAOLn}}}}\",[]]Dh[\"{{{AIIh{OLb}}{AIIh{{LCn{Mn}}}}{AIIh{{LCn{Mn}}}}}{{LJf{HLdAOM`}}}}\",[]]Aj[\"{{{AIIh{AJEj}}}CJb}\",[]]Ah[\"{{{AIIh{Nh}}}CJb}\",[]]Ai[\"{{{AIIh{JHl}}}CJb}\",[]]Ai[\"{{{AIIh{OMb}}}CJb}\",[]]Aj[\"{{{AIIh{AJEl}}}CJb}\",[]]Ah[\"{{{AIIh{Nj}}}CJb}\",[]]Ai[\"{{{AIIh{JHn}}}CJb}\",[]]Ai[\"{{{AIIh{OMd}}}CJb}\",[]]Aj[\"{{{AIIh{BGLb}}}LFd}\",[]]Aj[\"{{{AIIh{BGLd}}}LFd}\",[]]Aj[\"{{{AIIh{BHLh}}}LFd}\",[]]Aj[\"{{{AIIh{BHMb}}}LFd}\",[]]o[\"{A@dLFd}\",[]]n[\"{{}OKb}\",[]]0o[\"{ObABOl}\",[]]111Ba[\"{{AIIh{AIIh{c}}}ACC`{}}\",[\"K\"]]0000000000000000000Cg[\"{{{AIIh{AC@n}}AJIj{AIIh{CIl}}}{{LJf{AJEbB@Nh}}}}\",[]]Dl[\"{{{AIIh{CCbACA`}}OLfB@MdAJIj{AIIh{CIl}}}{{LJf{{LFb{BEEbAJEh}}B@Nj}}}}\",[]]Bd[\"{{{AIIh{ACAf}}}{{LIl{BEEf}}}}\",[]]Dm[\"{{{AIIh{AHOn}}LJ`CJb{AIIh{{LCn{Mn}}}}{AIIh{CCbABOh}}}{{LJf{HLdAOLn}}}}\",[]]Ca[\"{{{AIIh{AHO`}}LFd{AIIh{CCb{LCn{Mn}}}}}HLd}\",[]]Bd[\"{{{AIIh{ACAh}}}{{LIl{BEEh}}}}\",[]]Dm[\"{{{AIIh{AI@`}}LJbCJb{AIIh{{LCn{Mn}}}}{AIIh{CCbABOh}}}{{LJf{HLdAOM`}}}}\",[]]Ca[\"{{{AIIh{AHOb}}LFd{AIIh{CCb{LCn{Mn}}}}}HLd}\",[]]o[\"{{}B@@`}\",[]]n[\"{{}JLn}\",[]]o[\"{{}AOLd}\",[]]o[\"{{}BANf}\",[]]o[\"{{}BAOh}\",[]]n[\"{{}LLb}\",[]]n[\"{{}LJ`}\",[]]o[\"{{}ACEh}\",[]]o[\"{{}AJFb}\",[]]o[\"{{}AI@l}\",[]]o[\"{{}BCLf}\",[]]o[\"{{}A@@h}\",[]]o[\"{{}B@Ml}\",[]]o[\"{{}BCNl}\",[]]o[\"{{}BFDl}\",[]]o[\"{{}BFFj}\",[]]o[\"{{}AOMl}\",[]]o[\"{{}BBMf}\",[]]o[\"{{}BGMd}\",[]]o[\"{{}BEHh}\",[]]o[\"{{}AHM`}\",[]]o[\"{{}AOMb}\",[]]o[\"{{}BBAl}\",[]]o[\"{{}BF@d}\",[]]o[\"{{}BANh}\",[]]o[\"{{}BHLh}\",[]]n[\"{{}LLd}\",[]]n[\"{{}LJb}\",[]]o[\"{{}AJH`}\",[]]o[\"{{}AJDn}\",[]]o[\"{{}ACEj}\",[]]o[\"{{}AJFd}\",[]]o[\"{{}AI@n}\",[]]o[\"{{}BCLh}\",[]]o[\"{{}A@@j}\",[]]o[\"{{}BC@h}\",[]]o[\"{{}B@Nd}\",[]]o[\"{{}BCNn}\",[]]o[\"{{}BFDn}\",[]]o[\"{{}BFFl}\",[]]o[\"{{}AOMn}\",[]]o[\"{{}BBMh}\",[]]o[\"{{}BGMf}\",[]]o[\"{{}BHMb}\",[]]o[\"{{}BEHj}\",[]]o[\"{{}AHMb}\",[]]o[\"{{}AOMd}\",[]]o[\"{{}BBAn}\",[]]o[\"{{}BF@f}\",[]]o[\"{{}ACCj}\",[]]n[\"{{}JM`}\",[]]o[\"{{}BCM`}\",[]]o[\"{{}BAOf}\",[]]Ak[\"{{{AIIh{BBBb}}}B@O`}\",[]]Ak[\"{{{AIIh{BCMh}}}B@O`}\",[]]Aa[\"{BHAfB@O`}\",[]]Aa[\"{BFIdB@O`}\",[]]A`[\"{CLbB@O`}\",[]]A`[\"{JLdB@O`}\",[]]A`[\"{JLhB@O`}\",[]]Aa[\"{BEIbB@O`}\",[]]Cg[\"{{{AIIh{AHOn}}LJ`CJb{AIIh{CCb{LCn{Mn}}}}LFd}HLd}\",[]]Ca[\"{{{AIIh{AHO`}}LFd{AIIh{CCb{LCn{Mn}}}}}HLd}\",[]]Cg[\"{{{AIIh{AI@`}}LJbCJb{AIIh{CCb{LCn{Mn}}}}LFd}HLd}\",[]]Ca[\"{{{AIIh{AHOb}}LFd{AIIh{CCb{LCn{Mn}}}}}HLd}\",[]]Aj[\"{{{AIIh{AJHh}}}CNl}\",[]]Aj[\"{{{AIIh{BFNn}}}CNl}\",[]]Ak[\"{{{AIIh{AJEj}}}BF@d}\",[]]Ai[\"{{{AIIh{Nh}}}BF@d}\",[]]Aj[\"{{{AIIh{JHl}}}BF@d}\",[]]Aj[\"{{{AIIh{OMb}}}BF@d}\",[]]Ak[\"{{{AIIh{AJEl}}}BF@f}\",[]]Ai[\"{{{AIIh{Nj}}}BF@f}\",[]]Aj[\"{{{AIIh{JHn}}}BF@f}\",[]]Aj[\"{{{AIIh{OMd}}}BF@f}\",[]]Bn[\"{{{AIIh{CCbBCNl}}CIn}{{AIIh{CCbBCNl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNn}}CIn}{{AIIh{CCbBCNn}}}}\",[]]Bf[\"{{{AIIh{CCbAJEj}}OLfCJbCJb}HLd}\",[]]Bd[\"{{{AIIh{CCbNh}}OLfCJbCJb}HLd}\",[]]Bf[\"{{{AIIh{CCbAJEl}}OLfCJbCJb}HLd}\",[]]Bd[\"{{{AIIh{CCbNj}}OLfCJbCJb}HLd}\",[]]Aj[\"{{{AIIh{AJEf}}}LIb}\",[]]Bc[\"{{{AIIh{ACBn}}}{{LIl{LJ`}}}}\",[]]Aa[\"{BHAfB@O`}\",[]]Aa[\"{BFIdB@O`}\",[]]A`[\"{CLbB@O`}\",[]]A`[\"{JLdB@O`}\",[]]Aa[\"{BEIbB@O`}\",[]]Aj[\"{{{AIIh{AJId}}}OOd}\",[]]Bn[\"{{{AIIh{AJIf}}}{{LJf{{LIl{LLd}}B@Nd}}}}\",[]]o[\"{A@dLFd}\",[]]o[\"{JLnLFd}\",[]]Am[\"{{{AIIh{CCbAJEh}}}OOl}\",[]]Aj[\"{{{AIIh{AHOn}}}LFd}\",[]]Aj[\"{{{AIIh{AI@`}}}LFd}\",[]]A`[\"{AIIhLL`}\",[]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bd[\"{{{AIIh{BGFh}}}{{LIl{AJEf}}}}\",[]]Bc[\"{{{AIIh{BCAd}}}{{LIl{CLj}}}}\",[]]Bn[\"{{{AIIh{CCbB@M`}}CJ`}{{AIIh{CCbB@M`}}}}\",[]]Bn[\"{{{AIIh{CCbB@Md}}CJ`}{{AIIh{CCbB@Md}}}}\",[]]A`[\"{BHAfCJ`}\",[]]A`[\"{BFIdCJ`}\",[]]o[\"{CLbCJ`}\",[]]o[\"{JLdCJ`}\",[]]A`[\"{AHMfLIl}\",[]]Aa[\"{ACEfAIIh}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]Aa[\"{BC@jAC@j}\",[]]A`[\"{LGlBC@h}\",[]]Aa[\"{BCLfAJFb}\",[]]Aa[\"{BCLhAJFd}\",[]]10Bg[\"{{{AIIh{CCbc}}LFd}B@Nn{O`JLl}}\",[\"\"]]Bg[\"{{{AIIh{CCbc}}LFd}B@O`{O`JLl}}\",[\"\"]]o[\"{{}BCMj}\",[]]o[\"{{}BCMl}\",[]]A`[\"{CJbBHLh}\",[]]Cd[\"{{{AIIh{CIl}}}{{LJj{AOLbc}}}{}}\",[\"FromStr::Err\"]]o[\"{CJ`LLb}\",[]]o[\"{CJ`LLd}\",[]]Al[\"{CJb{{LJf{LLbBGFb}}}}\",[]]Al[\"{CJb{{LJf{LLdBGFd}}}}\",[]]Aa[\"{BGDlAOLl}\",[]]Aa[\"{BI@fBB@d}\",[]]Aa[\"{BGDnAOLl}\",[]]Aa[\"{BI@hBB@d}\",[]]Af[\"{{}{{LIl{Ab}}}}\",[]]0000Aj[\"{{{AIIh{AJEh}}}CNl}\",[]]Bo[\"{{{AIIh{CCbBFFj}}AC@j}{{AIIh{CCbBFFj}}}}\",[]]Bo[\"{{{AIIh{CCbBFFl}}AC@j}{{AIIh{CCbBFFl}}}}\",[]]Ao[\"{{{Nn{AJEj}}}{{Nn{Nd}}}}\",[]]Am[\"{{{Nn{Nh}}}{{Nn{Nd}}}}\",[]]An[\"{{{Nn{JHl}}}{{Nn{Nd}}}}\",[]]An[\"{{{Nn{OMb}}}{{Nn{Nd}}}}\",[]]Ao[\"{{{Nn{AJEl}}}{{Nn{Nd}}}}\",[]]Am[\"{{{Nn{Nj}}}{{Nn{Nd}}}}\",[]]An[\"{{{Nn{JHn}}}{{Nn{Nd}}}}\",[]]An[\"{{{Nn{OMd}}}{{Nn{Nd}}}}\",[]]Aj[\"{{{AIIh{BGFh}}}CNl}\",[]]Bo[\"{{{AIIh{CCbBGMd}}AC@j}{{AIIh{CCbBGMd}}}}\",[]]Bo[\"{{{AIIh{CCbBGMf}}AC@j}{{AIIh{CCbBGMf}}}}\",[]]Bc[\"{{{AIIh{AJEb}}}{{LIl{LI`}}}}\",[]]Bc[\"{{{AIIh{AJEf}}}{{LIl{LI`}}}}\",[]]Bc[\"{{{AIIh{ACAf}}}{{LIl{LI`}}}}\",[]]Bc[\"{{{AIIh{ACAh}}}{{LIl{LI`}}}}\",[]]A`[\"{AHNhLIl}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]Bc[\"{{{AIIh{AHMl}}}{{LIl{LFd}}}}\",[]]Am[\"{{{AIIh{B@@b}}LFd}HLd}\",[]]Aj[\"{{{AIIh{AJEf}}}OMh}\",[]]Bg[\"{{{AIIh{AJId}}}{{LJf{CAlB@Ml}}}}\",[]]Bg[\"{{{AIIh{AJIf}}}{{LJf{CAlB@Nd}}}}\",[]]A`[\"{BAOlLIl}\",[]]A`[\"{BAOnLIl}\",[]]A`[\"{AHMfLIl}\",[]]A`[\"{AOKnLIl}\",[]]Ck[\"{{{AIIh{CCbBBMf}}{LIl{{JMh{Mn}}}}}{{AIIh{CCbBBMf}}}}\",[]]Ck[\"{{{AIIh{CCbBBMh}}{LIl{{JMh{Mn}}}}}{{AIIh{CCbBBMh}}}}\",[]]A`[\"{BF@dLIl}\",[]]A`[\"{BF@fLIl}\",[]]Ac[\"{AIIhc{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Ao[\"{c{{LJf{e}}}{}{}}\",[\"U\",\"T\"]]0000000000000000000000000000000000000000000000Al[\"{CJb{{LJf{LLbBGFb}}}}\",[]]Al[\"{LFd{{LJf{LLbBGFb}}}}\",[]]2Al[\"{HLb{{LJf{LLbBGFb}}}}\",[]]33333333333333Aj[\"{AC@j{{LJf{AOMl}}}}\",[]]444444Ai[\"{CJb{{LJf{AHNj}}}}\",[]]555555555555555555555555555Bb[\"{{{Nf{B@Ll}}}{{LJf{BEH`}}}}\",[]]6Aj[\"{B@Ll{{LJf{BEH`}}}}\",[]]77Bb[\"{{{Nf{BABn}}}{{LJf{BEHd}}}}\",[]]Aj[\"{BABn{{LJf{BEHd}}}}\",[]]999999999999999999999Al[\"{LFd{{LJf{LLdBGFd}}}}\",[]]:Al[\"{HLb{{LJf{LLdBGFd}}}}\",[]]Al[\"{CJb{{LJf{LLdBGFd}}}}\",[]]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Ce[\"{{{Nf{B@Ll}}}{{LJf{BEHbc}}}{}}\",[\"TryFrom::Error\"]]Bm[\"{B@Ll{{LJf{BEHbc}}}{}}\",[\"TryFrom::Error\"]]Ao[\"{c{{LJf{e}}}{}{}}\",[\"U\",\"T\"]]Bm[\"{BABn{{LJf{BEHfc}}}{}}\",[\"TryFrom::Error\"]]1Ce[\"{{{Nf{BABn}}}{{LJf{BEHfc}}}{}}\",[\"TryFrom::Error\"]]222222222222222222222222Aj[\"{{}{{LJf{c}}}{}}\",[\"U\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cd[\"{{{A@l{{AIIh{CCbc}}}}{AIIh{CCbOKd}}}CMb{}}\",[\"F\"]]000000000Cc[\"{{{AIIh{BCAb}}AJJn{AIIh{ACEf}}}{{LJh{HLd}}}}\",[]]Bk[\"{{{AIIh{BGLb}}B@Nn}{{LJf{HLdAJFh}}}}\",[]]Bk[\"{{{AIIh{BGLd}}B@O`}{{LJf{HLdAJFj}}}}\",[]]0Bk[\"{{{AIIh{BHLh}}B@O`}{{LJf{HLdAJFj}}}}\",[]]Ak[\"{{{AIIh{AJEf}}}ABOf}\",[]]Bi[\"{{{AIIh{CCbBADb}}}{{LJf{LLbJJd}}}}\",[]]Bi[\"{{{AIIh{CCbBADb}}}{{LJf{CMhJJd}}}}\",[]]Bh[\"{{{AIIh{CCbBADb}}}{{LJf{OdJJd}}}}\",[]]Bj[\"{{{AIIh{CCbBADb}}}{{LJf{ACCnJJd}}}}\",[]]Bf[\"{{{AIIh{CCbBADb}}}{{LJl{LLd}}}}\",[]]Bf[\"{{{AIIh{CCbBADb}}}{{LJl{CMj}}}}\",[]]Be[\"{{{AIIh{CCbBADb}}}{{LJl{Of}}}}\",[]]Bg[\"{{{AIIh{CCbBADb}}}{{LJl{ACD`}}}}\",[]]Aa[\"{AOLbBGF`}\",[]]Bc[\"{{{AIIh{AJEj}}}{{Nn{AJEj}}}}\",[]]Ba[\"{{{AIIh{Nh}}}{{Nn{AJEj}}}}\",[]]Bb[\"{{{AIIh{JHl}}}{{Nn{AJEj}}}}\",[]]Bb[\"{{{AIIh{OMb}}}{{Nn{AJEj}}}}\",[]]Bc[\"{{{AIIh{AJEl}}}{{Nn{AJEl}}}}\",[]]Ba[\"{{{AIIh{Nj}}}{{Nn{AJEl}}}}\",[]]Bb[\"{{{AIIh{JHn}}}{{Nn{AJEl}}}}\",[]]Bb[\"{{{AIIh{OMd}}}{{Nn{AJEl}}}}\",[]]A`[\"{ACEhCJb}\",[]]An[\"{{{AIIh{CCbAJEh}}}AHMl}\",[]]A`[\"{ACEjCJb}\",[]]C`[\"{{{AIIh{CCbB@Nn}}}{{AIIh{CCb{LCn{Mn}}}}}}\",[]]C`[\"{{{AIIh{CCbB@O`}}}{{AIIh{CCb{LCn{Mn}}}}}}\",[]]Ah[\"{Mn{{LIl{B@Oh}}}}\",[]]Ah[\"{Mn{{LIl{B@Oj}}}}\",[]]Ah[\"{Mn{{LIl{B@Ol}}}}\",[]]A`[\"{ACCnCMh}\",[]]A`[\"{ACD`CMj}\",[]]Bi[\"{AJEb{{LJf{{LFb{AJEfBCOf}}AJEb}}}}\",[]]Aa[\"{{}c{}}\",[\"I\"]]o[\"{CMhCNl}\",[]]o[\"{CMjCNl}\",[]]Aj[\"{{{AIIh{AJEh}}}CNl}\",[]]21Aj[\"{{{AIIh{ACAf}}}CNl}\",[]]Aj[\"{{{AIIh{ACAh}}}CNl}\",[]]Bn[\"{{{AIIh{CCbBACb}}CNl}{{AIIh{CCbBACb}}}}\",[]]Bn[\"{{{AIIh{CCbBACf}}CNl}{{AIIh{CCbBACf}}}}\",[]]Bl[\"{{{AIIh{ONj}}OLf}{{A@l{{Nn{AJD`}}}}}}\",[]]Bm[\"{{{AIIh{BAD`}}OLf}{{A@l{{Nn{AJD`}}}}}}\",[]]Bm[\"{{{AIIh{AOOf}}OLf}{{A@l{{Nn{AJD`}}}}}}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]Ak[\"{{{AIIh{AJEf}}}ACBl}\",[]]Bc[\"{{{AIIh{AJEf}}AJIjAJGn}ACBn}\",[]]Ce[\"{{{AIIh{CCbAJEh}}AHNhAJH`OLf}{{LJf{LJbAI@d}}}}\",[]]43Bm[\"{{{AIIh{CCbAJEh}}LJb}{{LJf{HLdAJDn}}}}\",[]]E`[\"{{{A@l{{AIIh{CCbBEDb}}}}{AIIh{CCbOKd}}}{{CMb{{LIl{c}}}}}{}}\",[\"Stream::Item\"]]Ef[\"{{{A@l{{AIIh{CCbAJHh}}}}{AIIh{CCbOKd}}{AIIh{CCb{LCn{Mn}}}}}{{CMb{{LJh{LFd}}}}}}\",[]]E`[\"{{{A@l{{AIIh{CCbAJHh}}}}{AIIh{CCbOKd}}{AIIh{CCbONd}}}{{CMb{{LJh{HLd}}}}}}\",[]]Ec[\"{{{AIIh{CCbAJHh}}{AIIh{CCbOKd}}{AIIh{CCb{LCn{Mn}}}}}{{CMb{{LJf{LFdAIAb}}}}}}\",[]]Fg[\"{{{AIIh{CCbBBKn}}{AIIh{CCbOKd}}{AIIh{CCb{LCn{AJFl}}}}{AIIh{CCb{LCn{ACCj}}}}}{{CMb{{LJh{LFd}}}}}}\",[]]Dn[\"{{{A@l{{AIIh{CCbAIAh}}}}{AIIh{ACEf}}{AIIh{CCbOKd}}}{{CMb{{LJh{HLd}}}}}}\",[]];:Cd[\"{{{AIIh{CCbBBMf}}{Nf{OL`}}}{{AIIh{CCbBBMf}}}}\",[]]Cd[\"{{{AIIh{CCbBBMh}}{Nf{OLb}}}{{AIIh{CCbBBMh}}}}\",[]]Cj[\"{{{AIIh{BACd}}CJ`B@Nn{AIIh{{LCn{Mn}}}}}{{JMh{Mn}}}}\",[]]Cj[\"{{{AIIh{BEHd}}CJ`B@Nn{AIIh{{LCn{Mn}}}}}{{JMh{Mn}}}}\",[]]Cj[\"{{{AIIh{BACh}}CJ`B@O`{AIIh{{LCn{Mn}}}}}{{JMh{Mn}}}}\",[]]Cj[\"{{{AIIh{BEHf}}CJ`B@O`{AIIh{{LCn{Mn}}}}}{{JMh{Mn}}}}\",[]]Cj[\"{{{AIIh{BHGb}}CJ`B@Nl{AIIh{{LCn{Mn}}}}}{{JMh{Mn}}}}\",[]]Bb[\"{LFd{{LFb{LFd{LIl{LFd}}}}}}\",[]]000Aj[\"{{{AIIh{AJEf}}}LFd}\",[]]A`[\"{AIIhLKn}\",[]]0000000000000000000000000000000000000000000000000000000000000Ce[\"{{{AIIh{CCbBACb}}{Nf{BF@h}}}{{AIIh{CCbBACb}}}}\",[]]Ce[\"{{{AIIh{CCbBACf}}{Nf{BF@j}}}{{AIIh{CCbBACf}}}}\",[]]A`[\"{BHAfJKj}\",[]]o[\"{BACbNf}\",[]]o[\"{BACfNf}\",[]]Aj[\"{{{AIIh{AC@n}}}HLd}\",[]]Ck[\"{{{AIIh{CCbAJId}}{AIIh{{LCn{Mn}}}}}{{LJf{HLdAJK`}}}}\",[]]n[\"{{}LFd}\",[]]Ak[\"{{{AIIh{AJEf}}}AHLb}\",[]]Bn[\"{{{AIIh{CCbBCNl}}CNl}{{AIIh{CCbBCNl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNn}}CNl}{{AIIh{CCbBCNn}}}}\",[]]Bi[\"{{{AIIh{CCb}}}{{AIIh{CCbc}}}{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bc[\"{{AIIh{AIIh{CCbc}}}HLd{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cc[\"{{{AIIh{CCbAJEh}}OLfLJbLLd}{{LJf{HLdBBLd}}}}\",[]]A`[\"{AHMfCNl}\",[]]B`[\"{{AIIh{AIIh{c}}}CNl{}}\",[\"K\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Aa[\"{BCKhBFI`}\",[]]A`[\"{BEDfLLb}\",[]]Aa[\"{BCKlBFIb}\",[]]A`[\"{BEDjLLd}\",[]]A`[\"{BEOhLLd}\",[]]A`[\"{BCMnLLd}\",[]]o[\"{OO`LLd}\",[]]Aa[\"{BCKhAJFn}\",[]]Aa[\"{BCKlAJG`}\",[]]o[\"{LLbCJb}\",[]]o[\"{LLdCJb}\",[]]Aj[\"{{{AIIh{AJEh}}}CNl}\",[]]Aj[\"{{{AIIh{BAOf}}}CNl}\",[]]Bd[\"{{{AIIh{BBKn}}}{{LJh{AJIj}}}}\",[]]Bd[\"{{{AIIh{AC@n}}}{{LJh{AJIj}}}}\",[]]A`[\"{AI@lCJb}\",[]]A`[\"{AI@nCJb}\",[]]Cb[\"{{{AIIh{BCM`}}{AIIh{AJIb}}CJb}{{Nf{B@@b}}}}\",[]]Bn[\"{{{AIIh{CCbBCNl}}CNl}{{AIIh{CCbBCNl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNn}}CNl}{{AIIh{CCbBCNn}}}}\",[]]Bg[\"{{{AIIh{AJEf}}LJ`}{{LIl{AI@l}}}}\",[]]Bj[\"{{{AIIh{CCbAJEh}}LJb}{{LIl{AI@n}}}}\",[]]Aa[\"{AHN`AI@n}\",[]]Db[\"{{{A@l{{AIIh{CCbAJId}}}}{AIIh{CCbOKd}}}{{CMb{{LJh{HLd}}}}}}\",[]]00Ec[\"{{{A@l{{AIIh{CCbAJId}}}}{AIIh{CCbOKd}}{AIIh{{LCn{Mn}}}}}{{CMb{{LJh{LFd}}}}}}\",[]]Eg[\"{{{A@l{{AIIh{CCbAJId}}}}{AIIh{CCbOKd}}{AIIh{{LCn{Mn}}}}}{{CMb{{LJf{LFdAJK`}}}}}}\",[]]1Bm[\"{{{AIIh{JM`}}{AIIh{CLh}}}{{LJh{HLd}}}}\",[]]Cd[\"{{{AIIh{CCbAJHh}}LFd}{{LJf{{LIl{JHd}}AIAb}}}}\",[]]Cd[\"{{{AIIh{CCbBFNn}}LFd}{{LJf{{LIl{JHd}}AIAb}}}}\",[]]Cn[\"{{{AIIh{CCbAJHh}}{AIIh{CCb{LCn{Mn}}}}}{{LJf{HLdBC@d}}}}\",[]]Bj[\"{{{AIIh{CLj}}AJGn}{{LJf{HLdBFHh}}}}\",[]]Bn[\"{{{AIIh{CCbAJHb}}OLf}{{AIIh{CCbAJHb}}}}\",[]]Bn[\"{{{AIIh{CCbAJHd}}OLf}{{AIIh{CCbAJHd}}}}\",[]]A`[\"{BC@jAE`}\",[]]o[\"{JLnCJb}\",[]]o[\"{{}AJIj}\",[]]0o[\"{{}AIIh}\",[]]Bj[\"{{ACAf{Nf{BACb}}}{{LJf{AJEbBCKn}}}}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]Bh[\"{{{AIIh{AHMf}}AJIj}{{LJj{AIAj}}}}\",[]]A`[\"{AI@lCIn}\",[]]Aj[\"{{{AIIh{AJEh}}}CIn}\",[]]A`[\"{AI@nCIn}\",[]]Ce[\"{{{AIIh{CCbAJHb}}{LIl{LKn}}}{{AIIh{CCbAJHb}}}}\",[]]Ce[\"{{{AIIh{CCbAJHd}}{LIl{LKn}}}{{AIIh{CCbAJHd}}}}\",[]]Aa[\"{ACEbAJIj}\",[]]Aa[\"{ACEdAJIj}\",[]]Aa[\"{ACEfAJIj}\",[]]o[\"{A@dCIn}\",[]]Bn[\"{{{AIIh{CCbBCNl}}CIn}{{AIIh{CCbBCNl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNn}}CIn}{{AIIh{CCbBCNn}}}}\",[]]A`[\"{AHMfCIn}\",[]]Bo[\"{{{AIIh{CCbBCNl}}AC@j}{{AIIh{CCbBCNl}}}}\",[]]Bo[\"{{{AIIh{CCbBCNn}}AC@j}{{AIIh{CCbBCNn}}}}\",[]]A`[\"{AHMfLIl}\",[]]k[\"{{}}\",[]]0000000000Bh[\"{ACAfc{}}\",[\"IntoFuture::IntoFuture\"]]11A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]o[\"{A@dLFd}\",[]]Ao[\"{{AIIhc}{{OLl{c}}}{}}\",[\"S\"]]0C`[\"{{{AIIh{CCbAJEj}}OLfCJbCNl{LIl{CJb}}}HLd}\",[]]Bn[\"{{{AIIh{CCbNh}}OLfCJbCNl{LIl{CJb}}}HLd}\",[]]C`[\"{{{AIIh{CCbAJEl}}OLfCJbCNl{LIl{CJb}}}HLd}\",[]]Bn[\"{{{AIIh{CCbNj}}OLfCJbCNl{LIl{CJb}}}HLd}\",[]]Bk[\"{{{AIIh{CCbJM`}}OLf{AIIh{BCM`}}}HLd}\",[]]A`[\"{BF@dLIl}\",[]]A`[\"{BF@fLIl}\",[]]Bn[\"{{{AIIh{LLb}}{AIIh{LLb}}}{{LIl{ACC`}}}}\",[]]Bn[\"{{{AIIh{LJ`}}{AIIh{LJ`}}}{{LIl{ACC`}}}}\",[]]C`[\"{{{AIIh{AOMl}}{AIIh{AOMl}}}{{LIl{ACC`}}}}\",[]]C`[\"{{{AIIh{B@Nn}}{AIIh{B@Nn}}}{{LIl{ACC`}}}}\",[]]Bn[\"{{{AIIh{CMh}}{AIIh{CMh}}}{{LIl{ACC`}}}}\",[]]Bl[\"{{{AIIh{Od}}{AIIh{Od}}}{{LIl{ACC`}}}}\",[]]C`[\"{{{AIIh{ACCn}}{AIIh{ACCn}}}{{LIl{ACC`}}}}\",[]]Bn[\"{{{AIIh{LLd}}{AIIh{LLd}}}{{LIl{ACC`}}}}\",[]]C`[\"{{{AIIh{BFAn}}{AIIh{BFAn}}}{{LIl{ACC`}}}}\",[]]Bn[\"{{{AIIh{LJb}}{AIIh{LJb}}}{{LIl{ACC`}}}}\",[]]C`[\"{{{AIIh{AIAd}}{AIIh{AIAd}}}{{LIl{ACC`}}}}\",[]]C`[\"{{{AIIh{BBBl}}{AIIh{BBBl}}}{{LIl{ACC`}}}}\",[]]C`[\"{{{AIIh{AJKb}}{AIIh{AJKb}}}{{LIl{ACC`}}}}\",[]]C`[\"{{{AIIh{AOMn}}{AIIh{AOMn}}}{{LIl{ACC`}}}}\",[]]C`[\"{{{AIIh{BEEb}}{AIIh{BEEb}}}{{LIl{ACC`}}}}\",[]]C`[\"{{{AIIh{BFAf}}{AIIh{BFAf}}}{{LIl{ACC`}}}}\",[]]C`[\"{{{AIIh{B@O`}}{AIIh{B@O`}}}{{LIl{ACC`}}}}\",[]]Bn[\"{{{AIIh{CMj}}{AIIh{CMj}}}{{LIl{ACC`}}}}\",[]]Bl[\"{{{AIIh{Of}}{AIIh{Of}}}{{LIl{ACC`}}}}\",[]]C`[\"{{{AIIh{ACD`}}{AIIh{ACD`}}}{{LIl{ACC`}}}}\",[]]Be[\"{{{AIIh{AJEf}}}{{ACCf{AI@h}}}}\",[]]Bk[\"{{{AIIh{AJEh}}LJb}{{LJf{AJH`AJDn}}}}\",[]]Df[\"{{{AIIh{CCbAJHh}}{AIIh{CCb{LCn{JHb}}}}}{{LJf{{LIl{LFd}}AIAb}}}}\",[]]Cc[\"{{{AIIh{CCbAJHh}}LFd}{{LJf{{AAb{Mn}}BC@f}}}}\",[]]Bb[\"{{{AIIh{CCbAJEh}}ACD`}AJHj}\",[]]Aj[\"{{{AIIh{AHO`}}}LFd}\",[]]Aj[\"{{{AIIh{AHOb}}}LFd}\",[]]Bb[\"{{{AIIh{CCbAJEh}}ACD`}AJIf}\",[]]Bn[\"{{{AIIh{CCbBCNl}}CJb}{{AIIh{CCbBCNl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNn}}CJb}{{AIIh{CCbBCNn}}}}\",[]]A`[\"{AHMfLIl}\",[]]A`[\"{BAOlLIl}\",[]]A`[\"{BAOnLIl}\",[]]Ce[\"{{{AIIh{CCbBACb}}{Nf{AJJb}}}{{AIIh{CCbBACb}}}}\",[]]Ce[\"{{{AIIh{CCbBACf}}{Nf{AJJd}}}{{AIIh{CCbBACf}}}}\",[]]Ce[\"{{{AIIh{CCbB@M`}}{Nf{AJJj}}}{{AIIh{CCbB@M`}}}}\",[]]Ce[\"{{{AIIh{CCbB@Md}}{Nf{AJJl}}}{{AIIh{CCbB@Md}}}}\",[]]o[\"{A@dCJb}\",[]]Bn[\"{{{AIIh{CCbBFFj}}CIn}{{AIIh{CCbBFFj}}}}\",[]]Bn[\"{{{AIIh{CCbBFFl}}CIn}{{AIIh{CCbBFFl}}}}\",[]]Ak[\"{{{AIIh{AJEf}}}BGFh}\",[]]Aj[\"{{{AIIh{CLj}}}BCAd}\",[]]Ai[\"{{{Nf{BACd}}}BACb}\",[]]Ai[\"{{{Nf{BACh}}}BACf}\",[]]Aj[\"{{BCMjc}BCMjACDh}\",[\"\"]]Aj[\"{{BCMlc}BCMlACDh}\",[\"\"]]Bm[\"{{{AIIh{CCbAJId}}JHb}{{LJf{HLdAJK`}}}}\",[]]n[\"{{}LI`}\",[]]o[\"{OKbOKb}\",[]]0000Bd[\"{{{AIIh{BGLb}}}{{LIl{AC@j}}}}\",[]]Bd[\"{{{AIIh{BGLd}}}{{LIl{AC@j}}}}\",[]]Bd[\"{{{AIIh{BHLh}}}{{LIl{AC@j}}}}\",[]]Bd[\"{{{AIIh{BHMb}}}{{LIl{AC@j}}}}\",[]]Bd[\"{{{AIIh{AJEf}}}{{LIl{BCKn}}}}\",[]]Ck[\"{{{AIIh{AC@n}}B@M`AJIj{AIIh{CIl}}}{{LJf{AJEbB@Nh}}}}\",[]]Ak[\"{{{AIIh{BABj}}}AC@j}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]An[\"{{AJHfCNl}{{LJj{LFd}}}}\",[]]Cj[\"{{{AIIh{ONl}}}{{LIl{{LFb{{Nn{AHO`}}{Nn{AHOn}}}}}}}}\",[]]Ck[\"{{{AIIh{AJJf}}}{{LIl{{LFb{{Nn{AHO`}}{Nn{AHOn}}}}}}}}\",[]]Cj[\"{{{AIIh{ONn}}}{{LIl{{LFb{{Nn{AHOb}}{Nn{AI@`}}}}}}}}\",[]]Ck[\"{{{AIIh{AJJh}}}{{LIl{{LFb{{Nn{AHOb}}{Nn{AI@`}}}}}}}}\",[]]A`[\"{BGDlAAb}\",[]]A`[\"{BI@fAAb}\",[]]A`[\"{BGDnAAb}\",[]]A`[\"{BI@hAAb}\",[]]An[\"{{{AIIh{CCbBGLb}}}B@Nn}\",[]]An[\"{{{AIIh{CCbBGLd}}}B@O`}\",[]]An[\"{{{AIIh{CCbBHLh}}}B@O`}\",[]]An[\"{{{AIIh{CCbBHMb}}}B@O`}\",[]]Ak[\"{{{AIIh{BCAb}}}BAA`}\",[]]Ba[\"{{{AIIh{CCbAJEh}}BCLd}HLd}\",[]]Bo[\"{{{AIIh{CCbACA`}}BEEbBAOf}{{LIl{BCLd}}}}\",[]]B`[\"{{{AIIh{ONl}}B@NnCMh}CKn}\",[]]Bn[\"{{{AIIh{BACd}}CJ`B@Nn}{{LJf{CKnBFIh}}}}\",[]]Ba[\"{{{AIIh{AJJf}}B@NnCMh}CKn}\",[]]Bn[\"{{{AIIh{BEHd}}CJ`B@Nn}{{LJf{CKnBFIh}}}}\",[]]B`[\"{{{AIIh{ONn}}B@O`CMj}CL`}\",[]]Bn[\"{{{AIIh{BACh}}CJ`B@O`}{{LJf{CL`BFIn}}}}\",[]]Ba[\"{{{AIIh{AJJh}}B@O`CMj}CL`}\",[]]Bn[\"{{{AIIh{BEHf}}CJ`B@O`}{{LJf{CL`BFIn}}}}\",[]]Bn[\"{{{AIIh{BHGb}}CJ`B@Nl}{{LJf{CL`BFIn}}}}\",[]]Aj[\"{{}{{ONf{c}}}{}}\",[\"T\"]]0A`[\"{AI@lCJb}\",[]]A`[\"{AI@nCJb}\",[]]Bn[\"{{{AIIh{CCbBACb}}LFd}{{AIIh{CCbBACb}}}}\",[]]Bn[\"{{{AIIh{CCbBACf}}LFd}{{AIIh{CCbBACf}}}}\",[]]Aj[\"{{{AIIh{BBKn}}}CNl}\",[]]0Aj[\"{{{AIIh{BCAb}}}CNl}\",[]]Bk[\"{{{AIIh{AJEh}}LJb}{{LJf{AHNhAJDn}}}}\",[]]Cb[\"{{{AIIh{BCM`}}{AIIh{AJHf}}CJb}{{Nf{B@@b}}}}\",[]]Ak[\"{{{AIIh{ACAf}}}B@Nn}\",[]]Ak[\"{{{AIIh{ACAh}}}B@O`}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]Bf[\"{{{AIIh{CCbAJEh}}}{{LIl{OLf}}}}\",[]]Ce[\"{{{AIIh{CCbBCNl}}{Nf{AONj}}}{{AIIh{CCbBCNl}}}}\",[]]Ce[\"{{{AIIh{CCbBCNn}}{Nf{AONl}}}{{AIIh{CCbBCNn}}}}\",[]]43A`[\"{ACEbLIl}\",[]]A`[\"{ACEdLIl}\",[]]A`[\"{ACEfLIl}\",[]]Ai[\"{{{AIIh{OOl}}}LFd}\",[]]Bo[\"{{{AIIh{CCbBHLh}}AC@j}{{AIIh{CCbBHLh}}}}\",[]]Bo[\"{{{AIIh{CCbBHMb}}AC@j}{{AIIh{CCbBHMb}}}}\",[]]Bj[\"{{{AIIh{AJId}}CAl}{{LJf{HLdB@Ml}}}}\",[]]Bm[\"{{{AIIh{CCbAJIf}}CAl}{{LJf{HLdB@Nd}}}}\",[]]<;A`[\"{JLnB@@`}\",[]]A`[\"{AOLdJLn}\",[]]Bi[\"{{{Nf{B@Ll}}JMb}{{LJf{BEH`BGEj}}}}\",[]]Bi[\"{{{Nf{BABn}}JMb}{{LJf{BEHdBGEj}}}}\",[]]Bi[\"{{{Nf{B@Ll}}JMb}{{LJf{BEHbBGEl}}}}\",[]]Bi[\"{{{Nf{BABn}}JMb}{{LJf{BEHfBGEl}}}}\",[]]D`[\"{{{AIIh{CCbAJId}}{AIIh{CCb{LCn{JHb}}}}}{{LJf{A@@hAJK`}}}}\",[]]D`[\"{{{AIIh{CCbAJIf}}{AIIh{CCb{LCn{JHb}}}}}{{LJf{A@@jAJKb}}}}\",[]]Aj[\"{{{AIIh{AJEh}}}CNl}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]A`[\"{AHMfCNl}\",[]]E`[\"{{{AIIh{CCbBBMf}}c}{{AIIh{CCbBBMf}}}{{A`{}{{LIn{{Nn{BGLb}}}}}}CMfCMl}}\",[\"F\"]]E`[\"{{{AIIh{CCbBBMh}}c}{{AIIh{CCbBBMh}}}{{A`{}{{LIn{{Nn{BGLd}}}}}}CMfCMl}}\",[\"F\"]]Bj[\"{{{AIIh{BBKn}}}{{A@l{{Nn{AIAh}}}}}}\",[]]o[\"{A@dCJb}\",[]]Aa[\"{B@@`AHOd}\",[]]76Aa[\"{AJHnACAf}\",[]]Aa[\"{AJI`ACAh}\",[]]Cf[\"{{{AIIh{CCbBFDl}}{LIl{AC@j}}}{{AIIh{CCbBFDl}}}}\",[]]Cf[\"{{{AIIh{CCbBFDn}}{LIl{AC@j}}}{{AIIh{CCbBFDn}}}}\",[]]9;:An[\"{{{AIIh{B@@b}}AC@j}HLd}\",[]]B`[\"{{{AIIh{CCbAJEj}}CIn}HLd}\",[]]An[\"{{{AIIh{CCbNh}}CIn}HLd}\",[]]Ao[\"{{{AIIh{CCbJHl}}CIn}HLd}\",[]]Ao[\"{{{AIIh{CCbOMb}}CIn}HLd}\",[]]B`[\"{{{AIIh{CCbAJEl}}CIn}HLd}\",[]]An[\"{{{AIIh{CCbNj}}CIn}HLd}\",[]]Ao[\"{{{AIIh{CCbJHn}}CIn}HLd}\",[]]Ao[\"{{{AIIh{CCbOMd}}CIn}HLd}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]10Bh[\"{{{AIIh{AJEf}}}{{LIl{{Nn{Nd}}}}}}\",[]]Bg[\"{{{AIIh{ONl}}}{{LIl{{Nn{Nd}}}}}}\",[]]Bh[\"{{{AIIh{AJJf}}}{{LIl{{Nn{Nd}}}}}}\",[]]Bg[\"{{{AIIh{ONn}}}{{LIl{{Nn{Nd}}}}}}\",[]]Bh[\"{{{AIIh{AJJh}}}{{LIl{{Nn{Nd}}}}}}\",[]]Dm[\"{{{AIIh{CCbAJHh}}{AIIh{CCbOKd}}{AIIh{CCbONd}}}{{CMb{{LJf{HLdAIAb}}}}}}\",[]]Db[\"{{{A@l{{AIIh{CCbAJId}}}}{AIIh{CCbOKd}}}{{CMb{{LJh{HLd}}}}}}\",[]]Db[\"{{{AIIh{CCbAJEh}}OLfBAA`{AIIh{CCb{AAb{Mn}}}}}{{LIl{ACEd}}}}\",[]]Cf[\"{{{AIIh{CCbBCNl}}{AIIh{CIl}}}{{AIIh{CCbBCNl}}}}\",[]]Cf[\"{{{AIIh{CCbBCNn}}{AIIh{CIl}}}{{AIIh{CCbBCNn}}}}\",[]]Ak[\"{{{AIIh{AJEf}}}BABb}\",[]]Bj[\"{{{AIIh{AJEf}}JHb}{{LJf{HLdBFAl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNl}}CNl}{{AIIh{CCbBCNl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNn}}CNl}{{AIIh{CCbBCNn}}}}\",[]]Ai[\"{{{AIIh{OL`}}}LFd}\",[]]Ai[\"{{{AIIh{OLb}}}LFd}\",[]]Dg[\"{{{AIIh{B@Mb}}CJ`{AIIh{CIl}}{AIIh{BFNl}}}{{LJf{{Nn{ONl}}B@Nh}}}}\",[]]Ca[\"{{{AIIh{BACd}}CJ`{AIIh{BFNl}}}{{Nn{ONl}}}}\",[]]Dg[\"{{{AIIh{BEH`}}CJ`{AIIh{CIl}}{AIIh{BFNl}}}{{LJf{{Nn{ONl}}B@Nh}}}}\",[]]Ca[\"{{{AIIh{BEHd}}CJ`{AIIh{BFNl}}}{{Nn{ONl}}}}\",[]]Dg[\"{{{AIIh{B@Mf}}CJ`{AIIh{CIl}}{AIIh{BFNl}}}{{LJf{{Nn{ONn}}B@Nj}}}}\",[]]Ca[\"{{{AIIh{BACh}}CJ`{AIIh{BFNl}}}{{Nn{ONn}}}}\",[]]Dg[\"{{{AIIh{BEHb}}CJ`{AIIh{CIl}}{AIIh{BFNl}}}{{LJf{{Nn{ONn}}B@Nj}}}}\",[]]Ca[\"{{{AIIh{BEHf}}CJ`{AIIh{BFNl}}}{{Nn{ONn}}}}\",[]]Dg[\"{{{AIIh{BHG`}}CJ`{AIIh{CIl}}{AIIh{BFNl}}}{{LJf{{Nn{ONn}}B@Nf}}}}\",[]]Ca[\"{{{AIIh{BHGb}}CJ`{AIIh{BFNl}}}{{Nn{ONn}}}}\",[]]Bn[\"{{AJIjBBL`A@d}{{LJj{{LFb{AC@lAJEd}}}}}}\",[]]Bd[\"{{{AIIh{AJEh}}}{{AIIh{ONn}}}}\",[]]A`[\"{AOLdJLn}\",[]]Cb[\"{{{AIIh{AONj}}CMhAJIjB@NnOLf}{{LIl{AJHb}}}}\",[]]Cb[\"{{{AIIh{BCMj}}CMhAJIjB@NnOLf}{{LIl{AJHb}}}}\",[]]Cb[\"{{{AIIh{AONl}}CMjAJIjB@O`OLf}{{LIl{AJHd}}}}\",[]]Cb[\"{{{AIIh{BCMl}}CMjAJIjB@O`OLf}{{LIl{AJHd}}}}\",[]]B`[\"{{{AIIh{CCbAJEh}}OLf}HLd}\",[]]Bo[\"{{{AIIh{CCbAJEb}}}{{LJf{{Nn{Nd}}BCKn}}}}\",[]]Bh[\"{{{AIIh{AJEf}}}{{LIl{{Nn{Nd}}}}}}\",[]]Bg[\"{{{AIIh{ONl}}}{{LIl{{Nn{Nd}}}}}}\",[]]Bh[\"{{{AIIh{AJJf}}}{{LIl{{Nn{Nd}}}}}}\",[]]Bg[\"{{{AIIh{ONn}}}{{LIl{{Nn{Nd}}}}}}\",[]]Bh[\"{{{AIIh{AJJh}}}{{LIl{{Nn{Nd}}}}}}\",[]]o[\"{AJFbMn}\",[]]o[\"{AJFdMn}\",[]]Ai[\"{{{AIIh{CIl}}}CNl}\",[]]Aj[\"{{{AIIh{AJEj}}}CJb}\",[]]Ah[\"{{{AIIh{Nh}}}CJb}\",[]]Bn[\"{{{AIIh{CCbAHM`}}CJb}{{AIIh{CCbAHM`}}}}\",[]]Ai[\"{{{AIIh{JHl}}}CJb}\",[]]Bn[\"{{{AIIh{CCbAOMb}}CJb}{{AIIh{CCbAOMb}}}}\",[]]Ai[\"{{{AIIh{OMb}}}CJb}\",[]]Bn[\"{{{AIIh{CCbBBAl}}CJb}{{AIIh{CCbBBAl}}}}\",[]]Aj[\"{{{AIIh{AJEl}}}CJb}\",[]]Ah[\"{{{AIIh{Nj}}}CJb}\",[]]Bn[\"{{{AIIh{CCbAHMb}}CJb}{{AIIh{CCbAHMb}}}}\",[]]Ai[\"{{{AIIh{JHn}}}CJb}\",[]]Bn[\"{{{AIIh{CCbAOMd}}CJb}{{AIIh{CCbAOMd}}}}\",[]]Ai[\"{{{AIIh{OMd}}}CJb}\",[]]Bn[\"{{{AIIh{CCbBBAn}}CJb}{{AIIh{CCbBBAn}}}}\",[]]Aa[\"{AJHhBFNn}\",[]]Ai[\"{{{AIIh{ONl}}}CNl}\",[]]Aj[\"{{{AIIh{AJJf}}}CNl}\",[]]Aj[\"{{{AIIh{AJEh}}}CNl}\",[]]Ai[\"{{{AIIh{ONn}}}CNl}\",[]]Aj[\"{{{AIIh{AJJh}}}CNl}\",[]]Co[\"{{{AIIh{ONl}}B@Nn{AIIh{{LCn{Mn}}}}{AIIh{{LCn{Mn}}}}}CNl}\",[]]D`[\"{{{AIIh{AJJf}}B@Nn{AIIh{{LCn{Mn}}}}{AIIh{{LCn{Mn}}}}}CNl}\",[]]Co[\"{{{AIIh{ONn}}B@O`{AIIh{{LCn{Mn}}}}{AIIh{{LCn{Mn}}}}}CNl}\",[]]D`[\"{{{AIIh{AJJh}}B@O`{AIIh{{LCn{Mn}}}}{AIIh{{LCn{Mn}}}}}CNl}\",[]]Bn[\"{{{AIIh{CCbBFFj}}CIn}{{AIIh{CCbBFFj}}}}\",[]]Bn[\"{{{AIIh{CCbBFFl}}CIn}{{AIIh{CCbBFFl}}}}\",[]]Cc[\"{{{AIIh{CCbONl}}}{{LIl{{OLh{{Nn{AHOn}}}}}}}}\",[]]Cd[\"{{{AIIh{CCbAJJf}}}{{LIl{{OLh{{Nn{AHOn}}}}}}}}\",[]]Cc[\"{{{AIIh{CCbONn}}}{{LIl{{OLj{{Nn{AI@`}}}}}}}}\",[]]Cd[\"{{{AIIh{CCbAJJh}}}{{LIl{{OLj{{Nn{AI@`}}}}}}}}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]De[\"{{{AIIh{CCbBCNl}}c{AIIh{CIl}}}{{AIIh{CCbBCNl}}}{{JH`{CLh}}}}\",[\"\"]]De[\"{{{AIIh{CCbBCNn}}c{AIIh{CIl}}}{{AIIh{CCbBCNn}}}{{JH`{CLh}}}}\",[\"\"]]Ci[\"{{{AIIh{CCbONl}}{AIIh{{LCn{Mn}}}}}{{LJf{CNlJJ`}}}}\",[]]Cj[\"{{{AIIh{CCbAJJf}}{AIIh{{LCn{Mn}}}}}{{LJf{CNlJJ`}}}}\",[]]Cj[\"{{{AIIh{CCbONn}}{AIIh{{LCn{Mn}}}}}{{LJf{CNlBCA`}}}}\",[]]Ck[\"{{{AIIh{CCbAJJh}}{AIIh{{LCn{Mn}}}}}{{LJf{CNlBCA`}}}}\",[]]o[\"{A@dCNl}\",[]]Bn[\"{{{AIIh{CCbBCNl}}LLb}{{AIIh{CCbBCNl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNn}}LLd}{{AIIh{CCbBCNn}}}}\",[]]A`[\"{AHMfLIl}\",[]]Ca[\"{{{AIIh{CCbAJHh}}}{{LJf{{LIl{LLb}}AJHl}}}}\",[]]Ca[\"{{{AIIh{CCbBFNn}}}{{LJf{{LIl{LLb}}AJHl}}}}\",[]]Ca[\"{{{AIIh{CCbAJHj}}}{{LJf{{LIl{LLd}}B@Nd}}}}\",[]]Ak[\"{{{AIIh{AJEb}}}AJIj}\",[]]Ak[\"{{{AIIh{AJEf}}}AJIj}\",[]]Ak[\"{{{AIIh{ACAf}}}AJIj}\",[]]Bg[\"{{{AIIh{CLj}}}{{LJf{AJIjAJDh}}}}\",[]]Ak[\"{{{AIIh{ACAh}}}AJIj}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]B`[\"{{LLbc}LLb{{CKl{LLb}}}}\",[\"\"]]B`[\"{{LJ`c}LJ`{{CKl{LJ`}}}}\",[\"\"]]B`[\"{{LLdc}LLd{{CKl{LLd}}}}\",[\"\"]]B`[\"{{LJbc}LJb{{CKl{LJb}}}}\",[\"\"]]20Ae[\"{{AC@jCJb}AE`}\",[]]Bn[\"{{{AIIh{CCbBCNl}}ADn}{{AIIh{CCbBCNl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNn}}ADn}{{AIIh{CCbBCNn}}}}\",[]]A`[\"{JLnAC@j}\",[]]B`[\"{{}{{AIIh{{LCn{BFN`}}}}}}\",[]]Ao[\"{{AIIh{AA@d{CCbMn}}}HLd}\",[]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Am[\"{{}{{LIl{{Nf{ONj}}}}}}\",[]]Bn[\"{{{AIIh{CCbBBMf}}CNl}{{AIIh{CCbBBMf}}}}\",[]]Bn[\"{{{AIIh{CCbBBMh}}CNl}{{AIIh{CCbBBMh}}}}\",[]]Aj[\"{{{AIIh{AHOn}}}CJb}\",[]]Aj[\"{{{AIIh{AI@`}}}CJb}\",[]]A`[\"{ACCjLIl}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]10Bf[\"{{{AIIh{CIl}}}{{LJj{CJbBBB`}}}}\",[]]Bm[\"{{{AIIh{AC@n}}{Nn{BBKn}}}{{LJh{HLd}}}}\",[]]Ai[\"{AJEd{{LJj{HLd}}}}\",[]]Cj[\"{{{AIIh{ONh}}BBL`BBBj{AIIh{A@d}}}{{LFb{AJIjAC@l}}}}\",[]]Cb[\"{{{AIIh{CCbAJEh}}LJbAJH`}{{LJf{AJH`BFHj}}}}\",[]]Am[\"{{{AIIh{AJEf}}CJb}HLd}\",[]]B`[\"{{{AIIh{CCbAJEh}}CJb}HLd}\",[]]A`[\"{BC@hCNl}\",[]]B`[\"{{{AIIh{CCbJLn}}BC@j}HLd}\",[]]Aa[\"{B@@`AHOd}\",[]]Bm[\"{{{AIIh{ONj}}AIAj}{{LJh{{Nn{BBKn}}}}}}\",[]]Bn[\"{{{AIIh{BAD`}}AIAj}{{LJh{{Nn{BBKn}}}}}}\",[]]Bn[\"{{{AIIh{AOOf}}AIAj}{{LJh{{Nn{BBKn}}}}}}\",[]]Ci[\"{{{AIIh{CCbONl}}{AIIh{CCb{AAb{Mn}}}}}{{LIl{CKn}}}}\",[]]Cj[\"{{{AIIh{CCbAJJf}}{AIIh{CCb{AAb{Mn}}}}}{{LIl{CKn}}}}\",[]]Ci[\"{{{AIIh{CCbONn}}{AIIh{CCb{AAb{Mn}}}}}{{LIl{CL`}}}}\",[]]Cj[\"{{{AIIh{CCbAJJh}}{AIIh{CCb{AAb{Mn}}}}}{{LIl{CL`}}}}\",[]]Cb[\"{{{AIIh{ACDj}}HLbAJJ`AC@j}{{LJf{HLdBCNf}}}}\",[]]Cb[\"{{{AIIh{BANf}}HLbAJJ`AC@j}{{LJf{HLdBCNf}}}}\",[]]Cb[\"{{{AIIh{BAAb}}HLbAJJ`AC@j}{{LJf{HLdBCNf}}}}\",[]]Cb[\"{{{AIIh{ACDl}}HLbAJJ`AC@j}{{LJf{HLdBCNh}}}}\",[]]Cb[\"{{{AIIh{BANh}}HLbAJJ`AC@j}{{LJf{HLdBCNh}}}}\",[]]Cb[\"{{{AIIh{BAAd}}HLbAJJ`AC@j}{{LJf{HLdBCNh}}}}\",[]]Al[\"{{}{{LJf{B@LnBGEl}}}}\",[]]Bm[\"{{{AIIh{AJEf}}LJ`}{{LIl{{Nn{AJEj}}}}}}\",[]]Bo[\"{{{AIIh{AJEh}}LJb}{{LIl{{AIIh{AJEl}}}}}}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]Aj[\"{{{AIIh{AJEf}}}HLd}\",[]]Am[\"{{{AIIh{CCbAJEh}}}HLd}\",[]]Bf[\"{{{AIIh{AJCn}}}{{LJf{A@dJJb}}}}\",[]]Be[\"{{{AIIh{AJCn}}}{{LJf{ObJJb}}}}\",[]]Bg[\"{{{AIIh{AJCn}}}{{LJf{ABOlJJb}}}}\",[]]Bg[\"{{{AIIh{AJCn}}}{{LJf{AHMfJJb}}}}\",[]]Bf[\"{{{AIIh{AJCn}}}{{LJf{A@fJJb}}}}\",[]]Bf[\"{{{AIIh{AJCn}}}{{LJf{A@hJJb}}}}\",[]]Ak[\"{{{AIIh{BCAb}}}BAA`}\",[]]Cf[\"{{{AIIh{CCbBCNl}}{LIl{AOMl}}}{{AIIh{CCbBCNl}}}}\",[]]Cf[\"{{{AIIh{CCbBCNn}}{LIl{AOMn}}}{{AIIh{CCbBCNn}}}}\",[]]<;Aj[\"{{{AIIh{AC@n}}}LFd}\",[]]Aj[\"{{{AIIh{ACA`}}}LFd}\",[]]Bc[\"{{{AIIh{AJEf}}AJIjAJGn}ACBn}\",[]]Co[\"{{{AIIh{CCbAJEh}}AHNhAJH`OLf}{{LJf{{LFb{LJbCNl}}AI@d}}}}\",[]]Bn[\"{{{AIIh{CCbBCNl}}CJ`}{{AIIh{CCbBCNl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNn}}CJ`}{{AIIh{CCbBCNn}}}}\",[]]Ai[\"{AJEd{{LJj{HLd}}}}\",[]]Bg[\"{{{AIIh{BCAb}}AJJn}{{LJh{LFd}}}}\",[]]A`[\"{AHMfCJb}\",[]]10Aj[\"{{{AIIh{A@d}}}BCNj}\",[]]Ce[\"{{{AIIh{CCbBACb}}{Nf{BCNl}}}{{AIIh{CCbBACb}}}}\",[]]Ce[\"{{{AIIh{CCbB@M`}}{Nf{BCNl}}}{{AIIh{CCbB@M`}}}}\",[]]Ce[\"{{{AIIh{CCbBACf}}{Nf{BCNn}}}{{AIIh{CCbBACf}}}}\",[]]Ce[\"{{{AIIh{CCbB@Md}}{Nf{BCNn}}}{{AIIh{CCbB@Md}}}}\",[]]Aa[\"{BACbBGMd}\",[]]Aa[\"{BACfBGMf}\",[]]Bj[\"{{{AAb{BBL`}}BBBj}{{LJf{BACbJJf}}}}\",[]]Bj[\"{{{AAb{BBL`}}BBBj}{{LJf{BACfJJh}}}}\",[]]Co[\"{{{AIIh{CCbAJId}}{AIIh{CCb{LCn{JHb}}}}}{{LJf{HLdAJK`}}}}\",[]]A`[\"{AI@lCJb}\",[]]A`[\"{AI@nCJb}\",[]]A`[\"{BF@dCJb}\",[]]A`[\"{BF@fCJb}\",[]]Bc[\"{{{AIIh{AJEf}}}{{LIl{LFd}}}}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]10Aj[\"{{{AIIh{AHMl}}}LFd}\",[]]Be[\"{{{AIIh{AC@n}}{LIl{BACb}}}HLd}\",[]]Bn[\"{{{AIIh{CCbACA`}}{LIl{{Nf{BACf}}}}}HLd}\",[]]Bd[\"{{{AIIh{BFN`}}}{{LIl{BBBh}}}}\",[]]o[\"{{}AIIh}\",[]]n[\"{{}OKb}\",[]]0000Bn[\"{{{AIIh{CCbBCNl}}LFd}{{AIIh{CCbBCNl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNn}}LFd}{{AIIh{CCbBCNn}}}}\",[]]o[\"{CJbLLb}\",[]]o[\"{CJbLLd}\",[]]A`[\"{BAOhCJb}\",[]]Bf[\"{{{AIIh{BGFh}}{AIIh{BGFh}}}CNl}\",[]]Bo[\"{{{AIIh{CCbBBMf}}AC@j}{{AIIh{CCbBBMf}}}}\",[]]Bo[\"{{{AIIh{CCbBBMh}}AC@j}{{AIIh{CCbBBMh}}}}\",[]]Ae[\"{{LFdCJb}BANf}\",[]]Ae[\"{{LFdCJb}BANh}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]7Bg[\"{{{AIIh{AJEh}}LJb}{{LIl{AJH`}}}}\",[]]An[\"{{{AIIh{AJEf}}JHb}BABl}\",[]]Bm[\"{{{AIIh{CCbBEH`}}{AAb{{AAb{Mn}}}}}HLd}\",[]]Bm[\"{{{AIIh{CCbBEHd}}{AAb{{AAb{Mn}}}}}HLd}\",[]]Bm[\"{{{AIIh{CCbBEHb}}{AAb{{AAb{Mn}}}}}HLd}\",[]]Bm[\"{{{AIIh{CCbBEHf}}{AAb{{AAb{Mn}}}}}HLd}\",[]]Am[\"{{{AIIh{AJEf}}LLb}HLd}\",[]]B`[\"{{{AIIh{CCbAJEh}}LLd}HLd}\",[]]Ce[\"{{{AIIh{CCbBBMf}}{AAb{CJ`}}}{{AIIh{CCbBBMf}}}}\",[]]Ce[\"{{{AIIh{CCbBBMh}}{AAb{CJ`}}}{{AIIh{CCbBBMh}}}}\",[]]Fg[\"{{{AIIh{BHAh}}{AIIh{BBL`}}{AIIh{{LCn{BBL`}}}}{AIIh{AJIh}}{AIIh{{LCn{Mn}}}}ACEl}{{LJf{BFHdJIl}}}}\",[]]Af[\"{{BCMjOLf}BCMj}\",[]]Af[\"{{BCMlOLf}BCMl}\",[]]A`[\"{BAOhCJb}\",[]]Al[\"{BADb{{LJf{CMhJJd}}}}\",[]]Ak[\"{BADb{{LJf{OdJJd}}}}\",[]]Ai[\"{BADb{{LJl{CMj}}}}\",[]]Ah[\"{BADb{{LJl{Of}}}}\",[]]o[\"{OKbOKb}\",[]]Bo[\"{{{AIIh{CCbBFFj}}AC@j}{{AIIh{CCbBFFj}}}}\",[]]Bo[\"{{{AIIh{CCbBFFl}}AC@j}{{AIIh{CCbBFFl}}}}\",[]]Bb[\"{{{AIIh{ONl}}}{{LIl{CNl}}}}\",[]]Bc[\"{{{AIIh{AJJf}}}{{LIl{CNl}}}}\",[]]Bb[\"{{{AIIh{ONn}}}{{LIl{CNl}}}}\",[]]Bc[\"{{{AIIh{AJJh}}}{{LIl{CNl}}}}\",[]]Bg[\"{{{AIIh{AJEf}}}{{LJf{HLdBCKn}}}}\",[]]Ba[\"{{{AIIh{BFA`}}LJ`AHNh}CNl}\",[]]Ba[\"{{{AIIh{BFAb}}LJbAHNh}CNl}\",[]]Cf[\"{{{AIIh{CCbBCNl}}{LIl{AC@j}}}{{AIIh{CCbBCNl}}}}\",[]]Cf[\"{{{AIIh{CCbBCNn}}{LIl{AC@j}}}{{AIIh{CCbBCNn}}}}\",[]]A`[\"{AI@lCJb}\",[]]A`[\"{AI@nCJb}\",[]]Bl[\"{{{AIIh{CCbAJEj}}OLfOLfCNlCNlCJb}HLd}\",[]]Bj[\"{{{AIIh{CCbNh}}OLfOLfCNlCNlCJb}HLd}\",[]]Bk[\"{{{AIIh{CCbJHl}}OLfOLfCNlCNlCJb}HLd}\",[]]Bk[\"{{{AIIh{CCbOMb}}OLfOLfCNlCNlCJb}HLd}\",[]]Bl[\"{{{AIIh{CCbAJEl}}OLfOLfCNlCNlCJb}HLd}\",[]]Bj[\"{{{AIIh{CCbNj}}OLfOLfCNlCNlCJb}HLd}\",[]]Bk[\"{{{AIIh{CCbJHn}}OLfOLfCNlCNlCJb}HLd}\",[]]Bk[\"{{{AIIh{CCbOMd}}OLfOLfCNlCNlCJb}HLd}\",[]]A`[\"{BAOhCJb}\",[]]Ak[\"{{{AIIh{OOl}}Of}CJb}\",[]]Bi[\"{{{AIIh{CCbAJIb}}CJb}{{LJj{HLd}}}}\",[]]<;A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]10Cf[\"{{{AIIh{CCbBCNl}}{LIl{BFDl}}}{{AIIh{CCbBCNl}}}}\",[]]Cf[\"{{{AIIh{CCbBCNn}}{LIl{BFDn}}}{{AIIh{CCbBCNn}}}}\",[]]A`[\"{AI@lCJb}\",[]]A`[\"{AI@nCJb}\",[]]Bi[\"{{{AIIh{CCbAJCn}}}{{LJf{A@dJJb}}}}\",[]]Bh[\"{{{AIIh{CCbAJCn}}}{{LJf{ObJJb}}}}\",[]]Bj[\"{{{AIIh{CCbAJCn}}}{{LJf{ABOlJJb}}}}\",[]]Bj[\"{{{AIIh{CCbAJCn}}}{{LJf{AHMfJJb}}}}\",[]]Bi[\"{{{AIIh{CCbAJCn}}}{{LJf{A@fJJb}}}}\",[]]Bi[\"{{{AIIh{CCbAJCn}}}{{LJf{A@hJJb}}}}\",[]]Cc[\"{{{Nf{AJEd}}CJbCNl}{{LJj{{LFb{BC@jBC@j}}}}}}\",[]]Bn[\"{{{AIIh{CCbBACb}}CJb}{{AIIh{CCbBACb}}}}\",[]]Bn[\"{{{AIIh{CCbBACf}}CJb}{{AIIh{CCbBACf}}}}\",[]]Aj[\"{{{AIIh{AJEf}}}CNl}\",[]]Ak[\"{{{AIIh{BBKn}}}BAA`}\",[]]0Ci[\"{{{AIIh{CCbBBMf}}CIn}{{LJf{{AIIh{CCbBBMf}}AOLh}}}}\",[]]Ci[\"{{{AIIh{CCbBBMh}}CIn}{{LJf{{AIIh{CCbBBMh}}AOLj}}}}\",[]]A`[\"{AHMfCIn}\",[]]Cf[\"{{{AIIh{CCbBCNl}}{LIl{BFFj}}}{{AIIh{CCbBCNl}}}}\",[]]Cf[\"{{{AIIh{CCbBCNn}}{LIl{BFFl}}}{{AIIh{CCbBCNn}}}}\",[]]Bg[\"{{{AIIh{CCbAJEh}}}{{LIl{BAOf}}}}\",[]]Cf[\"{{{AIIh{CCbBACb}}{LIl{BACj}}}{{AIIh{CCbBACb}}}}\",[]]Cf[\"{{{AIIh{CCbBACf}}{LIl{BACj}}}{{AIIh{CCbBACf}}}}\",[]]Cf[\"{{{AIIh{CCbBACb}}{LIl{BACl}}}{{AIIh{CCbBACb}}}}\",[]]Cf[\"{{{AIIh{CCbBACf}}{LIl{BACl}}}{{AIIh{CCbBACf}}}}\",[]]Cc[\"{{}{{LJf{{LFb{{AAb{BBL`}}BBBj}}{Nn{JIh}}}}}}\",[]]Bn[\"{{{AIIh{CCbBFDl}}LLb}{{AIIh{CCbBFDl}}}}\",[]]Bn[\"{{{AIIh{CCbBFDn}}LLd}{{AIIh{CCbBFDn}}}}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]Bo[\"{{{AIIh{CCbBACb}}AC@j}{{AIIh{CCbBACb}}}}\",[]]Bo[\"{{{AIIh{CCbBACf}}AC@j}{{AIIh{CCbBACf}}}}\",[]]Ci[\"{{{AIIh{CLj}}{LIl{AC@j}}}{{LJf{{LIl{AC@j}}AJDh}}}}\",[]]Bj[\"{{{AIIh{BCAb}}AJJnLFd}{{LJh{HLd}}}}\",[]]054Bm[\"{{{AIIh{ONl}}}{{LJf{{LIl{BFNl}}JJ`}}}}\",[]]Bn[\"{{{AIIh{AJJf}}}{{LJf{{LIl{BFNl}}JJ`}}}}\",[]]Bn[\"{{{AIIh{ONn}}}{{LJf{{LIl{BFNl}}BCA`}}}}\",[]]Bo[\"{{{AIIh{AJJh}}}{{LJf{{LIl{BFNl}}BCA`}}}}\",[]]Aj[\"{{{AIIh{AHOn}}}CJb}\",[]]Aj[\"{{{AIIh{AI@`}}}CJb}\",[]]Bk[\"{{{AIIh{AC@n}}{LIl{{Nf{BFA`}}}}}HLd}\",[]]Cc[\"{{{AIIh{CCbAJEh}}{LIl{{AIIh{BFAb}}}}OLf}HLd}\",[]]Aj[\"{{{AIIh{ACA`}}}CJb}\",[]]Bf[\"{{{AIIh{AHNh}}{AIIh{AHNh}}}CNl}\",[]]Bn[\"{{{AIIh{CCbBBAl}}ADn}{{AIIh{CCbBBAl}}}}\",[]]Bn[\"{{{AIIh{CCbBBAn}}ADn}{{AIIh{CCbBBAn}}}}\",[]]Ak[\"{{{AIIh{AIAh}}}BAA`}\",[]]0Bd[\"{{{AIIh{AJEf}}}{{ACCf{JKb}}}}\",[]]Cb[\"{{{AIIh{AJEh}}LJb}{{LJf{{LIl{AJIj}}AJDn}}}}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]Bn[\"{{{AIIh{CCbBCNl}}LLb}{{AIIh{CCbBCNl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNn}}LLd}{{AIIh{CCbBCNn}}}}\",[]]A`[\"{AHMfLIl}\",[]]Bo[\"{{{AIIh{AHMf}}{AIIh{CIl}}}{{LJh{BCNj}}}}\",[]]Em[\"{{{AIIh{AJEf}}{AIIh{CCb{LCn{Mn}}}}{AIIh{{LCn{Mn}}}}{AIIh{{LCn{Mn}}}}}{{LJf{HLdBHHl}}}}\",[]]El[\"{{{AIIh{ONl}}{AIIh{CCb{LCn{Mn}}}}{AIIh{{LCn{Mn}}}}{AIIh{{LCn{Mn}}}}}{{LJf{HLdBHHl}}}}\",[]]Em[\"{{{AIIh{AJJf}}{AIIh{CCb{LCn{Mn}}}}{AIIh{{LCn{Mn}}}}{AIIh{{LCn{Mn}}}}}{{LJf{HLdBHHl}}}}\",[]]El[\"{{{AIIh{ONn}}{AIIh{CCb{LCn{Mn}}}}{AIIh{{LCn{Mn}}}}{AIIh{{LCn{Mn}}}}}{{LJf{HLdBHHn}}}}\",[]]Em[\"{{{AIIh{AJJh}}{AIIh{CCb{LCn{Mn}}}}{AIIh{{LCn{Mn}}}}{AIIh{{LCn{Mn}}}}}{{LJf{HLdBHHn}}}}\",[]]Am[\"{{{AIIh{CCbAHMj}}}HLd}\",[]]0Al[\"{{{AIIh{AJEh}}Of}CJb}\",[]]Bl[\"{{{AIIh{AJEf}}}{{ACCh{{LIl{AJIj}}}}}}\",[]]Bg[\"{{{AIIh{CLj}}}{{LJf{BEDbAJDh}}}}\",[]]>=E`[\"{{{AIIh{BHAh}}{AIIh{{LCn{Mn}}}}{AIIh{BBL`}}{AIIh{BGLf}}}{{LJf{BHCnJIl}}}}\",[]]0Bf[\"{{{Nf{BBCb}}}{{LJf{B@M`BGFf}}}}\",[]]Bf[\"{{{Nf{BBCb}}}{{LJf{B@MdBGFf}}}}\",[]]Bn[\"{{{AIIh{CCbBFDl}}LLb}{{AIIh{CCbBFDl}}}}\",[]]Bn[\"{{{AIIh{CCbBFDn}}LLd}{{AIIh{CCbBFDn}}}}\",[]]o[\"{OKbOKb}\",[]]0000Aj[\"{{{AIIh{AJEh}}}CNl}\",[]]0Ci[\"{{{AIIh{CLj}}{LIl{AC@j}}}{{LJf{{LIl{AC@j}}AJDh}}}}\",[]]Cd[\"{{{AIIh{CCbA@d}}{AIIh{AJCn}}}{{LJf{HLdJJb}}}}\",[]]Cc[\"{{{AIIh{CCbOb}}{AIIh{AJCn}}}{{LJf{HLdJJb}}}}\",[]]Ce[\"{{{AIIh{CCbABOl}}{AIIh{AJCn}}}{{LJf{HLdJJb}}}}\",[]]Ce[\"{{{AIIh{CCbAHMf}}{AIIh{AJCn}}}{{LJf{HLdJJb}}}}\",[]]Cd[\"{{{AIIh{CCbA@f}}{AIIh{AJCn}}}{{LJf{HLdJJb}}}}\",[]]Cd[\"{{{AIIh{CCbA@h}}{AIIh{AJCn}}}{{LJf{HLdJJb}}}}\",[]]Bo[\"{{{AIIh{CCbBACb}}BGMd}{{AIIh{CCbBACb}}}}\",[]]Bo[\"{{{AIIh{CCbBACf}}BGMf}{{AIIh{CCbBACf}}}}\",[]]Aj[\"{{{AIIh{BBMf}}}CJb}\",[]]Aj[\"{{{AIIh{BBMh}}}CJb}\",[]]Cc[\"{{{AIIh{CCbB@M`}}{Nf{A`}}}{{AIIh{CCbB@M`}}}}\",[]]Cc[\"{{{AIIh{CCbB@Md}}{Nf{A`}}}{{AIIh{CCbB@Md}}}}\",[]]Cj[\"{{BBMf{LIl{BACb}}{Nn{BBKn}}{Nf{ONj}}}{{LJh{AC@n}}}}\",[]]Aj[\"{{{AIIh{ACAf}}}CNl}\",[]]Aj[\"{{{AIIh{ACAh}}}CNl}\",[]]Ca[\"{{{AIIh{CCbAJEh}}OLfAJGd}{{LJf{HLdBH@n}}}}\",[]]Bd[\"{{{AIIh{BHAh}}}{{AAb{BCNb}}}}\",[]]Bj[\"{{{AIIh{AJEf}}AJIj}{{LJf{HLdJJl}}}}\",[]]Bm[\"{{{AIIh{CCbAJEh}}AJIj}{{LJf{HLdJJl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNl}}LFd}{{AIIh{CCbBCNl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNn}}LFd}{{AIIh{CCbBCNn}}}}\",[]]Bl[\"{{}{{LJf{{LFb{BBL`BFGf}}{Nn{JIh}}}}}}\",[]]Ai[\"{AJEd{{LJj{HLd}}}}\",[]]A`[\"{AJFbCJb}\",[]]A`[\"{AJFdCJb}\",[]]An[\"{{{AIIh{AC@n}}B@M`}HLd}\",[]]D`[\"{{{AIIh{CCbAJEh}}LJb{LIl{AC@j}}}{{LJf{{LIl{AC@j}}AJDn}}}}\",[]]o[\"{{}AIIh}\",[]]Aj[\"{{{AIIh{AJEf}}}LFd}\",[]]Bn[\"{{{AIIh{CCbBACb}}CJb}{{AIIh{CCbBACb}}}}\",[]]Bn[\"{{{AIIh{CCbBACf}}CJb}{{AIIh{CCbBACf}}}}\",[]]Bn[\"{{{AIIh{CCbBCNl}}LLb}{{AIIh{CCbBCNl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNn}}LLd}{{AIIh{CCbBCNn}}}}\",[]]:Bb[\"{{{AIIh{CCbAJEh}}OfLLd}HLd}\",[]]Ak[\"{{}{{LJf{B@M`JJf}}}}\",[]]Ak[\"{{}{{LJf{B@MdJJh}}}}\",[]]Bn[\"{{{AIIh{CCbBCNl}}CNl}{{AIIh{CCbBCNl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNn}}CNl}{{AIIh{CCbBCNn}}}}\",[]]65Cg[\"{{{AIIh{CCbA@d}}{AIIh{CCbAJCn}}}{{LJf{HLdJJb}}}}\",[]]Cf[\"{{{AIIh{CCbOb}}{AIIh{CCbAJCn}}}{{LJf{HLdJJb}}}}\",[]]Ch[\"{{{AIIh{CCbABOl}}{AIIh{CCbAJCn}}}{{LJf{HLdJJb}}}}\",[]]Ch[\"{{{AIIh{CCbAHMf}}{AIIh{CCbAJCn}}}{{LJf{HLdJJb}}}}\",[]]Cg[\"{{{AIIh{CCbA@f}}{AIIh{CCbAJCn}}}{{LJf{HLdJJb}}}}\",[]]Cg[\"{{{AIIh{CCbA@h}}{AIIh{CCbAJCn}}}{{LJf{HLdJJb}}}}\",[]]Bi[\"{{{AIIh{CCbAHNh}}{AIIh{AHNh}}}CNl}\",[]]n[\"{{}HLd}\",[]]Ce[\"{{{AIIh{CCbBCNl}}{LIl{LFd}}}{{AIIh{CCbBCNl}}}}\",[]]Ce[\"{{{AIIh{CCbBCNn}}{LIl{LFd}}}{{AIIh{CCbBCNn}}}}\",[]]Bn[\"{{{AIIh{AJEf}}}{{LJf{{AAb{AJIj}}JJl}}}}\",[]]Cd[\"{{{AIIh{CCbAJEh}}OLf}{{LJf{{AAb{AJIj}}JJl}}}}\",[]]Am[\"{{{AIIh{CCbAJEj}}}HLd}\",[]]Al[\"{{{AIIh{CCbJHl}}}HLd}\",[]]Am[\"{{{AIIh{CCbAJEl}}}HLd}\",[]]Al[\"{{{AIIh{CCbJHn}}}HLd}\",[]]Ai[\"{AJEd{{LJj{HLd}}}}\",[]]Bj[\"{{{AIIh{AJEf}}AJIj}{{LJf{HLdJJl}}}}\",[]]Bm[\"{{{AIIh{CCbAJEh}}AJIj}{{LJf{HLdJJl}}}}\",[]]D`[\"{{{AIIh{CCbAJEh}}LJb{LIl{AC@j}}}{{LJf{{LIl{AC@j}}AJDn}}}}\",[]]Ce[\"{{{AIIh{CCbBCNl}}{Nf{BF@`}}}{{AIIh{CCbBCNl}}}}\",[]]Ce[\"{{{AIIh{CCbBCNn}}{Nf{BF@b}}}{{AIIh{CCbBCNn}}}}\",[]]Cf[\"{{{AIIh{CCbBCNl}}{LIl{AC@j}}}{{AIIh{CCbBCNl}}}}\",[]]Cf[\"{{{AIIh{CCbBCNn}}{LIl{AC@j}}}{{AIIh{CCbBCNn}}}}\",[]]Aa[\"{BAOlAJGb}\",[]]Aa[\"{BAOnAJGb}\",[]]9Bn[\"{{{AIIh{CCbBCNl}}CNl}{{AIIh{CCbBCNl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNn}}CNl}{{AIIh{CCbBCNn}}}}\",[]]Am[\"{{{AIIh{AJEf}}LLb}HLd}\",[]]o[\"{OKbOKb}\",[]]Bn[\"{{{AIIh{CCbBCNl}}CJ`}{{AIIh{CCbBCNl}}}}\",[]]Bn[\"{{{AIIh{CCbBCNn}}CJ`}{{AIIh{CCbBCNn}}}}\",[]]3109854Bn[\"{{{AIIh{AJEf}}}{{LJf{{AAb{AJIj}}JJl}}}}\",[]]Bn[\"{{{AIIh{AJEh}}}{{LJf{{AAb{AJIj}}JJl}}}}\",[]]10A`[\"{BFNlLIl}\",[]]Bm[\"{{{AIIh{CCbBCNl}}Mn}{{AIIh{CCbBCNl}}}}\",[]]Bm[\"{{{AIIh{CCbBCNn}}Mn}{{AIIh{CCbBCNn}}}}\",[]]o[\"{{}AJGd}\",[]]") \ No newline at end of file diff --git a/pr/509/docs/search.index/generic_inverted_index/b518a2effe00.js b/pr/509/docs/search.index/generic_inverted_index/b518a2effe00.js new file mode 100644 index 000000000..dcf7e4f23 --- /dev/null +++ b/pr/509/docs/search.index/generic_inverted_index/b518a2effe00.js @@ -0,0 +1 @@ +rb_("QU5iADswAAABAAC/AQMAPgPhAGIE3ACtDAAAOzAAAAEAAEsCKQACAhUAGQIJACQCAQAoAgAAKgIAACwCAwA0AgYAQAICAEQCBwBPAgQAVQIDAFsCAQBeAgIAYgIBAGUCBQBsAgsAeQIFAIoCFACjAgAApQICAKkCEgC9AgMAwgIEAMgCBADOAgAA0AIFANcCAwDcAgcA5QIDAOoCAQDtAgQA8wIFAPoCAwD/AgUACAMAABEDCAAbAw0A9AmEAG0L4QAhEAAAyBABADswAAABAADDARAAZgbhAHkKLgCqCgAArAoNALsKBQDCChoA3goAAOAKAQDkChQA+goAAP0KHgAdCwYAJQspAFELAABTCwAAVQsXADswAAABAAALAQsAQwIAAKgCAAAaAwAAEQgTABwLAAAkCwAAUAsAAFILAADLDuwAJxABAF8QAQA7MAAAAQAAcAEGACYEAADBCQAATwsAAFQLAABcDWYBzxEFADswAAABAAAGAAIAZQcDAGoHAgA7MAAAAQAAKQAFACkDFAA0BgAAYAcEAG4HDAC8CQEAOzAAAAEAAAoAAgBDBAAATwwJADswAAABAAAMAAEAMgQMAAHDDAAAArsRAAC8EQAAAAIjEQAAJBEAAAACsQEAAGkHAABEagAAADswAAABAADhAA8AeQouAKoKAACsCg0AuwoFAMIKGgDeCgAA4AoBAOQKFAD6CgAA/QoeAB0LBgAlCykAUQsAAFMLAABVCxcA") \ No newline at end of file diff --git a/pr/509/docs/search.index/name/07d6c6249cec.js b/pr/509/docs/search.index/name/07d6c6249cec.js new file mode 100644 index 000000000..bd9b2399c --- /dev/null +++ b/pr/509/docs/search.index/name/07d6c6249cec.js @@ -0,0 +1 @@ +rd_("b()b->bBi0bCe00bFnbIdbOkb[]beq0000000000000000000000000000000000000000000000000000000000000000000000000000000000bid0000000000000brtbtybu8cAck0cAnycArccBbr00cBoxcBufcClicDir0cErrcMAX000cNotcOpt00cOrdcPincUni0cVeccbbrccmp0000000000000000000cdir000cecn000cf32cf64cfmt00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cgetci32cimpcios0clen000clog0cmincmutcnew00000000000000000000000000000000000cnoqcnot0cnow000000crtt000crun0cstrcu16cu32cu64cudpdArgsdAsFddDropdEct000dEct100dFromdHashdIntodKeys0dLong0dNonedPath00dPing0dPolldReaddSenddSide0dSyncdVZipdZERO0dacks0daddr0dbooldbulkdcodedcwnd0ddataddrop000000dfrom000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dhash00000000000000000000dinto000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dmain0000dnext0dopen00dpath0dperfdping000dpoll00000000000000dqlogdread00drecv00dseal0dsend00dsent0dside0dsign0dsize00dsmoldspindstop00dtake00000du128dunitdvzip0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eAsRefeByteseChunk0eCloneeCloseeCubic00eDebugeDerefeError00000000000eEvent00eKnowneOther0eRangeeReset000eRetry0eShort0eSizedeStats0eSuiteeWriteealpnsearrayebenchebuild00000000ebytes00000ecauseeclone000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eclose000ecubicederef0eerroreframe0eindex0eis_ce0elocal0eparse0epaths0eprint00ereset00eretry0esliceespawn00estats000000000etitle0etokenetokioetupleeusizeewrite00fAcceptfAckEcn0fAes128fAes256fBackupfBorrowfBufMutfChunksfClient00fClosedfCrypto0fCursorfFuturefHasherfIpAddrfOpenBifOpened000fOptionfOutputfPathId0fResult00000fServer00fStream000fStringfTypeIdfVarInt0faccept000fborrow000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fchunks0fcipherfclient0000fclosedfcoding0fconfig0fcrypto0000000000fdecode000000000000fdst_ipfencode000000000000ffinish00fhandlefheader0fignore0finsert00000fkeylogfoffset0fon_ack0000000fpacket00frandomfreason000000frebindfrefuse0fremote00frustls00000fserver0000fsharedfsource00000000fsrc_ip00fstatus0fstream0fstridefudp_rx000fudp_tx000fvarintfverify0fwindow0000000gBlocked00gCommandgContextgDefaultgDisplaygFromStrgGeneral0gHmacKey0gInitialgInstantgKeyPair0gLayeredgMaxData0gNewReno00gOpenUnigPadding0gPathAck0gPathBufgReadBufgRequestgRuntime0gSession0gStopped00000gStreamsgToOwnedgTooLonggTryFromgTryIntogUnknowngWritten0gZeroRttgcid_len000gclientsgcommand00000gcompare0000000000000000000gconnect0gdecrypt00000gdefault000000000000000000000000000000000000000000000000000gdrainedgdst_cid0000000gencrypt000gis_0rtt0gmetrics0000000gmin_mtu0gnoq_udpgon_sent000gopen_bigpath_idgpointergruntimegsrc_cid0000gstopped0gstreams000gtag_len0gtype_id000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000gupgrade0gversion00000hAcceptBihBytesMuthChacha20hCommandshDatagram00hDerefMuthDisabled0hDurationhEndpoint00hFinished0hIncoming0hIteratorhLayerExthLongTypehMAX_SIZE0hNO_ERROR0hNewToken0hOnClosedhOpenPathhOrderinghReadable0hReceiver0hRecvMetahResponsehStreamId0hTimedOut00hToStringhTokenLog0hTooLarge0hTransmit00hUdpStats0hUnixTimehWritable0hcong_alghcontentshdatagram0hdurationhendpoint0hfinalizehframe_rx0hframe_tx0hfrom_buf0hfrom_env0hfrom_keyhfrom_strhfrom_u320hfrom_u640hgot_type000hgroup_id0000hhas_0rtthincominghinterval0hinto_any0000000his_alivehlifetime0hlocal_ip0000hmax_data0hmax_sizehnew_renohnoq_perfhon_byteshopen_unihpriority0hprotocol0hqlog_dirhresponsehrng_seed0hssthresh0hto_owned000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_from000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_into000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_poll000000000htry_sendhvalidate000iAbandoned0iAcceptUniiArbitraryiAsyncRead0iAvailable00iBbrConfig0iBorrowMutiCommonOptiConnectediCriterioniDatagramsiDecodableiDiscarded0iEncodableiFormatteriFourTupleiFrameType0iHandshakeiHeaderKey0iHistogramiMalformediMaxPathId0iMultipathiPacketKey0iPartialEqiPathErroriPathEvent00iPathStats0iReadError00iTryFutureiUdpSenderiUdpSocketiValueEnumiaccept_biiarbitrary0000000ias_rustlsiassemblericlone_box0000000idatagrams000ideref_mut0ifrom_bits00iinitiator0iinto_0rttiinto_iteriis_client0iis_closediis_server0imay_retry0imigration0inew_timer00inew_token0inoq_protoion_closediopen_path0ipath_acks0iping_pathipoll_nextipoll_read00ipoll_recvipoll_sendireach_out0ireferenceireset_key0iretry_tag0000isize_hint000istable_idito_string0000000000000000000000000000000000000000000000000000000000000itoken_key0itoken_positransport00iwait_idleiwrite_alljArgMatchesjAsyncTimerjAsyncWrite0jBATCH_SIZEjClosedPath000jComparablejConnectingjConnection00jController0jEquivalent0jFrameStats0jIntoFuturejInvalidCid0jIoSliceMutjMaybeFrame0jNamedGroupjNonZeroU32jOtherErrorjPartialOrdjPathAckEcn0jPathStatus0jQlogConfig0jRecvStream00jResetErrorjRetryError0jSendStream00jServerNamejSocketAddrjStreamInfojSubcommandjSystemTimejTimeSource0jTlsSession0jTokenStore0jUdpSockRefjWriteError0jaccept_unijallow_spin0jborrow_mut000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jclone_into000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jclose_pathjcongestion000jconn_statsjconnection0jequivalent00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jerror_code000000jframe_type0jinto_inner0jis_drained0jlocal_addr0jlost_bytes0jnew_senderjpad_to_mtu0jpath_stats00jpoll_closejpoll_flush0jpoll_write00jprint_jsonjread_chunk0jread_exactjset_statusjstart_time0jthroughputjtotal_sizekAcceptErrorkCLIENT_ADDRkCipherSuitekClientStatskCloseReasonkConfigError0kContentTypekCryptoError0kCubicConfig0kDataBlocked0kFinishErrorkIdleTimeout0kIntoRequestkOutOfBounds0kPathAbandon0kQlogFactory0kResetStream0kSERVER_ADDRkSERVER_NAMEkSmolRuntimekStopSending0kStreamEvent0kStreamStats0kUnspecifiedkaccept_withkadd_address0kbind_socketkcertificatekcurrent_mtu00kdescription0kdestination00kinitial_mtu000kinitial_rtt00kinto_future0000000000000kmax_path_id0kmax_streamsknamed_layer0kon_end_acks000kon_intervalkpacing_rate0kpartial_cmp0000000000000000000kpath_eventskpath_statuskread_chunkskread_to_endkrecv_stream0ksample_size0ksend_bufferksend_stream0ksend_window00kserver_name0ktime_source0ktoken_store0kupload_sizekupper_bound0kweak_handle0kwith_crypto0kwith_prefix0kwrite_chunklAckFrequency0lAddressAddedlClientConfig00000lClosedStream000000lConnectError00lConnectionId00lDatagramInfolDecryptError0lEcnCodepoint00lEncryptError0lIllegalValuelImmediateAck0lIntoIteratorlLOCALHOST_V4lLastOpenPathlNatTraversallNonZeroUsizelNoneTokenLog0lObservedAddr0lPathResponse0lPathsBlocked0lReadDatagramlRemoteStatus0lRttEstimator0lSendDatagramlServerConfig00000lSocketAddrV4lSocketAddrV6lStoppedErrorlTokioRuntimelUnstructuredlaugment_args0000lcid_lifetime000lclose_reasonlconnect_withlconservativeldata_blocked0ldrain_streamlearly_crypto000lexpect_types000lgenerate_cid000lgro_segmentslhandle_event0linitial_keys00000000linto_request0llost_packets0lmax_incoming0lmay_fragment00lnetwork_pathlnew_receiverlnoprotection0lorig_dst_cid0lpath_abandon0lpoll_timeoutlqlog_factory0lreset_stream0lsegment_size00lsend_streamslset_lifetime0lset_priority0lstop_sending0lstream_statslupload_statslwith_initial000lwrite_chunks0mAlertReceived0mBloomTokenLog0mCidsExhausted000mCloneToUninitmDatagramEventmEndpointEventmEndpointStatsmFinishedEarlymHandshakeData0mHandshakeDone0mHandshakeTypemINVALID_TOKEN0mIncomingAlpnsmInvalidHeadermLocallyClosed0mMaxStreamData0mMaxStreamsUni0mNewConnectionmNewRenoConfig0mParseIntErrormPartialDecodemPathChallenge0mPossibleValuemPrivateKeyDermReadableErrormRemoveAddress0mRootCertStoremStdSystemTime0mUnexpectedEndmaccepted_0rttmack_frequency00mbench_exports0mcid_generator00mcreate_sendermdata_transfermdownload_sizemduration_histmimmediate_ack0minto_incoming0mmax_ack_delay0mno_protectionmobserved_addr0mon_first_bytemon_mtu_update0000000mpath_response0mpaths_blocked0mpeer_identity0000mpoll_read_bufmpoll_shutdownmpoll_transmitmqlog_from_env0mread_datagrammsend_datagrammsend_fairness0msignature_len0mstart_session000000000nAddIpv4Address0nAddIpv6Address0nAddressRemovednAsyncUdpSocketnCertificateDernClosePathError0nCommandFactorynConnectionLost000000nEndpointConfig0nFromArgMatchesnINTERNAL_ERROR0nIncomingFuturenInvalidFrameIdnInvalidMessage00nMaxStreamsBidi0nNO_VIABLE_PATH0nNoneTokenStore0nPeerMisbehaved00nReachOutAtIpv40nReachOutAtIpv60nReadExactErrornReadToEndErrornShouldTransmitnTransferResultnTransportError00nUdpSocketStatenWeakPathHandlenconnect_clientncrypto_sessionndownload_statsnfor_connection000nhandle_timeoutnhandshake_data00000nhandshake_done0nhas_subcommandninitial_window0000000000000ninto_unorderednis_handshaking0000nis_valid_retry000nminimum_change0nnext_1rtt_keys000npath_challenge0nqlog_from_path0nread_handshake000nread_unorderednreceive_window00nreceived_reset00nremote_address0000nremove_address0nsaturating_add000nsaturating_sub0nthroughput_bpsntime_threshold0ntotal_durationnvalue_variantsoConnectionClose000oConnectionError0oConnectionEvent0oConnectionStats0oGetRandomFailedoNewConnectionId0oOpenStreamStatsoPathCidsBlocked0oProtectedHeader0oQlogFileFactory0oRemoteAbandoned0oSignatureSchemeoSystemTimeErroroTokenReuseError0oTransportConfig00oTryFromIntErroroVersionMismatch0oZeroRttAcceptedoZeroRttRejected000obloom_token_logoclone_to_uninit000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000odefault_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`MaxPathIdReachedA`ObservedIpv4Addr0A`ObservedIpv6Addr0A`PathStatusBackup0A`PeerIncompatible00A`QuicClientConfig0A`QuicServerConfig0A`TokenMemoryCache0A`TooManyAddressesA`ValidationFailed0A`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`receive_datagramA`recv_buffer_size0A`send_buffer_size0A`transport_config0000A`validation_token0A`with_single_cert0A`write_all_chunksAaAPPLICATION_ERROR0AaApplicationClosed000AaControllerFactory0AaControllerMetrics0AaHandshakeTokenKey0AaInvalidServerName0AaNetworkChangeHint0AaPacketDecodeError0AaPathAbandonReason0AaSendDatagramError0AaStreamDataBlocked0AaStreamsBlockedUni0AaUnsupportedByPeer0Aacongestion_events0Aacongestion_window0Aamax_datagram_sizeAanew_connection_id0Aapath_cids_blocked0Aasend_buffer_spaceAaset_server_config0Aaset_up_connectionAato_possible_valueAbAEAD_LIMIT_REACHED0AbAckFrequencyConfig0AbBadMaxFragmentSize0AbCONNECTION_REFUSED0AbConnectionIdParserAbDatagramsUnblockedAbFLOW_CONTROL_ERROR0AbHandshakeConfirmedAbHandshakeDataReadyAbIllegalOrderedReadAbInvalidCertificate0AbMtuDiscoveryConfig0AbNotEnoughAddressesAbPERF_CIPHER_SUITESAbPROTOCOL_VIOLATION0AbPrivatePkcs8KeyDerAbRetireConnectionId0AbSTREAM_LIMIT_ERROR0AbSTREAM_STATE_ERROR0AbServerCertVerifiedAbServerCertVerifierAbSetPathStatusError0AbStreamsBlockedBidi0AbTransportErrorCode0AbUnsupportedVersion00000Abcommand_for_update0000Abcrypto_buffer_size0Abfrom_u64_unchecked0Abignored_handshakesAbis_same_connectionAbmin_reset_interval0Abnew_expected_items0Abpath_status_backup0Abrefused_handshakesAbremote_path_statusAbsend_datagram_waitAbset_alpn_protocols000Abset_receive_window0Absupported_versions0Abtoken_memory_cacheAbverify_server_certAbwith_start_instant0AcCongestionAlgorithmAcPathNewConnectionId0AcPathStatusAvailable0AcRemoteCidsExhaustedAcTransportParametersAcUnorderedRecvStreamAcUnsupportedNameType0AcWrongConnectionSideAcaccepted_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_ERROR0AdHandshakeNotComplete0AdInappropriateMessage000AdInvalidRemoteAddress00AdNoInitialCipherSuite0AdServerSideNotAllowedAdSupportedCipherSuiteAdVarIntBoundsExceeded0AdVerifierBuilderErrorAdWeakConnectionHandleAdack_frequency_config0Adblack_holes_detected0Adfrom_arg_matches_mut00000Adhandle_client_streamAdincoming_buffer_size0Adis_multipath_enabledAdmax_receive_segments0Admax_udp_payload_size00Admtu_discovery_config0Adpoll_endpoint_eventsAdpreferred_address_v40Adpreferred_address_v60Adread_certs_from_fileAdreordering_threshold0Adretire_connection_id0Adretry_token_lifetime0Adset_max_idle_timeoutAdset_recv_buffer_sizeAdset_send_buffer_sizeAdstreams_blocked_bidi0Adtransport_parameters00000AeConnectionIdGenerator0AeDigitallySignedStructAeNoApplicationProtocol0AeNoDefaultClientConfig0AePATH_UNSTABLE_OR_POOR0AeValidationTokenConfig0Aeconfidentiality_limit0Aehandle_network_change0Aeincoming_buffer_bytesAeis_probably_same_pathAeloss_reduction_factor0Aemax_transmit_segments0Aenat_traversal_updatesAepath_observed_addressAepath_status_available0Aestream_receive_window00AfCRYPTO_BUFFER_EXCEEDED0AfExtensionNotNegotiatedAfFailedToGetCurrentTime0AfFailedToGetRandomBytes0AfMultipathNotNegotiated000AfNatTraversalRoundEndedAfPathRetireConnectionId0AfProtectedInitialHeaderAfSkipServerVerificationAfbuild_transport_configAfexport_keying_material0000Afget_into_many_segmentsAfget_loop_many_segmentsAfmax_concurrent_streamsAfobserved_external_addr0Afpath_new_connection_id0Afverify_tls12_signatureAfverify_tls13_signatureAfwith_root_certificates0AgCertRevocationListErrorAgHandshakeSignatureValidAgNoCertificatesPresented0AgPeerSentOversizedRecord0Agack_eliciting_threshold0Agaugment_args_for_update0000Aghas_pending_retransmitsAgis_multipath_negotiatedAgset_keep_alive_intervalAgupdate_from_arg_matches00000Agvalidation_token_config0AhAPPLICATION_ABANDON_PATH0AhNoProtectionClientConfigAhNoProtectionServerConfigAhget_max_udp_payload_size0Ahinitial_dst_cid_provider0Ahnew_with_abstract_socketAhremote_address_validated0Ahset_max_concurrent_pathsAhsupported_verify_schemesAiCONNECTION_ID_LIMIT_ERROR0AiEncryptedClientHelloErrorAiExportKeyingMaterialError0AiInvalidCertRevocationList0AiNO_CID_AVAILABLE_FOR_PATH0AiTRANSPORT_PARAMETER_ERROR0AiUnsupportedOperationErrorAiadd_nat_traversal_address0Aidatagram_send_buffer_size0Aigenerate_self_signed_certAiopen_bidirectional_streamAipath_retire_connection_id0Aiset_default_client_configAiset_path_max_idle_timeoutAjDEFAULT_SUPPORTED_VERSIONSAjUnusableAfterNetworkChangeAjdatagram_send_buffer_spaceAjincoming_buffer_size_total0Ajmax_concurrent_uni_streams0Ajopen_unidirectional_streamAjset_max_concurrent_streamsAjtry_with_platform_verifier0AkHashedConnectionIdGeneratorAkInvalidEncryptedClientHello0AkPATH_RESOURCE_LIMIT_REACHED0AkRandomConnectionIdGeneratorAkenable_segmentation_offload0Akmax_concurrent_bidi_streams0Akupdate_from_arg_matches_mut00000Akupdate_local_if_same_remoteAlconfigure_tracing_subscriberAldatagram_receive_buffer_size0Alinitiate_nat_traversal_round0Alon_spurious_congestion_event000Alreceive_bidirectional_streamAlremove_nat_traversal_address0Alset_path_keep_alive_intervalAmFixedLengthConnectionIdParserAmInappropriateHandshakeMessage000Amcongestion_controller_factory0Amdefault_path_max_idle_timeout0Amnegotiated_key_exchange_group0Amreceive_unidirectional_streamAmsend_observed_address_reports0Amset_max_concurrent_bi_streamsAnaugment_subcommands_for_updateAnmax_concurrent_multipath_paths0Anset_max_concurrent_uni_streamsAopersistent_congestion_threshold0B`default_path_keep_alive_interval0B`receive_observed_address_reports0Baget_local_nat_traversal_addresses0Bbget_remote_nat_traversal_addresses0Bbmax_remote_nat_traversal_addressesBfset_max_remote_nat_traversal_addresses0BoDEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED") \ No newline at end of file diff --git a/pr/509/docs/search.index/normalizedName/dd897f13bf77.js b/pr/509/docs/search.index/normalizedName/dd897f13bf77.js new file mode 100644 index 000000000..875b292af --- /dev/null +++ b/pr/509/docs/search.index/normalizedName/dd897f13bf77.js @@ -0,0 +1 @@ +rd_("b()b->bbi0bce00bfnbidbokb[]beq000000000000000000000000000000000000000000000000000000000000000000000000000000000033333333333333brtbtybu8cack0canycarccbbr00cboxcbufcclicdir0cerrcmax000cnotcopt00cordcpincuni0cvecBc[5,\"noq_proto\",\"noq_proto::shared\"]2Bn[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\"]7Bi[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\"]=Bn[5,\"noq_proto\",\"noq_proto::config::transport\"]Bi[5,\"core::num::error\",\"core::num::error\"]Ak[5,\"noq\",\"noq::connection\"]:Ae[5,\"noq\",\"noq::path\"]i[5,\"noq\"]Bb[5,\"noq_proto\",\"noq_proto::frame\"]Bc[6,\"rustls::error\",\"rustls::error\"]Be[5,\"noq_proto\",\"noq_proto::endpoint\"]Bo[5,\"noq_proto::endpoint\",\"noq_proto::endpoint\"]122Ai[5,\"noq::crypto::rustls\"]Ck[5,\"noq_proto::crypto::rustls\",\"noq_proto::crypto::rustls\"]106Bo[5,\"noq_proto\",\"noq_proto::token_memory_cache\"]Ba[15,\"noq_proto::ProtectedHeader\"]o[2,\"noq_proto\"]Bc[15,\"noq_proto::PathAbandonReason\"]Af[10,\"noq::congestion\"]Cd[10,\"noq_proto::congestion\",\"noq_proto::congestion\"]Ae[5,\"noq::congestion\"]Cc[5,\"noq_proto::congestion\",\"noq_proto::congestion\"]Ab[10,\"noq::crypto\"]Bl[10,\"noq_proto::crypto\",\"noq_proto::crypto\"]j[10,\"noq\"]Bh[10,\"noq_proto\",\"noq_proto::connection\"]9Bc[6,\"noq_proto\",\"noq_proto::packet\"]:Bn[6,\"noq_proto\",\"noq_proto::connection::paths\"]Ak[6,\"noq\",\"noq::connection\"]Cb[6,\"noq_proto\",\"noq_proto::connection::datagrams\"]f[3,\"\"]i[5,\"noq\"]Bn[5,\"noq_proto\",\"noq_proto::config::transport\"]Bd[10,\"noq_proto\",\"noq_proto::packet\"]21Bi[5,\"rustls_pki_types\",\"rustls_pki_types\"]Be[5,\"rustls::verify\",\"rustls::verify\"]Ae[10,\"rustls::verify\"]Da[6,\"noq_proto::connection::paths\",\"noq_proto::connection::paths\"]:6o[5,\"noq_proto\"]Bc[15,\"noq_proto::PacketDecodeError\"]Aa[5,\"noq::crypto\"]Bk[5,\"noq_proto::crypto\",\"noq_proto::crypto\"]o[2,\"noq_proto\"]Aa[6,\"perf\",\"perf\"]Dg[5,\"noq_proto::transport_parameters\",\"noq_proto::transport_parameters\"]Al[5,\"noq\",\"noq::recv_stream\"]Bh[2,\"noq_proto::connection::send_buffer\"]An[2,\"noq_proto::bench_exports\"]Ba[15,\"noq::crypto::rustls::Error\"]Bg[15,\"noq_proto::crypto::rustls::Error\"]Ai[5,\"noq::crypto::rustls\"]Ck[5,\"noq_proto::crypto::rustls\",\"noq_proto::crypto::rustls\"]Be[6,\"rustls::suites\",\"rustls::suites\"]i[5,\"noq\"]Bc[5,\"noq_proto\",\"noq_proto::varint\"]Be[6,\"rustls::webpki\",\"rustls::webpki\"]Ak[5,\"noq\",\"noq::connection\"]>j[10,\"noq\"]Bk[10,\"noq_proto\",\"noq_proto::cid_generator\"]Be[5,\"rustls::verify\",\"rustls::verify\"]6Bc[5,\"noq_proto\",\"noq_proto::config\"]Bg[5,\"noq_proto\",\"noq_proto::connection\"]Bc[5,\"noq_proto\",\"noq_proto::packet\"]Ao[5,\"certificate\",\"certificate\"]Bc[6,\"rustls::error\",\"rustls::error\"]5Bm[5,\"perf::noprotection\",\"perf::noprotection\"]01Aa[5,\"noq::crypto\"]Bk[5,\"noq_proto::crypto\",\"noq_proto::crypto\"]Ce[5,\"rustls::crypto::cipher\",\"rustls::crypto::cipher\"]Bj[5,\"noq_proto\",\"noq_proto::cid_generator\"]07Ba[15,\"noq::crypto::rustls::Error\"]Bg[15,\"noq_proto::crypto::rustls::Error\"]") \ No newline at end of file diff --git a/pr/509/docs/search.index/root.js b/pr/509/docs/search.index/root.js new file mode 100644 index 000000000..fdc2e46b7 --- /dev/null +++ b/pr/509/docs/search.index/root.js @@ -0,0 +1 @@ +rr_('{"normalizedName":{"I":"AQQA9PsRAAABAAEAAQATgAKgIAAAD9VlcwEAAaBQAAATOmvzbgAAAPID2AEBAwGwIBFbAAFl8rgSAAABAAEAAbCAE1YAAXX0pwAAACkB9AcBAPMAAjRh984BAABrBGQK4gABAEwCAQABAwGwIBFZAAFl8rYSAAABABMAArCAE1YAAWF1OjAAAAEAAAAAAAkAEAAAAKcA0AHECcUJhBCcEH0RfhHKE8sTAQAA9qYAAADnAEIA8wcBAHYDE4ACoIAAABQaMnMBAASgAAAACXygEAAABjmwAARgAdiggAAAFBkwNThz9qYAAADnAEIA8wcBAHYDAQYA9LURAAABAAEAAQABgAKwAARgAdiggAAAFBk4c5MABKCQAAAUGqAAAAABzjIzNnLz2AEAAAEAAQABAgD1bggAAAEAYAHdAnAEIwACsLAS4gABbnL01QEAAAEAAQClBwEBADswAAABAADdAAIA7gEAAGIE3AABAwD00wwAAAEAAQABAAEAAbBQFCEAAXIBZQUAAAGAAqAQAAANOaAwAAAQhzNzAAdlamVjdGVkAAD0+xEAAAEAAQABAAAAB2NjZXB0ZWQAAAH6EQAAAPICdHQCAGFyAQUIAAAA8gFvAQFydfLvAQAAAQAAtgAAAAEEsEASrgABsEAUWwABcmRlaHQAOjAAAAEAAAAAACgAEAAAAFIEUwRUBDEGjwmQCbcJ5QkiDd0PShBLEFgQhxDeEN8Q4BAnEY8S6xLsEu0S7hIZEycTKBNUE1UTvxPAE8QTxRPGE9YT1xM5FDoUUBRRFHUUdhTygAEBZWkBAwD0kwkAAAEAJgMBAAEDAPO3DAAAAQABAPOAAmlvAYACoEAAAA/JsCANWgABZXABBQGwMBMHAAFo8+0TAAABAGsAE4ICoDAAABDRY3ABAgA7MAAAAQAA4gACAGkMAABcDeEAAQEAOjAAAAEAAAAAAAcAEAAAAOUR5hEaExsTcBNxExcUGBTzBgFpAQwRAAATggKwgBMtAAFudAEBAbBQEQ0AAWP06AcAAAEAAQD8ASOAAqBQAAAQ0mNuABBuZ2Nvbm5lY3Rpb25zaWRlAAABeRMAAAAAAmVuAADyAwgAAAEAAAAIYW5kc2hha2UAAPScEgAAAQABAAEAAAAEcnJvcgAA8lcNAAABAAAABGh1bmsBANAAEO4AAXMBYRAAAAAABWh1bmtzAAAB+BIAAADyAmxsAQBjAR0NAAAA8gCEgJUAAPRpBQAAyQABAAEA9SANAAABALECAQABAAADYmxlAADyrgkAAAEAAPKBdAMAYWV0AApwdWRwc29ja2V0AADzmRIAAAEAAQAA8oADAWFpb2UACmFydGluc3RhbnQAAPJtEwAAAQAAAAhuZ2xlY2VydAAA8vYSAAABAADygAIAaXQAD29vdGNlcnRpZmljYXRlcwAA8hsUAAABAAAABGVmaXgAAPJfEAAAAQAAEoABAbDgFGEAAXJsAAZuaXRpYWwAAPTqEAAAAQABAAEAAAAFcnlwdG8AAPJdEAAAAQAA9gEAAWgFAaDQAAAUPWNpcHJzYQDyAhAAAKkAAANkb3cAADswAAABAAAHAAEAzQcHADswAAABAAAcAAcATxACAJsRDQDCEQIAlBIBABcTAQBnEwEA+RMCAPKAAgBudAAJYXRoaGFuZGxlAAABiREAAAAABWFuZGxlAADyWxAAAAEAAAAPb25uZWN0aW9uaGFuZGxlAAABsxMAAADygWsDAGNocBKAAQKgAAAAE66wsBNcAAFhZHgAA2RsZQAAARwNAAAA8gJpdAEAaQABYhMAAPZ1AACEiKCQAAAUoUPVR2LrrqEQAAAUPREBAYRwFgA7MAAAAQAAMgEMAJkBIwDAAQYAOwYAAGYG4QDNBwcATxACAJsRDQDCEQIAlBIBABcTAQBnEwEA+RMCAAEFAPZCDQAAAQABAAQDAQAtAwEKAaBAAAAUHWXyRxQAAAEAAYADsHARdgABsCAUOwABsGATOAABYWRvAQkA8+sSAAABAOoA8QABRWH1nRAAAOIAAQBMAgEA8QABQ2H2hBAAABgA4QABAEwCAQAAAmlwAAA7MAAAAQAA3QACAO4BAABiBNwAAAEFADswAAABAAAHAAMAwhECAGcTAQD5EwIAAQcA9okTAAABAKIAVAAWAAEAAYACoRAAABR9sIAUdQABaXUBBgD0FhEAAAEAtgABAAEFAPQ0EwAAAQCSAAEA8wABYwGgEgAAAQMA9ksNAACuAlkAAQCyAgEAAQQAOzAAAAEAAAoABACWEAUAwRIBAB8TAQA4FAAAAYYCoAAAABNBoAAAABNCZHLzAgF2AWwTAADzgAJlbwGAArAgE4cAAaAgAAAP+GJkAAhlZ290aWF0ZQAA8sgSAAABAAAAB2lzbWF0Y2gAAPL4EQAAAQAAMgFuAgGAE2kAEO5tbnM7MAAAAQAABQABAHQJBQA7MAAAAQAABQABAEkTBQASgAECoLAAABOusIAUIQABb2R6EoABAU3L1cGCLmlhAAlzaWduYXR1cmUAAAEaFAAAAAgAAAABGRQAAADyg2xzMQIAMjMACHJ2ZXJjZXJ0AAABbBMAAAASgAEBoFAAABRBZWPyAAIAc3TyywcAAAEAAAALdWlsZGVyZXJyb3IAAAGyEwAAAPIAAQBiAPNCEwAAHwEBABKAAQGgAAAAE0FyZBKAAQGgUAAAFAtlY/KBZgIAaXn2CgAAAgagcAAAEpKgsAAAFAtpc2FjZW52eQA7MAAAAQAADAAFAFcGAgCrBwQAmgkBALEMAQDSEAAA5kIAAIKJwAAAAACIEXcFctfeO8n6xYh7sVAUmAABsHATwgABoTAAABSMBAABC2EaAPLbCQAA0QYBgAKgUAAAERSgoAAAEzprcBOAAqAgAAAQrGVpAQUBsGAUSQABZjswAAABAAAGAAMAZAwCAHITAQD3EwEAAA1vdW5kc2V4Y2VlZGVkAADysBMAAAEAAPoaAQBi82AGAAABAGkBABKBaQEBoDAAABHZbmEBAgA7MAAAAQAA7gACAC4NAQDLDuwAAAdhcmlhbnRzAAAB2REAAAAAA251bQAAAY0MAAAA8gFlAgBldgDzgBAAAJMADwIBAgD0rxEAAAEAAQABAAEKAPOpEwAAAQABAPOBAm10AQYA83ERAAABAAEAAYICsGATMgABsNAURwABaXITgAKwEA0zAAFlaQAFb25maWcAAPTpEwAAAQBJAAEAAPIEb2tlbgEAY/L0EgAAAQAAAAVhaWxlZAAA8sYSAAABAADygm9uAgBmdAAAAAGwABQ+AAFk9FoMAAABAAEAAQAA+ogCAGVpNgYAAWSBiLEwFGQAAaBgAAARcKBQAAARBbCQEwUAAbBAEQIAAQEAALQQBwABHhQAAPIAAgFpdWUAOjAAAAEAAAAAAAoAEAAAANEJ0gktEFoTWxOJE4oTLBSAFJYUlxTygAICbHJpdPY3AADDx6AwAAAJnEAEAICoQBQBADowAAABAAAAAAAHABAAAABEBEUERgS+Cb8JxwxHEUgRAQUAOzAAAAEAAAcAAQAzEQcAAQABsFARswABYwGNDAAAAQECsIAT8QABoDAAABCgaXUB4hMAAAEBADswAAABAAAGAAIApQcFAJcMAAABAQD19BAAAAEAAQABAH0C84ACZWwBAAGgkAAAE2Bo8ikTAAABABMAA7BAEfIAAbAwE0MAAWRlcvN8BQAAJAIBAAEOAPR5FAAAAQABAAEAAQUA9LoSAAABAKMAAQABBwD0chMAAAEAhAABACOAAqFAAAAUWWJ2AAJ6ZQAAATEGAAABhxAAAPYFAACBhrAAD9AAAQABAJcABAA6MAAAAQAAAAAACQAQAAAAvQe+Bz4NPw3ZD0UQkRCSEJMSYRMBAQD1ygkAAAEAAQABAAEAAAZyYm91bmQAAPJZEAAAAQAAEoABAUPX7rpDV2VvAANhdHMAAAHpEAAAAAgCAAABWBAAAADyg2FkcwIAaXQSgAEBsAAGMAZDb2UABHJhZGUAAPJyCQAAAQAAABBvY2FsaWZzYW1lcmVtb3RlAAABcxQAAAAAAnV0AAA7MAAAAQAABQABAG0UBQAA8g1yb21hcmdtYXRjaGVzAQBtOzAAAAEAAAUAAQAtFAUAADIDYXRlAgKwQBKzAAGgAAAAE/VmbGVzADswAAABAAAUAAUAMxEHANUSAQBPEwQAJRQEAJAUAAD2AgAABAKgkAAAEyFkZ2xwY2kAOjAAAAEAAAAAAAcAEAAAADoGOA26ErsSXhNfE4oUixQBAwD28wcAAAEAjgB4B6UA5gEBAAGwcBOwAAFl8vAPAAABACMAAqBAAAAUB2Vz9FkQAAABAB0EAQAAF3NhYmxlYWZ0ZXJuZXR3b3JrY2hhbmdlAAABWRQAAAAABmVyc2lvbgAAOzAAAAEAAAUAAQBJEwUAAAANcGVyYXRpb25lcnJvcgAAAU0UAAAAAAdhbWV0eXBlAADydxMAAAEAAAAFeXBlZXIAAPITEwAAAQAA8odwcG9ydGVkBABibm92AAd1Y3R1cmVkAAABoBAAAAASgAEBsJAT5wABcmEAB2VjaWZpZWQAAAEBEAAAAPKAAwBwdHUACWVjdnN0cmVhbQAAAXYTAAAA8gZyZGVyZWQBAHIA8qkRAAAYAAEAAPc9BgAACwEBAP0IqAABAAkCAANvd24AAAECCAAAAPIAAQFucwD1QQUAAAEAlAoBAIoAAQYA810UAAABADUAAQIAOzAAAAEAAIUAAgD4EAAAABKEAAAEdGltZQAAAa0JAAAApgEAAAIDwAAAAARhsQAUXwAtdHhkbnPyhgAAAAEAOjAAAAEAAAAAAAsAEAAAAOsH6QlgDFkNChELEY0SjhIRExITnxOgEwAKeHBlY3RlZGVuZAAAARsRAAAA9lMAAIaFoGAAABMssGATVgABoHAAABKREEUKDgAFAPLLAQAAAQABDQD0NBMAAAEAkgABAAEKAPXEEwAAAQABAHMAAQAAAXgAAPTGBwAAAQABAAEAAAgVAADyqwkAAAEAAAACZWYAAAFWDQAAAAAEdGF0ZQAAAYgRAAAA+hwBAHMBjAwAAPReEQAAOwEBAAEA8oJjawIAZXIABG5kZXIAAAGLDAAAAPKAAwBlb3QBAwD0ixMAAAEADwABAAAAAAD0wgcAAAEAAQABAADygAEBeG/yAAMBcnN0cAHRAQAAAXoIAADygAEBcGkAATQAAbCAE1YAAXUB0AEAAPLECQAAAQAAATIAAAHPAQAA88IJAAABAHYDAIACAOOAAAAEYMAAAAABzjI2di9IAAgLwAAAAABuUkTWY5qWFGAP9beFDuhYHOtLdUWZbw3XehBES5lKE7xXE4U9BOKL7FJKawKaQzBwMTM2OGRucHNiY2VmZ2lsbXJ0dQA7MAAAAQAACgAEAHgIAQDKDwEABxACAA8QAwABAAA7MAAAAQAABQACAMYHAwC6CQEAIYACsCAQAgCpq2lvAYUCoAAAABNBoAAAABNCZHIjgAKgAAAAEIQ0ZQEJAPX+EwAABQABAAEAAQABgAKwgBMJAAGhAAAAFFZjZhOAA6AgAAARRbCQERkAAWVvcwECAbAAD8cAAXMBMhEAAAEAArCAFGsAAaBgAAAUj2Rz8noJAAAUA5OAhLDAE7AAAaAQAAAR1QDBQPEAAchy89gBAAABAAEAAQQA9IgSAAABAGIBAQABAAD0tBAAAAEAAQABAAggAAA7MAAAAQAA4gACAF8GAACQCOEAAPonAQFpcwA6MAAAAQAAAAAADAAQAAAAjgnGCccJyAnJCXQMdQy/D8AP5Q8BEXcTeBPyAAEBcGwBbQAAADswAAABAAAIAAMA6gkBAOQQAQA/EQQA8QEBKHPyYAwAAPkAAQUA9IsTAAABAA8AAQDxgAIkAGlwCAAAAAEwBgAAAXMMAAASgAEBoJAAABMhbGN0AgAAAQUAAgRJi678Hqp8BMsRjtZacGRucnN1ADswAAABAAAKAAQAeAgBAMoPAQAHEAIADxADAAEBAaAwAAAQoHUB4hMAABOAAqBAAAAShWNuAQYA9P8SAAABAIYBAQATAgKwYBMBAAFmbfIsDQAAAQAjgQKwQBMtAAFlbAEBADswAAABAAA/AAMAXgYAAKMJAADYDD0AAQEAOzAAAAEAAAkAAgBwCAcAARMBAFOAA6AQAAAHwWNkbgEBAPXODAAAAQABAAEAAQATgAKwsBPTAAFhbwATaXRocGxhdGZvcm12ZXJpZmllcgAA8mEUAAABAAAAA2VuZAAAAVkMAAAAAANvbGwAADswAAABAAAJAAEATwwJAAAAA250bwAAOzAAAAEAAOIAAgABCAAAbQvhAAAABHR1cmUAAAGKDAAAAAAHbnRlcnJvcgAAAfcRAAAA8gJvbQEAaTswAAABAAD0AAIAAAgAAHkK8wAA8oACAHJ19gUAAIWCsEANRgABIIESEAAEADswAAABAAAJAAQAYQUBABwGAQCzDAEArxEDAAEHAPR7EwAAAQABAAEA8wEBdDswAAABAAAIAAIAYQwAAI8MBwABAAGgUAAAFD1zAZASAAAjgAKgwAAAFHRpdAGCArBAFAEAAaBQAAAR5GJmAAAAADswAAABAAAGAAIAdRMAANoTBQAACFQAAPJLFAAAAQAA8ohhcmFtZXRlcgIAZXMAA29kZQAA8kcTAAABAAD6AgEAY/OFEQAAAQABAAAIMwAAOzAAAAEAAAcAAgD0EQIA7xIEAAEMFAAA8gNvcnQDAGNlcPMZDQAAAQABAAABAAGwYBPzAAFlASoUAAD6RgABc/OoCQAAAQABAPNGEQAAPQATAQAFZXN1bHQAAAGEEQAAAPICZXIBAHIAASYRAADygAMAZm1w8oABAXNkcoABA08dve1H3m5jcna2AwAAAgRonSxsgMZheWVpb3UA880BAAAPBuUBAQMAOzAAAAEAAIUAAgD4EAAAABKEABOBAqBgAAARqWlvAYACoKAAABQSsBAPygABYXQBAgGgMAAAEINhAa0MAAABgAKwUBErAAGwIA/BAAFjbvOAAm50AYECsKAT/wABsKAUAQABY3ITgAKgIAAADHllcgEEADswAAABAAAOAAIAMg0AABYQDQAjgAKwUA/wAAFidQEBAaBAAAARgmUBRxAAABOAArAwD+YAAW5yAQAA9GMTAAABAAEAAQATAgKwgBM4AAFzdvTsCQAAAQD2CQEAAYUCsHAT/AABsDATVAABZXMBAgA7MAAAAQAABwABANMJBwAAA2l6ZQAAAd0PAAAAAAd1cmF0aW9uAAAB2BEAAADyAmFsAgBkcwDyWxQAAAEAAARyaW5nAAA7MAAAAQAAPgACAKMJAADYDD0AABKAAQGgUAAAEYt0ZQEGAPT7DwAAAQDqAAEAAQEBoEAAABCeZTswAAABAAAHAAIA9wcFAIgIAQATgAKwIBKsAAFlaQALc3NpYmxldmFsdWUAAAEiEwAAANIAAQOgUAAAEIVvZXBzAPNXBAAAAQABAAADbmVkAAA7MAAAAQAAhQACAP4HAAD0CYQAAAAMYW55YWRkcmVzc2VzAAABxRIAAAAAAm5nAAAB/wcAAAAAA3JnZQAA8qYJAAABAADygAIAYW/ygAMAbG13AAZ1bnRpbWUAAAGfEAAAAPprAQByAS8GAAAAAQUA9OkTAAABAEkAAQAABHRvcmUAAPRUDQAAAQABAwEA8ngRAAABAAAJZXVzZWVycm9yAADy8hEAAAEAAAACb3MAAAEYDQAAAAAKZW1vcnljYWNoZQAA88MSAAABAKcAAAABZwAA8qQJAAABAPWIEAAAAQBpAAEADAESgAEBsGAT0wABb2kAAmV5AADyFg0AAAEA8gMTAAABAPpWhoEAnAMEAAABLgYAADowAAABAAAAAAAHABAAAACTCZQJugy7DAIRAxH0EvUS8oACAGVp9v0LAAUMoDAAAAysIQ28u9f3sNAUIQABa29wc3QwYWJjZWZnaW1ydXYAOzAAAAEAAF4CCgDdAQAAPgPhAEIGAQBVBwoAAQgAAG0L4QC8DAAAPg6EAF0QAQCwEAMAAYACsXAUmgABoNAAABQTY28BgAKgkAAAFBmgkAAAFBoyMwAGZXNzaW9uAADyUg0AAAEAAPIAAQFzMQA7MAAAAQAABgACAKUHBQCXDAAAkoABA7AABiwAAbBgEOIAAXNlaW8BDQA6MAAAAQAAAAAABwAQAAAAOxM8E9ET0hMIFAkUVBRVFAGAAUQX/A0XpW4BAAGwgBQfAAFw8hsUAAABAEMFA6AgAAARX6BAAAASpWRlc/MGEAAALAMBAAECAPO8EgAAAQABAAEAAPU/EQAAAQABAAEAAQAigAEB9lAGLAABbHkBAAD0twwAAAEAAQBmAAECADowAAABAAAAAAAJABAAAADtD+4P2RCREd4S3xLVE1cUiBSJFAAIaHJlc2hvbGQAAPLWEQAAAQAAAAVvdXJjZQAA9FANAAABAAMDAQAAAANvdXQAAPOgCQAAAQABAAB2AwAAg4OgQAAAEfEIAAYQQAEAOjAAAAEAAAAAABcAEAAAAPMH9AeCCK0J3AndCU8N2g/bD/oPnxCmEKcQqBCpEOIQ4xAZERoRhRLTE9QT/xMAFBKAAQGwMBCTAAFlYbYqAIDCyW/oNuESN6AgAAANHC5KtIg9L1E5S4QZ5qAQAAAQrAAABALACrgIAQMBoKAAABShd/KRFAAAAQATgAOw4BRhAAGwQBBfAAFhbHIBCwD0AxQAAAEAAQABAAEMAPRwEwAAAQCmAAEAI4ACoHAAABQrZ3fzgAJlbwEMAPNXFAAAMQABAPMAAWEBbxMAAAEQAPOAFAAAFgABAAEFAPTqEAAAAQABAAEAAQABoGAAABK1cvdPBgAAAQAwAvsDAQCoAwEAM4ADoBAAABKYZG5zAQkA9OgRAAABADMBAQABBwD0EREAAAEApwABADOAhKEAAAAUgbBAEF0AAYBBAQEBArAgDTwAAbAADMAAAWVz8u4HAAABAAEEAbBQEwsAAXL08g8AAAEA5AABABOAAqCwAAAUPWFz84ACYmMAA2lzdAAAAZgSAAAAAAJwcwAAAdURAAAA8gVnaHB1dAIAYmgB3A8AAAASgAEBsNAUGwABdW8SgAEBEWuSmh4Nb2W2vAAAgY2gIAAAB/AUc+QUG3CgUAAAEYmg4AAAE/ZJRiU1UE6w0BPnAAEAAAGX9QoAOjAAAAEAAAAAABsAEAAAAOMB5AHlASoEKwSWCX0Mvgy/DMIMIw0kDSUNJg3DDgIQhRCrENEQdhF3EbsRvBHwEzUUNhRJFEoUAQEA8+IHAACLCwEAAQoA9GMTAAABAAEAAQABgAKgMAAAE66gwAAAE+JveWMAhKBwAAAR2KAwAAAP3QiIAvJbFAAAAQABgAKgAAAAEJygAAAAEJ00NvMCAXbyTA0AAJMCAQQAOjAAAAEAAAAAAAcAEAAAAOoP6w+tEK4QDxMQE50TnhMBAAKgEAAADNewUBPnAAFpb/KuCQAAAQDzgQJlbwEHAPSCEwAAAQABAAEAAQMA9HsTAAABAAEAAQD6KQABcjswAAABAAAFAAEAWgQFAAABgQEsXcsYWgxtCHkAAPKOCAAAAQAA8gABAWxyAPXODAAAAQABAAEAAQD2DgAAgomgYAAAEfqgcAAAE5oj+FavkTklBYe/m6tABAAPaAUAOzAAAAEAABEACAD6AQAA5gcBAJwJAADjCQEA/RABAAgRAQCSEQUAixIBAAAACBJLv0HR9SZ5Y6VG055WCsRU1oIXiKnKb5Uv0tJPGjtBHZ95KXVTayUESwYd4YcP86eAAdUAEAFmXiMHcMdHKxeND5htZXtCY9gAtzGa66QgReXqzHOwsBRhAAGg4AAAE5WgMAAAD9V2Q4iaKUsNPCQLZ0Zhg3JcqOEWdgYpoYhEz81DsCk1Lg2ZBTQOBP/mTCAxtV51E+txLuPFxkBhaGlsb3J1eTAxYmNkZWZnamttbnBzdHZ3eAA7MAAAAQAAXQRkAGwAAACAAAMAqADkAJgBAAC+AQEAxwEDACUEAQBMBAEAYQQAAFYFAgBdBQMAYwUBABYGBQA1BgAAPAYAAD4GAgBOBgAAUQYFAGAGBQBLBwQAgwcFAIoHAQCUBwIAygcAANkHAQDiBwAA8gcAAAUIAAAlCDsAaggFAHsIAwB7CQAAgQkAAIUJAgCNCQAAoAkCAKgJAgC1CQEAzwkAAGkMAQCCDAIAjAwAAKUMAQCsDAAArgwBAMMMAADJDAEA0wwDABkNAgAuDQEANQ0AAFwN4QDLDuwA2A8AANwPAADtDwIA/Q8BAAUQAAATEAIAexACAL0QAQDIEAEAzhACANkQAAD4EAIAHBEAACgRAABGEQAAXhEAAGMRBgCDEQEAihEAAJERAADFEQIA4BEBAAAShACGEgMAkBIAAJISAACWEgAAmBIDAMoSBgDeEgEA9hIBAAcTAQBiEwAAbBMCAHsTAwCNEwcAuBMFANUTAADiEwAA6xMBAD0UAABHFAEAUhQAAFcUAABtFAUAeRQDAIgUAQABBAD0rxEAAAEAAQABACODArAwFD4AAWFyQ4ADsAATygABsAATzAABNDZhAQcAOzAAAAEAAAUAAgAlFAQAkBQAAAEBAaBQAAARhHIBJhEAAGOAhKAAAAAB06BgAAATzhhAAQGAA7BgE7YAAaBwAAASoLBQD8MAAWVpcgGAAqAAAAAGOKCAAAAUGThzE4ACoJAAABQaMjMBAwDzbggAAAEAYAEIIAAAAfERAAAA8gd0ZW10aW1lAQBlAU8NAADyGREAAAEAAAFjgISwMAxiAAGgQAAADR+ggAAAEV6wQA0gAAEAAB0B7QEAAADygAIAbnMBAAD0ChEAAAEAggEBACOAAqBgAAATLGJpAQEAOzAAAAEAAAYAAgBRBgUAhBEAAAAAAADyaRMAAAEAAVgUAADyA2lvbgEAcwA7MAAAAQAABQABAEkTBQAACWZ5c2NoZW1lcwAAAUEUAAAA+qACAGlzAAppcGhlcnN1aXRlAAABrxMAAAA2AACGcG9ydGVkAgOwUBMTAAGwcBN3AAGg0AAAFE1jdmJubwACdGUAAaAAAAATN3MBaAUAAPTgDwAAzAMBAAIAAQABoIAAABSQZgF/EwAA8gZvbW1hbmQAAXMBTg0AAAGaEQAAEgABAaBgAAAUdGNzAPLTEQAAAQD2AQCAg4OwEBLmAAECgQAAKAEBgAKwUBDKAAGwsBOLAAFhbAECAbBwFDsAAXA7MAAAAQAACQACAGIIBwDVEAEAAQMBsAAPxwABcwEyEQAAAQIBoDAAABCgdQHiEwAAAQIBoFAAABQ9cwGQEgAACBUAAAFeBgAAOzAAAAEAAD4AAgCjCQAA2Aw9ACKAAgDmUAAAB8FkbgECAPQKEQAAAQCCAQEAAAAAAPP/DwAAAQDoAAHnEQAAAAVlcnJvcgAA8j8TAAABAADygmF0AgBlcwECAPR0EQAAAQBrAQEAAAJuaQAA9BETAAABAI0AAQAAAANpZGkAAPRFEwAAAQCSAAEAAPKFb2NrZWQCAGJ18oABAWxp8gACAWJ0dfX9BwAAjQABAAEAAQA6MAAAAQAAAAAACgAQAAAAJhDhEJYTFBRdFF4UYBRrFGwUjxSTFAAMZWNlaXZld2luZG93AADz+RMAAAEAAQAAAAlpbWl0ZXJyb3IAAPI9EwAAAQAAAAJmbwAAAU0NAAAAIoACANAACZ4AAWRuAAdpbmlzaGVkAAABlxIAAAAABHZlbnQAAPL9DwAAAQAAAAZsb2NrZWQAAPQPEwAAAQCNAAEAAPIDYXRhAQBiAPQIEQAAAQCCAQEA8gJhbYeAOAkDOzAAAAEAAAUAAgBaBgMAvwcBADowAAABAAAAAAAcABAAAABCDUMNRA1IDUkNSg32D/cPSBBJEE0QThBrEGwQbRBuEG8QcBBxEK8Q3BDdEHYTmhO+E1MUXxR9FIwU8gACAmVpYXUBzQEAAPLcBwAA5QEBAQD3VA0AAAEAAQMBAMEAYAABAAAGZW5kaW5nAAD0+w8AAAEA6gABAAAIGAAAAZ4QAAAA+kcBAGU7MAAAAQAABwACAPcHBQCICAEAABKAAQGwIBKsAAFlaTIAAgGgUAAAEIVwc2XzVwQAAAEAAQAAUoABAqAwAAAMeXBncgAKc3lzdGVtdGltZQAA8hkRAAABAAABAAGwQBM/AAFl84gRAABJAQEAAQgA9HITAAABAIQAAQASAXMAAyQSCSXQm7BAE0MAAWFiZfK9BwAAAQA6MAAAAQAAAAAABwAQAAAAPg0/DdkPRRCREJIQkxJhEwAAAAA7MAAAAQAACwACAGYFAQAiBgkAOjAAAAEAAAAAABMAEAAAAKsJrAmFDIYMMA0xDcgOzA/ND84P4Q//DwAQ6BDpEPsQjBHiEeMR5xHygAIBc3VlAANpbWUAAPLaDwAAAQAACCYAADswAAABAAAJAAEATxEJAAA60QIBsGATbQABc3RpAIJsZQEB5kAAAAzXsFAT5wABaW/wgAMBAAEEWGJydG72QwAAxMh0ogL/L86wQAnyAAF99b0O+tQHmkyPbgmgEAAAEIRAApAAgGUiCAA7MAAAAQAAEgAIAPIHAADvDwAAyBABACgRAABjEQYAmBIAAHsTAwBHFAEAAYADsAATygABsAATzAABsHAUPgABNDZhAQQA9I0UAAABAAoAAQBDgQOwwBPxAAGgQAAAEV1kbXABAQA7MAAAAQAADgAEAPUHAQBSDQEATxEJAIsRAAAjgAKwcBETAg9ibwEAAbAwDVIAAXM7MAAAAQAACAADAMUSAAA2EwAAmhQGAEOAA6BAAAAMmLAwDLEAAW1ycwEBADswAAABAAAKAAMAcRECAKUTAwCCFAMAI4ADoDAAABBkoLAAABNZZGdtAAVocmVzaAAA8vIJAAABAAC2CwAAAQeggAAAEqCggAAAEZp0YWRlaXJ1dgA7MAAAAQAAGAAJAAMQAQAWEQEASxEBAFkRAwDIEQYAqRMCAPYTAABOFAEAfhQBAIGAhLDAE/EAAaAAAAAFP6BAAAARXQkokAAAAXAAAPO6BwAAAQABAAAAAmlkAAD1gwgAAAEAAQABAAEAAPKAAgBjaTKAAQKgQAAADJdjZXUBFAD0eRQAAAEAAQABAAGAArBgFB8AAaDQAAAT8GVvAYACoFAAABABoPAAABQqY24ILQAAAVYEAADyWg0AAAEACAAAAPMfBgAAAQABAAASgAECsBATHgE8sMAThwABd2N0dgYAgIKFoCAAAAfbTdfhT9DDAQEAEEgJAQAAOzAAAAEAAAsABQCYBwYA1Q8AAOIPAACqEAAACxMBAAADcmNlAAGwsBRmAAFsOzAAAAEAAAgAAQCxBwgA9FANAAABAAMDAQAAgAIAwAAAABCcwAAAABCdNDbyA2RkcgEAdgFMDQAAABoIAQGgQAAAEYhhcwD3jAwAAHkDWQE7AQEAAQCiARKBawEBoCAAAA1WZXJ2AACAAgOgAAAABkSgcAAAEt1jdW5ycwEAADswAAABAAAMAAMAewkAALgTBQBtFAUAAYYCoGAAABPBoJAAABQrZW7zgAJsdPEBAd1z9qgJAAABAAEAnAc9ABMBCDsAAAH6DwAAAPIBbAEAcgFVBAAAANKAAQOwMBH4AAFvYWl1AANpY2UAAaAgAAANNW0BHgYAAAAAFGlwc2VydmVydmVyaWZpY2F0aW9uAAABCxQAAAABAwDzsBIAAAEAAQAjhAKxIBSUAAFjawADaW50AAD00wwAAAEAAQABAAAAAAABsFAUIQABcgFlBQAAADowAgKwQBKuAAGwQBRbAAFkaGV081IEAAABAAEAOjAAAAEAAAAAACQAEAAAADEGjwmQCeUJIg3dD0oQSxBYEIcQ3hDfEOAQJxGPEusS7BLtEu4SGRMnEygTVBNVE78TwBPEE8UTxhPWE9cTORQ6FFAUURR1FHYUAYECoDAAABRSoFAAABPiY3MABWNoZW1lAAAB8BEAAAAIMQAA8k0RAAABAAA6WAIBoEAAABQebHN2APIZFAAAAQD6DQEBYWXyUAQAAAEAAAgEAAGgkAAAE65u9OsBAAABAGICAQABeRMAAHYEAICDhLBwERMCD7BwEvYAAU4eZd0JcUgAgAJgAgEAAaAgAAAP7HIBRgYAAAEAAqFAAAAUY6BAAAAQ/GNl84gJAAABAA4J84ACZHIBAQA6MAAAAQAAAAAACQAQAAAA1hHXEegS6RLPE9ATIxQkFJQUlRQACmxkdHJhbnNtaXQAAPKDEQAAEwEAYoACAfdABWMAAXJ1bAEIAPWqEQAAAQABAAEAAQAAAmVkAAABsAcAAABSgAECQTfTUBABcmtucgACAqBQAAARRWFvZXUAOzAAAAEAABwABQDcAQAAKQMUAHsHAgDyCQEA0A8BAAEGAPX0EAAAAQABAAEAfQIjgAKwUBOwAAFjaAEDAbBAEWAAAWUBww4AAAGAArAACfAAAbAwEfIAAWRyAAtwY29ubmVjdGlvbgAAASETAAAAAQMA9PYPAAABAOUAAQAAA3R1cwAAAdkPAAAA8oABAWFyAAl2ZXJjb25maWcAAPIfEwAAAQAAAAVpbmRvdwAA8pQSAAABAAAACXVmZmVyc2l6ZQAAAdcTAAAA+qECAGJ38oACAG5y8oACAGV0AApidWZmZXJzaXplAAAB1hMAAAAACWl2ZXdpbmRvdwAA8mcTAAABAADygmVjAgBldgAGaW9yaXR5AADy5BAAAAEAAAgnAADyQxMAAAEAAPIFdGF0dXMBAGUBkxIAAAAADWF4aWRsZXRpbWVvdXQAAAFXFAAAAAAQZWVwYWxpdmVpbnRlcnZhbAAAAYAUAAAA8oJ0aAMAa21z8oACAGFyABplbW90ZW5hdHRyYXZlcnNhbGFkZHJlc3NlcwAA8p8UAAABAAAACmRsZXRpbWVvdXQAAAHVEwAAAAAJbmlzdHJlYW1zAAABkxQAAAAABnRyZWFtcwAAAWAUAAAAAARhdGhzAAABQBQAAAAACGlzdHJlYW1zAAABjxQAAADyiW9uY3VycmVudISAAoAK8oJheAMAY2lyAAdpZmV0aW1lAADy4hAAAAEAAAAOcGFsaXZlaW50ZXJ2YWwAAAEsFAAAABqbAQGwAAzMAAFleQASZWZhdWx0Y2xpZW50Y29uZmlnAAABVhQAAAAADGxwbnByb3RvY29scwAA9GMTAAABAAEAAQAA9h8AAImCoEAAAA1FsHATWgABCZwLEAEAADswAAABAAALAAQAXQUDABkGAgCKBwEAxRECAAhPAADy9QcAAAEAOzAAAAEAAAwAAwBSDQEATxEJAIsRAADyAAEAcwA7MAAAAQAACAADAMUSAAA2EwAAmhQGAAEAADswAAABAAALAAIAgwcFAMoSBQAADWlkZW5vdGFsbG93ZWQAAAGuEwAAAAADYW1lAAD0Sw0AAK4CWQABAPIFEwAAAQAAB25kcG9pbnQAAAGSEgAAAAAEbmZpZwAAOzAAAAEAAAUAAQCWEAUA9cESAAABAF0AAQAYAQCGZXJpZmllAgDAAAAAE0HAAAAAE0JkcvICcnQBAHYAAWwTAADygAIAZW8IMgAAAfgPAAAA9gEAAAUBoLAAABQLYWNlbnN2OzAAAAEAAAcAAgBXBgIAqwcEAPKxDAAAAQASgAEBMj5G4AFpcmQSgAEBoEAAABCsZWGSAAEDsEAP4gDIsCATQwABdmVydADyKxMAAFYBAAxscG10dWRwcm9iZXMAAPKbEwAAAQAAEgABArAQFB8AAbDgFCEAAXBlb/JMBAAAAQD0ewgAAAEAAQABAAECAPT7DwAAAQDqAAEAAQEA84sMAAA+A1wBCCEAAPNPEAAAAQABAPKUEgAAAQAACW5yZWxpYWJsZQAAAZESAAAAAAV0cmVhbQEAwAAAABDhc/VIDQAAAQABAAMDAQAAABVic2VydmVkYWRkcmVzc3JlcG9ydHMAAPKNFAAAAQAAAAdhaXJuZXNzAADySxEAAAEAAAAHbnN0cmVhbQAAAZoTAAAAAANhaXQAAAFiEwAAAAghAADyDRMAAAEAAPIDcmFtAgBld/KVEAAAtQAA+s4CAGdvAANhY2UAAAEeEwAAAVoUAAAAAnplAADy7RIAAAEA89cTAAB5AAEA8oACAGlwAAZlbmNoZXMAAPOXEwAAAQABAADyBXVmZmVyAgBicwFMEAAAAPIAh4IqQBoQAQD06gEAAF8CAQABAPJZDAAAbwDygAIAZHQIAgAA894QAAABAAEAAPIAAQBpADowAAABAAAAAAAHABAAAAC8EN0SwhPDE/MT9BMSFBMUEoRtZW50AQGwsBRpAAFzYeYTBQDGyfbABEcAAaBwAAASjys5H7IxwaBwAAARD6CwAAAUUqBAAAAMmLBgEoYAAUAQiAOARmYgADswAAABAAAaAA0ARAUAAHwFAAACBgMAEgYBAKAHAQCdCQAA7wkAAM8PAACMEAEAOxEBANoRAwChEgMA0xIBAAGCAqEQAAAUWbAACYIAAWFkAAJ1YgAA8tMRAAABAAAAAmRkAAD0zxEAAAEAAQABAADyh3VyYXRpbmcCAGFzAYADoJAAABNZoDAAABPwoFAAABRzY3ByAAZsZXNpemUAAPJKEAAAAQAA8oABAXBldAEAgAIFAAMGoDAAABBkikUmHEXu3zCTGfTwvG10YmRnbHYAAA4KGPf+lQ6mDhKEy97KWScOwAlaB0XcW3BjF/F6D+oqQQOZFznVPiZHvGXJMe/nDJgUG+9drL1cZ+UI5xXBGS+R3pZveYySBevZUC97txkbHRoYD0UkUXnuqLJVKnpdB9htoCAAAAY5oGAAABM6CU2TeI6LZl6/c2KwIOIZn2EtLhgqbSU5HNYq/wGQoKAAABShYWVoaWtsbW9wcnN0dXkwMTI4YmNkZnZ3ADswAAABAAADAWsAjwEBANIBAADeAQEA8QEBAEAFAABmBQEAagUAAHYFBQAUBgEAIgYJAD0GAABIBwEApQcFAL0HAQD9BwAACggAAHAIBwCKCAMAfQkAAKsJAQCxCQAA6AkAAG0MAACFDAEAlwwAAKEMAwCnDAIAwAwBABgNAAAeDQAAMA0BAD4NAQDIDgAAxw8BAMwPAgDZDwAA4Q8AAPAPAQD/DwEAAxABACYQAAApEAMARBACAJEQAQChEAQAtBADALwQAAC/EAgAyhABAOEQAADoEAEA7hABAPsQAAAEEQAAFhEBACARAQBLEQEAWREDAGwRAACMEQAAtREDAMgRBgDVEQAA2REAAOIRAQDnEQAAkxIAALASAgDFEgAA1xIGAOQSAQD4EgAAARMBABUTAQA2EwEAWBMAAFwTAQBgEwEAYxMDAGkTAQB1EwAAehMAAH8TAACLEwEAlRMEAJsTAQCpEwIAwhMBAMkTAADaEwUA7xMAAPMTAwABFAEAEhQCABsUAQAqFAAALRQFAEAUAQBOFAEAWBQAAF0UAQBgFAAAaxQBAH4UAQCNFAIAkRQCAJgUCAABAAA7MAAAAQAABQACAMIHAwC4CQEAAYACsCAUSQABsCAT5wABYW8BCADzehEAAAEAAQABgAKADHprUFewQBMBAAFkdGOABKBAAAAMcrBwFGEAAWFlaXYBCwD0ghMAAAEAAQABAAEFAPSwEAAAAQABAAEA8wECY2QB/BAAAAGAA3b46ewx+LAQDHcAAaAwAAAQ0WNlcAEDAPT/EgAAAQCGAQEAI4ECoFAAABKKZXQTAAKgsAAAEzdhY/IsBAAAuwUBBAA7MAAAAQAACgADAE8TBAAlFAQAkBQAAAADdGxzAAA7MAAAAQAABQABAKUHBQABlwwAAAhTAADz8wcAAAEAjgDz+g8AAKUA5gHyAAEAdPLLAQAAAQAAsoACAlbCtcyw6W5zY3ABBQA7MAAAAQAACQABAE8RCQATAAOgcAAAE86gMAAAERhlZnT2IBEAAAEAbAMBAAoAAQABCwGgQAAAFB1l8kcUAAABACOBArBQEPAAAWN2AQAAOzAAAAEAAAYAAgB1EwAA2hMFACOIArBAFEsAAWVzAQ0BsEAURQABZfUNFAAAAQABAAEAAQABBQA7MAAAAQAACAADAPQRAgDvEgQADBQAAAEHAPT7EQAAAQABAAEAAQMA9LURAAABAAEAAQAACHN0aW1hdG9yAADykxAAAAEAAJYBAAABBKBwAAAR+rAgD9oAAWVhaWty9McBAAABAAEAAQA6MAAAAQAAAAAACAAQAAAABQhuCG8IzwmsDBMQFBAVEBwRNg8AAMHJEGLzBX2FHMh7MKneoDAAAAxzTr8+lap0AAAAAgBFoQ0BbAAAADswAAABAAAUAAcAYwUBAIMHBQAZDQIAyhIFAPYSAQBsEwAAUhQAAAEAASpLFcV/KW07MAAAAQAA4gACADsGAABmBuEAAQYA9KUTAAABAAEAAQABDwD0ghQAAAEAAQABAPOEAmht8wABcjswAAABAAAHAAIA1AEAAPsBBgABgAKwQBMtAAEr/tSQvsllbAEBAPSLEwAAAQAPAAEAI4ACoLAAABPwYWUABHRvcmUAAAEYEQAAABKFdGNlcnQBAbBwFBsAAXNpdoIAAMHLUaJN9cS0oPAAABN5TJcPQjWDoHAAABC8V/FmkbyVSi89yuP0sEAUOwABAAAQAIAArh8A8u8BAAABAAGAArAQEUsAAaCgAAAUWXN0AQEA9ksNAACuAlkAAQCyAgEAAYACsDAUFQABsEARbQABYWXzgAJsbQAEc2VlZAAA8vAJAAABAADygAECZ2FlAYACsFAT/AABoAAAAAmNY3QBgAKgMAAAFB44x3y4o1dhbwEDAPQ6DQAAeAaZAAEAAYACsBARTQABoEAAABKRZWkBBQD0+xEAAAEAAQABAAGAAqDQAAAUdKBQAAAUKmNuAAtrZW5saWZldGltZQAA8tMTAAABAAAIbAAA9c4MAAABAAEAAQABAADygAIAYW8IFAAA8kYNAAABAADyAAIAZXT0YQUAAAEAugABADswAAABAAAFAAIAswwBAK8RAwDygAEBeWEADnJlY29ubmVjdGlvbmlkAAD0OxMAAAEAlQABAPQIFAAAAQBLAAEA8oACAGlyAQAA9HsTAAABAAEAAQABAgA6MAAAAQAAAAAACQAQAAAA1hHXEegS6RLPE9ATIxQkFJQUlRTzAAFv8vIJAAABAAACbHQAADswAAABAAAFAAEAUQYFAAGEEQAAAARvbnNlAADynQkAAFIA9IwQAAABAK4AAQAIHQAA9PYPAAABAOUAAQAAAAJleQAA8swMAAABAAAIBQAAAUUNAAAAcgADAbBwE1oAAWVrc2k7MAAAAQAABgACAF0FAwAZBgIA88URAAABAAEAEoABAbAwFB8AAXRudgkAgIOFoEAAABHwsQAUZgABdxNr/ZIWEIAIhEAGAQUA9CUTAAABAI4AAQDzAgFj9WIQAAABALoAAQABAAhWAAAB8gcAAPPvDwAA2QABAPKCdWUBAXNuABByZGVyaW5ndGhyZXNob2xkAADyzxMAAAEAAAASYXR0cmF2ZXJzYWxhZGRyZXNzAADyfhQAAAEAAAAGZGRyZXNzAAD0FhEAAAEAtgABAAA6swIBoAAAABFdYW5kARQA9ZwUAAABAAEAAQABAAhCAADykRAAAAEAAAAJYXRoc3RhdHVzAAABYRMAAAAACnBlbnN0cmVhbXMAAAGWEwAAAAAMaWRzZXhoYXVzdGVkAAABdBMAAAAACGFsaWRhdGVkAADyPhQAAAEAAPIFZHJlc3MBAHb1yBEAAAEAAQABAAEA86kTAAABAAEAAAdhbmRvbmVkAADy7hEAAAEAAPKAAgBiZPoHBQFhY29wc27zogcAAAEAAQABcxQAAPKBbwIAdHYBAgDzgQkAACQDAQAACWFuZHNoYWtlcwAAAWATAAAA8gABAGgA8ikTAAABAPICc2UBAGTyoAcAAAEAAAGKArAAE8oAAbAAE8wAATQ2AANuY2UAAAHLDAAAAPKBcgEBZXKyAAICsBAQXwABZXVpbQD1PwUAAAoAvgABAE4HAQsA9FMUAAAMAB4ADwABCQA6MAAAAQAAAAAABwAQAAAAOxM8E9ET0hMIFAkUVBRVFBOAA7AQFCEAAbBgE4cAAW5ydggVAAD1Qg0AAAEAAQAEAwEAAXYTAAAAA2V0YQAAAZwJAAAACFgAAPLrEgAAAQAB1hMAAPIAAwBibXPzRAQAAAEAAQABxwwAAAg4AADzwhEAAAEAAQD1ZxMAAAEAkQABAAEAABNuaWRpcmVjdGlvbmFsc3RyZWFtAAABjBQAAAAAAAAA8poJAAABAAHSEAAACDcAAPKYFAAAAQAAAARlc2V0AADzxREAAAEAAQAAAAZ0YWdyYW0AAAHqEgAAAPIAAgBhcgDz8BAAAAEAuAEAEWRpcmVjdGlvbmFsc3RyZWFtAAABfRQAAAASgAEBsIAUdQABaXX2AwCDaXZlhoGwcBPCAAEKQBkAAAK2AQCAAgOgQAAAEtBldmxvdAAHYnN0cmFjdAAAAZASAAAA8gNpbmQBAGEBnwcAAAAIagAAOzAAAAEAAAYAAQCYBwYA9OIPAADIAGECAQASgAEBsHAShgABb2UACG5vcmRlcmVkAAABwREAAAAIIQAAAYIRAAAA8gRvZW5kAQBlAUcQAAAAAAhhbmRzaGFrZQAA9L0RAAABAAEAAQAACAIAAAGBEQAAAPIDYWN0AQBlAdgPAAAAAANyb3IAAPOHDAAAAQABAADygAIAcngAB2F0YWdyYW0AAPKQEAAAuQAAAAN1bmsBAMAAAAAQRnPy1g8AAAEAAAALcnRzZnJvbWZpbGUAAAHOEwAAAPKAAgBlaAACdWYAAAHxBwAAAUQRAAAIBgAAARURAAAA8gNibGUBAGXymAkAAAEAAPYPAACIgaAAAAATMJ8ADAAAQPTpAQAAWAIBAAEA9mIMAAABAGEAAQABAGsGAQEA9bUSAABuAAEAQgEBAACEdGlwdgIA0AARfQAB0AARfwABNDbyAnV0AQBh8skMAAABAADygWgBAW9ldgAAgAMCf8nP1osVoHAAABElY2RzbXT23zQAyckkFfm1/Uk+6oOeFmJjz8dnRg6wcBHyAAHACVQDAIKqLAA7MAAAAQAAMgANAEUGAACBBwEAEQgTAIoMAAAyDQAAVA0BABYQDQBWEAEAGBEAAG8RAAB4EQEA5hIBABkUAQABCQDzmRIAAAEAAQABAQDzYQgAAGIHAQATgQKgUAAAEK9lcwEEAbAwEycAAXPzzhAAAAEAAQABgAKgEAAAD/iwAAlyAAFkZQECAPMnDQAAYAYBAAAUb25uZWN0aW9uaWRnZW5lcmF0b3IAAAFoFAAAABICb20BArBAFAEAAaBQAAAR5GNiZgGXBwAAABKAAgHmUAAABVxxGjojdHZkZ3O2EwCAwcxGD4OD9/WwAAfdAAF06+SE2PsrwLylBsZKcViZN51PHb3tR96gAAAABWsAAAgAgFOmSgAAxs8tbJL2ETg53teLFupUKHDwy8U1KO1ZQ8E5tDlQM7BJIXB7h4YNQLv69RI40hCmyIcGJwSWHd8PAn4FbUpxSif1mzAJIOZaj+pY/LBsgjAV9F94S1kQzh6GOw9WcVs6uNth/jVQsfhQF9UWPUA8b7eCUbIPzANR4YQQci+xUxlABBgGgFunaQA7MAAAAQAAwgBhAHMAAgB5AAIAiQAAAJ4AAwDNAQAA8wEBAEoFCwBaBQEACQYAAEQGAABGBgEAVwYCAEoHAAB/BwEAqwcEANwHAADjBwEAgQgAAIwJAACRCQEAmgkBAMEJAADuCQAAcgwAAIEMAACHDAIAiwwAAJgMAACqDAEArQwAALEMAQC3DAIAHw0AACwNAQA6DQAARQ0CAEwNAABXDQEAwQ8BAMUPAQDJDwAA3g8BAOMPAQDmDwEA7A8AAPgPAAAnEAEATBAAAHIQAgB5EAEAfhABAIMQAACKEAEAkxABAJ4QAADSEAAABREAAA8RAAAUEQEAIhEEADARAQBfEQIAbREBAIERAQCFEQIA3hEBAOoRAQDxEQIA9xEAAKUSAACuEgEAsxIBALYSAwD5EgEACRMBAA0TAQATEwEAKxMAAC0TAQA6EwAAPRMDAEITAgChEwEAshMAAOATAQDnEwEA8RMBAAoUAAAVFAEAHRQAADsUAQBCFAQASxQCAGEUAgBoFAAAdBQAAIEUAAABgAKgIAAACeegMAAADLxlcgEBAPTyBwAA/QfZAAEA8YACUgBucwEFADswAAABAADuAAIALg0BAMsO7AAAC2VydmVyY29uZmlnAADywRIAAAEAAAALbGllbnRjb25maWcAAPK/EgAAAQAAMoACAbAgEoYAAWNzYvKAAQFjdlKAAQKgEAAACHppZGUAA2F0aAAA8rsRAAABAAAAAm52AADyRxEAAAEAAPqEAgBlcAAJbGVmYWN0b3J5AADy7BEAAAEAAAAFY3RvcnkAAPT0DwAAAQDlAAEAAPKAAwBhaXIAAmlyAAAB7gkAAAAABW9uZmlnAADyQA0AAAEAAPICb2cDAGNkZgFABAAAAPIAAgFsdXAAOzAAAAEAAFQAAwALAFIAvQEAAIAMAAABAAF2csNQRlVh8n8RAAABAAEAARaL5nSBomHyfREAAAEA84ACNDYBAAKgIAAAEdWgMAAAEphiaPJOBgAAjgkBEwD0eRQAAAEAAQABAAEHAPOZEgAAAQABAPOAA2RydAEIAPRjEwAAAQABAAEAE4ACsAAFaguacHMBCAD0ixMAAAEADwABACOAAqCgAAAUE2F0AQQBoAAAABM3c/TgDwAAzAMBAAIA8wIBcwFKBwAAAQAA9MIHAAABAAEAAQDxhQKam2htAAhpb2xhdGlvbgAA8jgTAAABAAASAm9sAQFmIbKy/iN2c/LsCQAAAQDy4xMAAAEA8gABAGMAAbwMAAABAgKgsAAAFDegsAAAFDhjc/PTEAAAAQBbAAAMbml0aWFsaGVhZGVyAAABChQAAAAABWVhZGVyAADy6hEAAAEAAPKBZAIAaGnygmN0AQFlafKAAgBlb3CAAQMAnwmwQBQ7AAF0YnB28YICpaZobQAKa2NzOGtleWRlcgAAAToTAAAAAAVleWRlcgAAARQRAAAA8oNhdGUCAGtwAARyaXR5AADy6gkAAAEA8uQQAAABAAADc29uAAAB1Q8AAAD6RgEAavMWBgAAAQABAADygAMBbm92YQCMcnJlZGFkZHJlc3N2AgDQABPKAAHQABPMAAE0NhKAAQGwEBBfAAFlaRKAAQGwUBQfAAFmc/KAAwFlaW94AYABVmJzrNvrdAECAPadCQAAUgCdBgEArgABAAAJaWJsZXZhbHVlAAABExEAAAEiEwAA8gABAHMAARgNAAAABHJpdGUAAPPSDwAAAQABAAAABmFuc21pdAAAAUYRAAAAAAVtZW91dAAAAdkQAAAA8oACAGlyAAZ1dGRvd24AAAFFEQAAAAACbmQAAAHIDAAAAPKAAgBlaAgkAAABRBEAAAD6DwEAYvPEDAAAAQABAAAawwIA52AAAAzHYWMAA2V4dAAAAcMMAAAAAARsdXNoAADy0A8AAAEAAAANbmRwb2ludGV2ZW50cwAAAckTAAAAAARsb3NlAAABzw8AAADyAWyIgDQgFzswAAABAAAPAAIA6AEAADEEDgA7MAAAAQAACQABAE8MCQABgQKgIAAAEPuwUBKsAAFhbwEDAaAAAAATyXMB+hAAACKAAQHAAAAACIFydlICbnQBArBQEWoAAWVjcwA7MAAAAQAACAAEAIUJAgC1CQEAexACAJISAAD2AgCAg4OwEBPnAAEACQIAYAEBAAD1ygkAAAEAAQABAAEACCMAAAHCDAAAAPIAAQBwOzAAAAEAAAUAAgDmAQEALQQDAPKsEgAAAQDyAAEAZwGFAAAA81YEAAAECQEA8oABAW5kAQEAOzAAAAEAAOIAAgBfBgAAkAjhAAEAAaBAAAAQnmU7MAAAAQAABwACAPcHBQCICAEAAQQA9LQQAAABAAEAAQABgQKgIAAAERuwQBNcAAFlafOAAmV0I4ACoEAAABABaXQAGWlzdGVudGNvbmdlc3Rpb250aHJlc2hvbGQAAPKUFAAAAQAAAAtpcGhlcnN1aXRlcwAAATcTAAAA8gABAGMBLAQAAAHnCQAAMoACAqCQAAAUTbBAEFkAAWZzYWIAEmVudG92ZXJzaXplZHJlY29yZAAA8iEUAAABAAAACWlzYmVoYXZlZAAA83oRAAABAAEAAAAKY29tcGF0aWJsZQAA87wSAAABAAEAAAAGZW50aXR5AAD1PxEAAAEAAQABAAEAAPKAAgBkbvpMAwBpbXMA8hMTAAABAPYBAACChWEf8epnhEJ6qU/nVxAAAQwhAgA6MAAAAQAAAAAADAAQAAAAjgnGCccJyAnJCXQMdQy/D8AP5Q8BEXcTeBMBCAD1xBMAAAEAAQBzAAEAAQEA8x8GAAABAAEAAQsA9okTAAABAKIAVAAWAAEAAQMA87wSAAABAAEAAQQBoKAAABShd/KRFAAAAQABDQDzVxQAADEAAQAADW5zdGFibGVvcnBvb3IAAPLnEwAAAQAAAAVhY2t1cAAA9LoSAAABAKMAAQAAAAh2YWlsYWJsZQAA9HITAAABAIQAAQAAMgFzAgGwQBNDAAFhYmXzPg0AAAEABgPykxIAAM4AAAAAAPWFDAAAAQBGAwEAAQAA8oJhdAIAc3UABmxvY2tlZAAA9I4QAAABAK4AAQAAMgACAaCgAAAUoWJ0d/IUBgAAAQDzQBQAAFEAAQAAD2lyZWNvbm5lY3Rpb25pZAAA9AgUAAABAEsAAQAACGoAAPSMEAAAAQCuAAEAAAAQdXJjZWxpbWl0cmVhY2hlZAAA8mYUAAABAADygAIAb3A6lwIBsIAThwABc3RjAA5ic2VydmVkYWRkcmVzcwAAAfYTAAAAAAxjb25uZWN0aW9uaWQAAPRwEwAAAQCmAAEAABKAAQGgcAAAFCt3ZxKAAQED46qFPbdlbwgbAAGgYAAAErVy808GAAABADAC9HsMAAABAKgDAQABgAKgQAAAE8GwMBLmAAFhcwADZW50AQDAAAAAEERz84IMAAABAAEAAAhHAAABgQwAAPJgEQAAAQDygAIBcnZuAAlkc2Jsb2NrZWQAAPToEQAAAQAzAQEAAAAHYWxsZW5nZQAA9BERAAABAKcAAQAA8oACAGhpCCEAAAHwBwAAAAACY24AAPI8DQAAAQAAEgFrAgDQAAzAAAFlc/LuBwAAAQAAAAVlYXNvbgAA8gsTAAABAADyBWFuZG9uAQBy9PIPAAABAOQAAQAA8oACAGJj9j8DAIqEoFAAABGJY9oPVByBF2ELgJQA9eMBAAABAAEARQIBADowAAABAAAAAAAUABAAAACWCX0Mvgy/DMIMIw0kDSUNJg3DDoUQ0RB2EXcRuxG8EfATNRQ2FEkUShTygAEBaGkBAAA7MAAAAQAAEwABABEIEwABhAKwQBRLAAF/TWSFQh9lcyOAAqAgAAANJ2JtAAJyZAAAATsNAAAAAAVlY29kZQAAARARAAAAAAJtcAAAOzAAAAEAABMAAQAwEBMAALKDaWFshIDnEAAADIAcQAAAB250ZXJyb3IAAAEPEQAAAAAHeXRlc2l6ZQAAAY8SAAAAOhICAbAAEysBVmJpcvISBgAAAQAA8oACAnN0YWUABG9tdHUAAPLKDwAAAQAAAAJuZwAA8uwHAAABAAASgAEBoAAAAAZHaXLygAIAZHQACGhyZXNob2xkAADy6BIAAAEAAAACZXkAAPJ+DAAAAQAAAAplY29kZWVycm9yAADyCRMAAAEAAHICZXQDAbAAEMoAAWRrdHPzlAcAAAEAAQAAAAZuZ3JhdGUAAPIuEAAAAQAAMoACAbAAEx4BPGlrZfYOAICEhLAQB+MAAQwABQAJUPaRMQDFzaCQAAATISa7ybhHpbBACXIAAaCQAAATOgu47NAADU9tS+xybERKrpOCZ1UOZDFi2kBEkAAAMy8PADswAAABAAAkARIAigATAI4BAADRAQAA1AEAAO4BAAD7AQYAVwQCAGIE3AA6BgAAbQcAALoHAgB6CAAA3gkEADgNAAAwEBMAuhIBAF4TAQCKFAEAAQAAOzAAAAEAAAgAAgB2AAAAmQwHABEAAqSgIAAACHpwdQG9AQAAAQMA9ZEJAAABAFQGAQBdBCOAAl+dYmbjMW5yAYACDwIdYSkroCAAABCHY2kBAQGwsBRmAAFsOzAAAAEAAAwAAwCxBwgAUA0BAFQQAQATgAKgQAAADHNjdAEAAWC2zDv4fmnzOA0AAFIHAQABAgKgIAAAEdWgMAAAEphiaAHcDwAAI4ECoIAAABM2YXABhAKwABF9AAGwABF/AAE0NghVAAABTgYAAAAAB2Zib3VuZHMAAPLwDwAAAQAAAA5vaW5naGFuZHNoYWtlcwAAAZUTAAAA8gADAWdvcGEAOjAAAAEAAAAAAA4AEAAAAKAJoQmiCckMygztD+4P2RCREd4S3xLVE1cUiBSJFFYOAIABB6AQAAANObCQEYMBExdN6v5ErnQzZ2xucHJzAQMA9MYJAAABAAEAAQABAAEhSMRpf8BjAbwMAAABAAGw8BSKAAFr9v4TAAAFAAEAAQABACUA8wMBZPLIEgAAAQABggKwcBQbAAGgQAAAERhpcxOAArBgE6MAAWVvAQEDoHAAABHYoEAAABOuoDAAAA/dZGxz8lsUAAABAAhbAAABOg0AAADyAmVyAQBl8loFAAABAAB2DQAAgYdktP4DmTphg3JcqOGAAAAVYQQA84AAAAA+AQEAAQQA88MSAAABAKcA8wABYzowAAABAAAAAAAKABAAAAD0D/UP2hDbEGIR7BHtEf8SABOGFIcUAYACoEAAABCfZ4TeHjyFbnABAgEqSxXFfyltOzAAAAEAAOIAAgA7BgAAZgbhAAGAA6BAAAAMcqAQAAAMerBwFGEAAWFldgGAAnb46ewx+KAwAAAQ0WNwAQQA8+8PAADZAAEAIwACoKAAABNYZHE6MAAAAQAAAAAACAAQAAAAgQeCB1QNVQ1WEFcQGBF4EXkRAQcA9I0RAAABAAEAAQABAQA7MAAAAQAACAACALgPBgBHEwEA84ACZG4jgAKwkBLVAAFlbwEAAPTqCQAAAQD5BgEAI4ACoBAAABNvaHkABmRzdGNpZAAA8tUQAAABAADygAEBZ3QBgQKgEAAAEzGgcAAAE3ZhY/MBAXLyqREAABgACAsAAbCAE88AAXQBlwkAAAD6qwEBaWXyAAEAZQGEAAAA8zsNAADmBgEA9msAAIKJVnFbOrjbKDZ6BvsrCAEAFJRNADswAAABAABoADIASgULAAkGAABEBgAAjAkAAJEJAQCBDAAAhwwCAKoMAQA6DQAARQ0CAFcNAQDeDwAA4w8BAOYPAQDsDwAAchACAHkQAQB+EAEAgxAAAJMQAQCeEAAADxEAABURAAAiEQIAYBEBAG0RAQCBEQEAhRECAN4RAQDxEQIA9xEAAKUSAACuEgEAsxIBAPkSAQAJEwEADRMBAC0TAQA9EwMAQxMBAKETAQCyEwAA4BMBAOcTAQDxEwEAHRQAAEIUBABLFAIAYxQAAGgUAAABBwKgsAAAFDegsAAAFDhjc/PTEAAAAQBbAAGDAhjShYkhsR8GjogLcGht84ACaW8AAm9uAAABTQYAAADyAAEAafOBAAAAAQABAAFqDAAAABBpcmVjdGlvbmFsc3RyZWFtAAABXxQAAADyAm5pAQBk8usHAAD+AQAAA2F0cwAAAecRAAAA8gZ0cmVhbXMBAHQAAZYTAAAABW5zdXJlAADy5hIAAAEAAPo5AQBl85YJAAAoAwEAAYUQAAAIJgAA9EkGAAABAAEAAQAAAApvbm5lY3Rpb25zAADy5BIAAAEAAAgHAAABUxQAAADyAWkBAGTySAYAADcCAPIAhoAWgArzJwQAAAEAAQAAEoABAaCgAAAUTW5yNgIAAIKFoLAAABQToLAAABMidKKh8RcNSpzxZy3nEAAEANADADswAAABAAAKAAMA1AEAAPsBBgBXBAIAAYQCoBAAAA05oDAAABCHM3MBgQKwMBMLAAGwQBMpAAFhZgGAArAgFDUAAbBgE+MAAWFyAYACsFAUaQABoBAAAAxqZnABAgA7MAAAAQAABgABAGMRBgABAgGgQAAAFB1l8kcUAAABAPOAAmlvAYACsEASqAELoCAAABEoYWkBgQKwABLRAAGwABHiAAFlcxOAAqAwAAATmmFyAQgA87ASAAABAAEAI4ACoBAAABN5ZHMAFXVyaW91c2Nvbmdlc3Rpb25ldmVudAAA9HkUAAABAAEAAQAAAYECoEAAABCsoHAAABQ4YWUIUAAA9HsIAAABAAEAAQAA8gABAW5yAPadCQAAUgCdBgEArgABAPIAAgJlcGl0APXkEgAAAQCEAAEA7gAAB3V1cGRhdGUAADswAAABAAAHAAEAMxEHAAASgAEBsGAR+AABdGkABnRlcnZhbAAAAS0QAAAAEoABARlA1pNy825kAYACsDAT8QABsDASxgABY2kABXRieXRlAAABMhEAAAASgAEBsCATLwBXc20SgAECsOAT6wABUtnSyi+zcmRn8oABAWlhAQMBsAATFQABczswAAABAAANAAMA4BEBAI0TBwB5FAMAAQUAOzAAAAEAAIUAAgD4EAAAABKEAAGCArAgEIgAAbBAEXgAAWxz84ECa3UBAwD13hEAAAEAGgEBAFMBAQMAOzAAAAEAAIQAAQA+DoQAAQAA9F4MAAABAI8FAQABAgA7MAAAAQAABwABAJkMBwAABWRhY2tzAAD0KRAAAAEAAQABAADyAAEGbmJkaXJ0dgA7MAAAAQAAigAFAOIBAABDBQAAfQWEAP8QAQCYEQEAAAtlc3Rpb25ldmVudAAAOzAAAAEAAAcAAQCNEwcAABr+AQGw0BSGAAFndAAAAADylQkAACgDOzAAAAEAAAUAAgCmEgEA+xIDAPICc2UBAGQAOzAAAAEAAAkAAwDaEQMAoRIDANMSAQASgAEBoJAAABQ3b2kygAIBdyd9KNaabG91AAR5dGVzAAAB6AkAAAABBQD0UxQAAAwAHgAPACOAAqBgAAATb2dzCC0AADswAAABAAAHAAEAjAcHAABSgAECsJAUNQABY2Js9m8nAIiKUslN0PCqG1joCZFnH61yYySVsFATFwABNxECwOiVADswAAABAABMAB8ATQYAAJgHBgD1BwEAdAkFAIQJAAC0CQAAbAwAALUMAQApDQIAUg0BAMQOAwDJDgEA1Q8AAOIPAADyDwEAChAEAKoQAADTEAEA1xABAAwRAAAvEQAATxEJAIsRAACNEQMA2BEAAAsTAQAhEwAAOBMBAEkTBQBZEwAACxQAAAEBAaBgAAAQn3IBLwYAAAGAARNuxrWO8m7yAAACZWkBCQAAAAAIHwAA8ooHAAABAAASgAEBsDAUaQABc2wSgAECsFAP8AABYoTp6G68ZmJ1AYQCsHAT/AABsDATVAABZXMABGFkZHIAAPK4EgAAAQAACAAAAPK2EgAAAQAA8oJwdgIANDYAC3h0ZXJuYWxhZGRyAADyFRQAAAEAAAEGAPSNFAAAAQAKAAEA8wIBcgH2EwAA8gNkZHIAAWX0ihAAAAEApQABAADyhWVydmVkAwBhZWmSgAEDoLAAABPwE/3kQSvcc2FldQAACA94qn2x20JyDaJ9iwFhu2DkO+hl5SrY8XV5QK8ZlVMz7cPsSnpcNh/YkN9qkmMcnbOgMAAADKxA3h2Zwzo6JQrLsftW6pPP80pmjeo7DGg5ZbdNprA1js3lpaES4qogPf8/48hdcBJCFbFoJpdzf4MbMBEpKftnIhxQQeu0Yp5Q/0jm30YkWIrihAFiZmtucHJ0dTBhY2RlZ2lsbW9xc3Z3eAA7MAAAAQAAaQIRAN0BAADvAQEAPgPhAC8GAABCBgEAVQcKAOgHAgABCAAA5gkAAG0L4QC8DAAATQ0AAD4OhABdEAEAeBAAALAQAwBkFAEAAQACoIAAABLFsHAUEgABYXM7MAAAAQAACAACAHEAAADTCQcAAQEBoQAAABRfZPLrBwAA/gEjgAOgAAAADI2hYAAAFFltbnMBgYWwMBMLAAGwQBMpAAGgUAAAEpGwoBNaAAGwcBHyAAEhCAoBBgFmIbKy/iNz8uMTAAABAAECAbBQEuYAAWX2lgkAACgDAQDGA7ADAQBTgAOgEAAADRhhb3IBAQFbBqRc3Th18ngIAAABAAEJAPPDEgAAAQCnAFOAA7BgEfgAAWVpdAEBAPQWDQAAAQDsBQEAEwADoDAAAAgCUZvHaE5dZW5z9UEFAAABAJQKAQCKAAGAArAgEy8AV6BQAAARMm1zQ4ADsOAT6wABUtnSyi+zZGdyMYADOQCwAA1NAythaW8BAAGhAAAAFFNk8kgGAAA3AhOAA6BQAAATLKAwAAAJ6GlseQEAAaCgAAATWGTygQcAAAEAAQQBoJAAABN2cvKpEQAAGADzgAJkZQAAAAGwAAVZAqluOzAAAAEAAAYAAQDAAQYAAAAJaWFibGVwYXRoAADydhEAAAEAAAAObm91Z2hhZGRyZXNzZXMAAAE2EwAAABIAAQOgYAAAE66wcBOjAAFhg3JcqOFlYWNu84AAAAA+AQEAAAACZHAAAAF6CAAAAAADb3RvAAABvAwAAAAAAnJmAAAB5wkAAADygAIAZXLyAAIAcHUBvQEAAAAIewAAATgUAAAACHsAAAE3FAAAAPIIb3RlY3Rpb24CAGNz89MQAAABAFsAABKAAQGgAAAADGpydAADaXplAAABhxAAAAAihGVyb3UCAOMgAAANOTNzAAR0b3JlAADyeBEAAAEAAAh4AADyiBAAAAEAAPKEb2tlbgIAbHPyAAEAdAHiAQAAAPKAAgBlegARbml0aWFsY2lwaGVyc3VpdGUAAPKsEwAAAQAACC8AAPKRCQAAAQAAABJlZmF1bHRjbGllbnRjb25maWcAAPLlEwAAAQAAABFkYXZhaWxhYmxlZm9ycGF0aAAA8kkUAAABAAAAE3J0aWZpY2F0ZXNwcmVzZW50ZWQAAPIfFAAAAQAAMoACAbBAEQ0AAWVpbwAScHBsaWNhdGlvbnByb3RvY29sAADy4xMAAAEAAPZ/AQDLw7BQFGkAAaCwAAATNkBHaBoACIAEAPToBwAAAQABAPwBAQQA9CkQAAABAAEAAQAjgAKgQAAAFKFhZAEAAqAwAAAUUqBQAAAT4mNzOzAAAAEAAJAABwBJBgMA/gcAAGEIAAD0CYQAXgwBAMMPAQDuEQEAAYABEAcbF6RodAAHcnR0a2V5cwAA9LURAAABAAEAAQAA8gABADHyJQQAAAEAAcMMAAASgAEBoIAAABEbdHAAEWl0aGFic3RyYWN0c29ja2V0AAABPRQAAAAAA2tlbgAA9JMJAAABACYDAQAAAANtZXIAAPO3DAAAAQABAADygAIAaW8ABWVuZGVyAAAByQ8AAAAABW9uZmlnAADyDREAAAEAAPIBbwEAY/ToBwAAAQABAPwBAAAFZWl2ZXIAAAHSEAAAAPrHAgBjbgAMeHBlY3RlZGl0ZW1zAADyXBMAAAEAAAgyAAD05REAAAEANAEBAPRwEwAAAQCmAAEA8glvbm5lY3Rpb24BAGkBDBEAAADyAIaAFAAXOzAAAAEAACMAAQCZASMAAPGAAmhpa3UINQAAAdEQAAAAAANpbnQAAPIHEwAAAQAA8gVoYW5nZQEAaADz7RMAAAEAawDyg29yawIAY3DygAEBd28AD2V5ZXhjaGFuZ2Vncm91cAAA8ooUAAABAADyAAEAawD2/hMAAAUAAQABAAEAJQDyBm90aWF0ZQEAZADyyBIAAAEA9J8AAMTIAAMJFmwZGm4bIvSqe3c1sBARSwABcwAQADKAQ4gJADswAAABAACKAAUA4gEAAEMFAAB9BYQA/xABAJgRAQABiQIY0oWJIbEfBo6IC3BobQEEAPRTFAAADAAeAA8AI4ACsHASrgABaXQHAQCAhbAwFBUAAbBAEW0AAaBgAAATb6AgAAAJt1EBAgEBADswAAABAAAHAAEAjAcHAAGBArAAE8EA4LEgFGkAAWRzI4ACsIAUNQABYWwBAgOwIBFNAAGgUAAAEfCgQAAAFB5sc3byGRQAAAEAAQIA8wwQAAABAAEAAQEAOzAAAAEAAAYAAQCaFAYA8wYBZfROFAAAAQAvAAEAAAZwZGF0ZXMAAAH1EwAAAAAEbmRlZAAAAQcUAAAA8gRvdW5kAQBlAPJ3FAAAAQDyCHJhdmVyc2FsAgFydWEBhhAAAPLiEgAAAQDygAECdGl1AARheWVyAADyJxAAAAEAAAAEcm91cAAAATgNAAAA8oACAGdsGg8BAbAwE3cAAWR0APZLDQAArgJZAAEAsgIBAPKAAgRtdGJjbHAADG5hdHRyYXZlcnNhbAAA8uISAAABAAAAAAQUYovTYCE4VX/syoKOF5XlIf7NZO5bPi1/EBKIjL6JUvndpnYwF63GPiccI+QE3xSDXvt7HjNgWil8Sz3zeRykQPJWH6B5Um9SdY8nDDS+Uy3kTKILQnvqCC+SWpSvr6WsQsDlhwjBR4dgIc7yC6Y3rduoDsJGjM8ZfLfYDNCesFATFwABU2bLIqx2fEIJ4hnQMGFlb2JjZGZnaGlrbG1ucHJzdHV2d3l6ADswAAABAACFADgABwAAAIUAAACiAAMAkQEDAJcBAADLAQEAIAQEACcEAgBQBAEAVgQAAFkFAAAfBgIALgYAADYGAQBNBgAAmAcGAPUHAQACCAIABggDAI4IAQB0CQUAhAkAAJMJAQC0CQAAbAwAALUMAQC6DAEAKQ0CADwNAQBSDQEAWg0BAMQOAwDJDgEA1Q8AAOIPAADyDwEAChAEAKoQAADTEAEA1xABAAIRAQAMEQAALxEAAEURAABNEQsAixEAAI0RAwDYEQAA9BIBAAsTAQAhEwAAOBMBAEkTBQBZEwAAgBMBAAsUAAABBQDz+RMAAAEAAQBDhgOwkBR1AAGgAAAAEqliZHcBgAKgEAAAE86gUAAAEpdsbiOAAqBAAAAR5GFpAQYA9A8TAAABAI0AAQDzAwFi9AgRAAABAIIBAQABgAKwQBGzAAGhMAAAFGhobwECAPOnDAAAAQABABOAhKAgAAAMmLAQCbwAAbAwFAEAAQAJSAEBAbCAE1YAAXXyxAkAAAEAAAAAAAGYAQAAOzAAAAEAAPQACQA8BgAAewkAAIEJAABpDAAApQwBADUNAABcDeEAuBMFAG0UBQAAC3RuZWdvdGlhdGVkAAD0AxQAAAEAAQABAAASgAEBoIAAABQrb2USBnRpcGF0aAECoGAAABPBZVWroGaUbmVwAX0MAAAAtgAAgAIDsBAJwgABsGARswABbHQzNm0BBgDz8hAAAAEADAEBAgGgQAAAEfFl808NAADKAwEAAA1zY292ZXJ5Y29uZmlnAAD0NBMAAAEAkgABAAASgAEBQtFkYVzBaXASAAEBWwakXN04ZHUAOzAAAAEAAAoABAB4CAEAyg8BAAcQAgAPEAMA8gABAnVpbwA7MAAAAQAA4wABAKgA4wABBgDzehEAAAEAAQATgAKwQBH4AAFibQEDAbBAFFsAAXTyvxMAAAEAI4UCoEAAABPvYnMjAAOgQAAAEQSgUAAAEW9hYmb3igkAAAEARQD8BgEAXgABAAAMZXNldGludGVydmFsAADyWhMAAAEAAAACdHUAAPJ4CAAAAQAAAAltdW1jaGFuZ2UAAPKzEQAAAQAAdgEAAIODsBANTQMroFAAABH3ABEBYAAEAZcBAAAAAAZyYXRpb24AAPK1DAAAAQAAtgAAgAIDsAAJngABHFUcouBkZ25kc3QBAwDz/Q8AAAEA+wABAwA7MAAAAQAACgADAHERAgClEwMAghQDAAEEAPRQDQAAAQADAwEAMwADsDANMAABb3N0AUEUAAABAAOgQAAAFHOwIBMNAAGwAAm4AAFlcnj0twwAAAEAAQBmAAEIAPPDEgAAAQCnAAGAArCwE6EAAaAwAAAR8W5yAQIA86AJAAABAAEAAQYA9IEQAAABAKcAAQCjAISgQAAADTiwQBAnAAFASQDzegwAALUGVwABAgD2dAwAAAEASgMBALcDAQABgQKwQBRLAAF/TWSFQh9lcwADaWNzAAA7MAAAAQAABwABAHAIBwDyARMAAAEAVgIAgAEFoAAAAAmcsHAR1gABsAAJugABcmFlaHh51uIAAMHLoJAAABNZoBAAABFwToE5URSgLvqaPhN9oDAAABPwAAAAAgBHvAkAOjAAAAEAAAAAACIAEAAAAAoGCwbzB/QHggitCdwJ3Qk2DTcNSw1PDdoP2w/5D/oPUhBTEJ8QphCnEKgQqRDiEOMQGREaEfARhRIFEwYT0xPUE/8TABQBAgA7MAAAAQAACwACALgTBQBtFAUA8wgBbTswAAABAAAMAAMAbBEAANcSBQAtFAUAAARldHJ5AADyswwAAAEAAAAHcmFnbWVudAAA884QAAABAAEAAAAGZWZyYW1lAADyNg0AAAEAAPKAAwBiZnIADWRwcGF5bG9hZHNpemUAAPPEEwAAAQABAPI5FAAAAQAAD3JhbnNtaXRzZWdtZW50cwAA8vMTAAABAAAIVAAA9AoRAAABAIIBAQAAAANpZGkAAPR0EQAAAQBrAQEAAPIAAgBidQEmEAAAAAADYXRhAAD0CBEAAAEAggEBAADyhHJlYW0CAGRzAAJ6ZQAA848JAAABAFUAAPKAAgBpdAAYb3RlbmF0dHJhdmVyc2FsYWRkcmVzc2VzAAABnhQAAPKfFAAAAQAADGVpdmVzZWdtZW50cwAA8sITAAABAAD6lQIAY20ABmVhY2hlZAAAAbUSAAAA8gVhdGhpZAEAcvR7DAAAAQCoAwEAAAAGY29taW5nAADyzBAAAAEAAAAJbGV0aW1lb3V0AADy3hIAAAEA9NUTAACCADEAAQDygAIAZG4ACnNvc2VnbWVudHMAAAHdEgAAAAAHcmFtc2l6ZQAAARkTAAAA+gsBAGf05gcAAAEA/AEBAAAACW5pc3RyZWFtcwAA8l0UAAABAAGTFAAACGIAAAEUFAAAAWAUAAAADXVsdGlwYXRocGF0aHMAAPKRFAAAAQAAAAhpc3RyZWFtcwAA8msUAAABAAAa3wEBoGAAABSPZHP2AACJb25jdXJyZW50BAGgQAAAFEBibXN1cAAHY2tkZWxheQAA8i0RAAABAAD2PwAAioGwsBMnAAFNgQ8gAAD0fAAAAAEAAQABAAAABmZvcm1lZAAAAXoMAAAACFsAAPUgBAAAAQABAAEAAQAA9gQAgISEsDAH3wABUgY1SYCodRHC8xZ1AAlgBCAF9h8EAMXNoCAAAAnAsJATPQABTH+rMTJfV+ACPNazBQQ3esdAY4arpqKEsHAUYQABoDAAABNiQEQABoALtxkAOzAAAAEAAE0CHwDbAQAAAgImAVoGAwCXBwAAvwcBAAAIAAB+CQIAsgkBAHkK8wB5DAAAjQwAAEINAgBIDQIA9g8BAEgQAQBNEAEAaxAGAJAQAACVEAAArxAAANwQAQBJEQEA6hIAAG8TAAB2EwAAmhMAAL4TAABTFAAAXxQAAH0UAACMFAAAAQMA89IPAAABAAEAAQQAOzAAAAEAAA0AAQCbEQ0A84ACaXIBgAKgMAAAEICwcBM4AAFhaQEAAqAwAAAMjaBwAAAR2WV284AQAACTAA8CE4ADoFAAABP1sBAP0AABZXBzAQkA9IsTAAABAA8AAQATgAJk3OiGtGJtbgGAArEwFJoAAaAgAAAMw2FlAQIA9A8QAAABAAEAAQABAgA7MAAAAQAACAABAL8QCADzAAFlAfYBAAABgAKgQAAAFAqgQAAAEIRlbwEAAaBQAAATb28BsAkAAAGAA7AwD9AAAaBAAAAMeqCQAAAUUmxvcwEBADswAAABAAATAAEAMBATAIOAhKBAAAAS+LCQE6wAAaAwAAAPz4AZAAGAA7CwEy0AAaAQAAATrrAwDVoAAWNlcwEBAaBwAAATMWUBOw0AAAEIAPPyEAAAAQAMAROAAqDAAAAUE21wAQIA91gQAADPAJ0CAQABAHMAAQATgAKwMBDpAKNpdPMBAXPyaRQAAAEAAAtwbXR1ZHByb2JlcwAA8osTAAABAAAABWNrZXRzAADyyhAAAAEAAPKAAgBhbAhcAADyxw8AAAEAAPIAAgBicAA7MAAAAQAABgABAGMRBgAAD3JlZHVjdGlvbmZhY3RvcgAA8vETAAABAAAygAIBIYNVGGz8c3RlAAN5cGUAAAGOCQAAAPIAAQB08uABAAABAAH/BwAAEoABAVZNI+H7sWdlAYICsCARRwABsDARuwABZXABBQD09A8AAAEA5QABAFOAA7CQEewAAWFpckIAAAOwUA1AAAGgIAAACe5jZGbylQEAAAEAOjAAAAEAAAAAAAgAEAAAAEAEiQekCaUJiBCJEPIQ8xD/EQAHeWNsb3NlZAAA8gYRAAABAAAAAAAA9d4JAAABAAEAAQABAAASgAEBoKAAABRzcGYABW9zdHY0AAABhBAAAAAIWgAA8sUPAAABAAD2AAABbAQBsUAUmgABYWhpbG7yEAYAAAEAABKAAQEbjaEPQMpha/YdAACEhaBAAAAUREQgAhFAEQDyZBQAAAEAAQEBbdjLFjavaQHbCQAAAQEBoEAAABQdZfJHFAAAAQABAAD13gkAAAEAAQABAAEAAQYA9CMTAAABAEIBAQABBAD0PRMAAAEABAEBAPMCAmVy9IgSAAABAGIBAQABgQEQAg7wokd0AAR0aW1lAADy3AkAAAEAOzAAAAEAAAcAAwCmEAMA4hABANMTAQASgAEBoJAAABRzZXMWLwAAwcqgMAAAEpFHEChFlYYCHB55vdmwYBPrAAGgEAAACbcACAAAQAckiwABeAAAAAEFADowAAABAAAAAAAIABAAAADtD+4PkRHeEt8S1RNXFIgUiRQjgAKwABOjAAFlaQGAA7BwE7YAAbEQFGkAAbAgEEoAAWRlaQECAPZtEQAAAQC/AQEAFwEBAEMAhChlYrjwIbBgEwEAAbCAEPAAASAQBfOYDAAAlAABAAGCAqCgAAATMaBAAAAQgG92AYACsFAR7AABsFAUSQABYW8BgAKgMAAAERWwMBC9AAFydgGEArCgE/8AAbCgFAEAAWNy8wABdPeCCQAAAQBhCOIAAQD6AOAAAYADsCAS9gABoKAAABO+sGATgAABZWxvAQAAOzAAAAEAAO4AAgAuDQEAyw7sAAEAAPQREQAAAQCnAAEAE4ACoTAAABSBZXTCAICEoLAAABPJsLAT7QABWAAE9JEBAAABAAEAAQA7MAAAAQAABwADAAYIAwCOCAEATREBANaHAwDBzqEQAAAUWaAQAAAM17BQE+cAAbAwEXYAAaAAAAAMgLBAERMCDwAACABAX/ALADowAAABAAAAAAAfABAAAAAsBi0GMAZ+B5gJmQmuCa8JZAxlDGYMbgxxDHMMHA0nDVsQXBCJEZESqBK8Er0SvhJyE3MThxOIE7MTzhP3E/gTAYACsLAUYQABsCATOAABZmnxAgGrZfROFAAAAQAvAAEA8wIBZfTFDwAAAQBPBAEAAYUCsHATgAABsIATtgABY3MBAgGwYBRJAAFmOzAAAAEAAAYAAwBkDAIAchMBAPcTAQAAgAIAwAAAAAfl50AAAAmNZHj6eQEAZQDyJxAAAAEA8gABAGUA89sHAABSCQEAAAl0b3BlbnBhdGgAAAGFEAAAAPYFAICChbAgCaYAAQAAQg4ABQAAxNF/NsqT6J9uWUtZ0nAuhAS7tS1BtEWxqM07aifbpFJyUMEtYBwIaPCJxL8e+DeREcZZcCck2eQkTUMHgzc4tVBFLtYm5cYRyeUP336vhsh9N0/ssYsPx5Ns5o0GbWqadp1cH+az5KgcODXcbmR6gGlrGV1ik1wMH9lQEQfHeIZARBAAADuvXwA7MAAAAQAAQgAZAOgBAAD1AQAAMQQOAEcEAQBVBAAAEAYBAN0HAQDhBwAA0QkBAOwJAQBPDAkAHQ0AAC0QAACGEAAA6hADAKoSAQDiEgEAWhMBAIkTAQDjEwEADRQEACwUAABbFAEAgBQAAJYUAQABAQD1OgYAAIAMAQCjAAEAAQAAOjAAAAEAAAAAAA4AEAAAAPEB8gE9BkgHSQfADMEMKRAqECsQLBBGEO4Q7xD4EgEDAaBQAAARiWgB0RAAAAEAAaBgAAAQn3IBLwYAAAEBAPWqEQAAAQABAAEAAQAzgAOhIAAAFAtub3ABggKwcBOAAAGwgBO2AAFjcyOAArBAEFsAAWFvAQgBCrOzG3rhY/ViEAAAAQC6AAEAAQABBAGwMBMHAAFo8+0TAAABAGsAE4ADoOAAABOzoHAAABM6aG9zAQgAOzAAAAEAAAUAAQDKEgUAAANvd24AAAFZBQAAAQIIAAABAAGgwAAAFB5p9FgTAAAIABoAGwABBgD0ghQAAAEAAQABAAEAAPQ2BgAAAQAFBwEAE4ACsHATLwBXbm8BCgGwQBRFAAFl9Q0UAAABAAEAAQABAAhQAADysxIAAAEAAPIFcGRhdGUBAGUA8tUSAAABAAAAAADy3gEAAAEAOzAAAAEAAA8AAwC/EAgAtREDALASAgAAA2FpcgAA8uMHAAABAAAIVgAAAYkHAAAA9gQAAISDsCARFAImsMAUigABAIgKGAEAADowAAABAAAAAAAMABAAAADfB+AHwAl3DHgMfgx/DMwMzQwWDRcNAxMEEwAOcGFsaXZlaW50ZXJ2YWwAAPKJEwAAAQD0LBQAAFQAFgABAHYSAACCiDKJ6e9GvrEAFCMAAWdPAudyh3sxSCJ6fg+P+uDO3RAAQAw4BwA7MAAAAQAADgAEAFoEBQB2DAAAvREDAJwSAwD23gAAgoqwQBEtAAGgIAAADVYQIACtgQsAOzAAAAEAABYACgAJAAAAbwABAPYBAABBBQEAjAcHAO4HAQDWDwEAYRAAAIEQAQApEQEAAQACoZAAABSBoDAAAAmtZXTyXxAAAAEAAQEBsEAREwIPdvO8EgAAAQABACOAAqAQAAAUdGVsAQABCTKfohG3bDswAAABAAAIAAMA6gkBAOQQAQA/EQQAAQABsGAT8wABZfSnDAAAAQABAIEHAQUA9CMTAAABAEIBAQABAgE/ggXYbXt0OzAAAAEAAAgAAgBhDAAAjwwHAPOAAmFlAYACsCAJrgABoOAAABPiYmwBCAD0nBIAAAEAAQABAAEEAbAAEO4AAXMBYRAAAAECAaBQAAAS+GMBHQ0AAAECAPQ9EwAAAQAEAQEAAAN0b3IAAAGMCQAAAYMQAABSAAECoCAAAAxsYWlyAAGtDAAAdgEAAIGGsEANVwABsBATXAABoAAAABM3AAABlRACADowAAABAAAAAAANABAAAABoBWkFMgYzBjQGIA0hDdIP0w/UD+APrBOtE68TNhYAAIGIaZk3FqzQSOorwcNAsBAGLAABsCAIAwABEAAAgQlHADswAAABAACTAAoAYQQAAKgJAgD4EAAARhEAAIMRAAAAEoQAhhIDAJYSAABiEwAA6xMBAAAJYWxpZHJldHJ5AAD0rxEAAAEAAQABAAAABHJ2ZXIAAPKxDAAAAQAAAAxtZWNvbm5lY3Rpb24AAAFZEwAAAPKAAgBhZQAOb2JhYmx5c2FtZXBhdGgAAAHwEwAAAAANdGhyZWNvdmVyYWJsZQAA8ocTAAABAAAygAIBoCAAAAfbYXJsAAllZ290aWF0ZWQAAAErFAAAAAAGbmFibGVkAAABwRMAAADyh2x0aXBhdGgCAGVuEoABAbAwEfgAAXVhAQABoEAAABD8ZfOICQAAAQAOCROAAqAwAAAP7GRyAAluZHNoYWtpbmcAAPWqEQAAAQABAAEAAQAA8gABAWFlAPN7BwAAAQABAAAGcmFpbmVkAADyww8AAAEAAAEEAQk18K3KwmMBoBIAAAADc2VkAAABsAwAAAAAA2VudAAA8q4MAAABAADygAIAaW+igAIC0AAGDgABsEAMbwABZWxhbwADaXZlAAAB2wkAAAASgAEBsDAJggABbGIAA3J0dAAA8m4IAAABAAD0PwCACQIAAQMICQ0RFBfWR2P9URQpMGFjZGhtcHN2YnQBgAKggAAAFCsD46qFPbdlbyMBA6BgAAATwWVVq6BmlGVucAF9DAAAAAJkcgAAAUcGAAAA8oABAWR0FgAAAAEECqubo7OooRAAABQLsDAQCgABY/Nk1MHuYWhzdHYAOzAAAAEAAOYABQDuAQAAYgTcAG0HAAC6BwIA3gkEAAEQAPR5FAAAAQABAAEAAQIA9OoJAAABAPkGAQATgAKgUAAAEJ9pdQAGaWNlbXV0AAABNQ0AAADyAAEAbPKPAQAAAQAAlgEAAAEEsFATOAABBFL/qwvsc2xucnUAAS8GAAAIaAAA8gIRAAABAAAACWVydmVybmFtZQAA8gUTAAABAAAADGVtb3RlYWRkcmVzcwAA86kTAAABAAEAAAAGZXNzYWdlAADzcREAAAEAAQAAAAVlYWRlcgAAAQURAAAAAAZyYW1laWQAAAFwEQAAAAATbmNyeXB0ZWRjbGllbnRoZWxsbwAA8mQUAAABAAAADWV2b2NhdGlvbmxpc3QAAPJHFAAAAQAAAAZmaWNhdGUAAPIyEwAAAQAA8oJydAIAaXISgAIA9kANMwABZWnyhGFsaWSIgLQQBwAIbm9yZGVyZWQAAAGpEQAAAAAGZXF1ZXN0AADz7w8AANkAAQAACB0AAAGDEAAAAPICZXIBAGEBrQwAAAAIAAAA8sEPAAABAAAABW9taW5nAADyKxEAAAEAAPKAAgBjbvKAAgBudAAFdXR1cmUAADswAAABAAAOAAIAMg0AABYQDQAAAAJueQAAOzAAAAEAAAcAAQDTCQcAAAgVAAABrAwAAAD2AwAABgGgsAAAFBIwYWZpcnVtOzAAAAEAAOIAAgDdAQAAPgPhADswAAABAABnAQMAAQgAAG0L4QA+DoQAAAJhbAAA8tEJAAABADowAAABAAAAAAAIABAAAAAtEFoTWxOJE4oTLBSAFJYUlxQAB2FsZXJyb3IAAPJtEQAAAQAAAAhhY2VpbmRleAAAAYoSAAAAcgADAbAgEQ8A6GZudnIAAYEIAAAACXJpdHlsaW1pdAAA8ogSAAABAAAygAIBUDWoPXbmZ3J2NgAAAAIEsNATsAABsFARagABoDAAAA/VJ1MZLeUIZW9iY2pzADswAAABAAAUAAkAFgYCAGAGAQDKBwAAhQkCALUJAQDTDAMAexACAJISAAAHEwEAAAJudAAAAeIHAADybRMAAAEAEoABAaAwAAAQr2FyCA8AADswAAABAAAFAAEAgwcFADswAAABAAAFAAEAyhIFAPKAAgBldPEBATpl83sHAAABAAEAABFuYXR0cmF2ZXJzYWxyb3VuZAAA8ncUAAABAAASgAIA9yAMqgABZW8ABWluZG93AAA7MAAAAQAADQABAJsRDQAAAAJ0dAAA8xMQAAABAAEAAAhgAAD0DxAAAAEAAQABAAAAA2V5cwAAOzAAAAEAAAgAAQC/EAgAAAANc3RjaWRwcm92aWRlcgAA8jsUAAABAAD2AQAAhYKwoBOsAAGgUAAAFAoIFBGEAAAB4QcAADswAAABAAAFAAIA6hADAKoSAQDygWECAGx08gABAGkAOzAAAAEAAIUAAgD4EAAAABKEAFKAAQKwgBGzAAF0bXMBAQA7MAAAAQAAJAAIAM0HBwBPEAIAmxENAMIRAgCUEgEAFxMBAGcTAQD5EwIAAAF4AADyDAYAAAEAAYoSAABSAAEDoHAAABKQoFAAABAFZWFvcwABnwcAAAALc2lzdGVudGtleXMAAPOwEgAAAQABAAABBgDzvBIAAAEAAQAIEQAAAW8RAAAAAARvdGFsAADyWxQAAAEAAPIDaXplAQB08r8TAAABAAAIRgAAAe8TAAAA8oV1ZmZlcgIAYnMABGxwbnMAAAEEEQAAAPICbmcDAGFiZvOKCQAAAQBFAPTMEAAAAQBeAAEA8oABAWlw8oFvAgBtbgEDAPMMEAAAAQABAAGAAqAgAAAJt7CAEq4AAWlzCCcAAPSlEwAAAQABAAEAAAAPYW5kc2hha2VtZXNzYWdlAAD0ghQAAAEAAQABAADyiXByb3ByaWF0ZQIAaG3ygAECcGx09gcAAMfGcoIir0GqsBANTQMrf+jHfMhEsCAIeAABsCAPwQABsMATWgABQEMACwAcjAAAOjAAAAEAAAAAAAkAEAAAAIUAlwEgBCEEIgQjBCQEVgRaDVsN8QECtLVlcvSIEgAAAQBiAQEAAAllZGlhdGVhY2sAAPSBEAAAAQCnAAEAABYBAIACBMAAAAABjrAwEJMAAXT/+LtkYbBwEbMAAW1wYWVpdfEAAal08+QRAADiAAEAEwACsGAR7AABZGYBzhMAAAEAAqBgAAATsqDgAAAUDGV0OzAAAAEAAAgAAQBtBQgAAQMBsGAUSQABZjswAAABAAAGAAMAZAwCAHITAQD3EwEAAARhbHVlAAABgBAAAAAACnJkZXJlZHJlYWQAAAExEwAAAPKEZWdhbAIAb3bygAEDbGFkZQGCAqBQAAAS0KEAAAAUdGN0AQQDsCARTQABoFAAABHwoEAAABQebHN28hkUAAABAAAKaGFuZHNoYWtlcwAAAVgTAAAA8gJyZQEAZPKBBwAAAQAAMgABAmtKn+5bsG9hZQDyUAQAAAEAFgIAAAEFsFAQ1QABsEAP4wABoRAAABPisFAMtQABbmRlaXJ1ADswAAABAAA3ABYAUwcBAGcMAQBADQEA6A8BAGUQBQCWEAUADREBAGoRAQD0EQIAvxIDAO8SBAAfEwEAJRMBADQTAQC0EwEAxxMBAOUTAQDpEwEADBQAADMUAQA3FAEAVhQAAAEHAPX0EAAAAQABAAEAfQIjgAKwcBMFAAFyeAEGAPToEQAAAQAzAQEA84ACYmUBgQOgQAAAErU9pMLUkg8II1K02lphbXQBgAKwQBMrAVawYBQ7AAFhcgEOAPRTFAAADAAeAA8AEwACsGAUawABcnM6MAAAAQAAAAAABwAQAAAAdBF1EeAS4RJFE0YT2BPZEwEIAPciEQAAAQABALwCAQCCAAUAAQUA9KYQAAABAAEAAQATgAKwcBRCAAFmbQEAAPQGCAAAAQABAAEAAAZpbWVvdXQAAPLtDwAAAQD23hIAAAEA9gCCADEAAQDyAAEBdG4AARwNAADygAEBZWkW6gAAgYw+l3MuijILSpvSSmIu97a1G0GgYAAAEXCgUAAAEQVaLkJKQZCwQBECAAEACAD1kQc7MAAAAQAADgACAAgAAABeAA0AOzAAAAEAABsBGQBPBgEAXwYAAGIIBwCACAAAgwgEAJAI4QCeCQEAygkEAHsMAQDXDAAAMw0BACQQAQB1EAIAuBADANUQAQBwEQAA5REBABoTAQA7EwEAcBMBANETAQAIFAEAFxQBAB4UAABUFAEA5r8gAMrJ4yAAAAGNNBFupfxARrKh/C/jZpo2xHNxaUlrVzoGfLj49pFAK093vqNKUuOXDoE5BBI+A8ANgKgAOjAAAAEAAAAAABwAEAAAAAIAAwB4AIYAhwBIBusHfwh6CekJYAyODFkNChELEXQRdRGNEo4S4BLhEhETEhNFE0YTnxOgE9gT2RMBAAFRm8doTl1z9UEFAAABAJQKAQCKABOAArCwE+cAAWtzE4ACoEAAABFFbnQBBQKgIAAAEdWgMAAAEphiaAHcDwAAI4ACsNAUGwABb3UjgAIRa5KaHg1lbwEBAqAgAAAR1aAwAAASmGJoAdwPAACDgIRDitS/tN2w4BRhAAGwQBBfAAEBCAkBCwDzVxQAADEAAQAAA2tleQAA8t8HAAABAADygAEBY3jyAAEAYQABbxMAAAEAADswAAABAAAFAAIA0wwDAAcTAQABBAD06hAAAAEAAQABAPOAAml0AARncmFtAAABeQwAAADyAXQBAG8A8igRAABwAVKAAQJJTTqGFNpzZG4BAwGgAAAAEERz84IMAAABAAEAAQABRtv3M05zbTswAAABAAAQAAUAHg0AAGwRAADXEgUAlxMCAC0UBQABAgD0gQwAAGsDdAEBACMAA6BAAAANOjnpqBagtGVyc/RaBQAAAQDrAAQBAYADoEAAABPBoGAAABShsDAS5gABYWVzAQEBoAAAABRBcwHwEQAAAQIBoEAAABREZfJkFAAAAQABAAKhQAAAFGOgQAAAEPxjZTowAAABAAAAAAAHABAAAACICYkJlxK1EiMTJBNmFGcUEYECxLAQE1YAAWFlAAJleQAA8ncMAAABAADyA2RlcgEAa/J/BwAAAQD0BREAAOUAAQAfAvYfAADBybBQESAAAUAAAAAAA44pAPPDEgAAAQCnAAECAPN6EQAAAQABAAEEAPX0EAAAAQABAAEAfQIBBQD0EREAAAEApwABAAEAArAgDTwAAbAADMAAAWVz8u4HAAABACOAAqAwAAAJfGhrAAl1YmNvbW1hbmQAAAGaEQAAAAARZW5kaW5ncmV0cmFuc21pdHMAAAEqFAAAAAAVY29ubmVjdGlvbmlkZ2VuZXJhdG9yAAABYxQAAAASgAIAwAAAAAZGZHLyAAEAZTswAAABAAAVAAIA3AEAACkDFAAACCkAAAHPCQAAAPKABAAwaHBzAQECsEAUigABsDATBwABZ2j1sxEAAAEAOQIBAGsAAQIA9aoRAAABAAEAAQABAAACcGUAAAEBEQAAAAAGa2Vua2V5AADyAxMAAAEAAPKAAgBveQAMZ25hdHVyZXZhbGlkAAABHhQAAADyAAEAaQD0WBMAAAgAGgAbAAAKb3Rjb21wbGV0ZQAA8qMTAAABAAAAAm5lAAD0/xAAAAEAmAABAAAABGVhZHkAAAEwEwAAAPICdGEBAHI7MAAAAQAABwACAP0QAQCSEQUAAPKAAgBhbwAIb25maXJtZWQAAPIvEwAAVwAA8AAFAQABBQYI3mNkbnN0bQF2DAAAOzAAAAEAAAcAAgC9EQMAnBIDAPKDaGFrAQFlaQgSAAABkREAAAAADGV0d29ya2NoYW5nZQAA8u0TAAABAAAABHZlbnQAAPK9EAAAAQAAAAtsaWVudHN0cmVhbQAAAb4TAAAA+n6EgBQgBAF+BwAA9VsQAAABAC0BHwELAfKAAgBsc/KAAQFkZzZpAIDCyaAQAAAJfAcyGskKA6BwAAATNkE301AQAaAgAAAHsAAACAGCA4MM9sACAISKoCAAAAfwO/FNKe3ioFAAABGJY9oPVByBNVN6Pa4BMOGG8bniOdxn0HciEREAhuQLADswAAABAAA7ABgA3AEAAOMBAgApAxQAKgQBAGwFAAB7BwIAlgkAAPIJAQB9DAAAvgwBAMIMAAAjDQMAww4AANAPAQACEAAAhRAAAKsQAADREAAAdhEBALsRAQD4EQEA8BMAADUUAQBJFAEAAQYA9M8TAAABAFMAAQAjgAKhEAAAFIFjchOAAqAgAAAJjmh5AYECsDAS9gABsAAIjgABY24BgAKgcAAAEzagcAAAE5VkbhOAAkMo9H07G2FwAYCEEYoEEMlRsJAR7AABPX7j0R6aoEAAABFvAQEJAYACoBAAAAnusEAQ1QABaXMBggKwIA1AAAGgoAAAE3lmbgGABKAAAAAJsKCgAAATMaAgAAARBKBAAAAQgGdvcHYBAQD0zxEAAAEAAQABAPOAAmRsAAFkAAD1ygkAAAEAAQABAAEAAPIBcAEAaQDzOA0AAFIHAQAAB2VnbWVudHMAAAG8EAAAAPKAAgBzdQAJc2VxdWljYml0AADyhhIAAAEAABKAAQGggAAAFCphdDKAAgIqddq/a92wcBKIAAFlb2FpCG4AAPTGCQAAAQABAAEAABKAAQEEM1a8Vul0aRKAAQKgwAAAE5WgQAAAE290aXIBAwD04w8AAAEAvQMBAAAZbW90ZW5hdHRyYXZlcnNhbGFkZHJlc3NlcwAA8pwUAAABAAAABWFpbGVkAAAB5BEAAAAShG5kb20BAbBAFAEAAWZi8oACAGFlABBheHVkcHBheWxvYWRzaXplAADyORQAAAEAAAANcG1hbnlzZWdtZW50cwAAARMUAAAAABdhbG5hdHRyYXZlcnNhbGFkZHJlc3NlcwAA8poUAAABAAD6rAIAY28AD250b21hbnlzZWdtZW50cwAAARIUAAAA9gAAAAQBsKAT/wABaWxtcmMBjAEAAAABAQD3IhEAAAEAAQC8AgEAggAFAAANZWxmc2lnbmVkY2VydAAAAVIUAAAAAAJpZAAA9LgQAAABAAEAAQAA8oACAGNz8oABAWVvIoNlcmECANAAB90AAWx0NgEAAAIEsEAUigABsDATBwABWqneKx7HbnRnaHJzADswAAABAAAWAAoAXAUAAKYJAQAREQEAcRECALMRAQC5EQEApRMDAO0TAQBZFAAAghQDAPReGACDjgASFRwI8etQ4tYfICEioRAAABPiJEZWpPuYvUgjTBGPI6AwAAAMwmQQsnLrUSWAEEABr7kOADswAAABAACqADEAlQEBAOABAQDmAQEALQQDAEAEAABIBQAAXgYAAFEHAwCJBwAA7AcBAP8HAACKCQEAlwkAAKMJAgCwCQAA0AkAAGcMAQDODAQA2Aw9ACgNAABADQEA6A8BAPsPAQBlEAUAiBABAJYQBQDMEAEA5hABAPIQAQANEQEAKxEBAGoRAQCqEQQA9BECAP8RAACsEgEAvxIDAO8SBAAfEwEAJRMBADQTAQC0EwEAxxMBAOUTAQDpEwEADBQAADMUAQA3FAEAVhQAAAGDAqCQAAAUGaCQAAAUGjIzAYACoFAAABRBoIAAABNsY2XzAAJzdPLLBwAAAQABgAOggAAAFHOwEAeKAAGggAAAFFJhZWkBAQE/RqsO5lFk8qAHAAABAAADdXJlAAABRQYAADswAAABAAAQAAQAigwAADINAAAWEA0AbxEAAPKAAQF0cwEGAQqzsxt64WP1YhAAAAEAugABAAEAAAhuY2hlY2tlZAAA8lYTAAABAADyATQBAHXyxAkAAAEAACKAAgDzIAnCAAEzNgACdHIAAPLcBwAA5QEACCcAAAHACQAAAAACbnYAAPK+CQAAAQDyRxEAAAEAAAJ0cwAA86cMAAABAAEAABKAAgD2YAm8AAFpdQACdXQAADswAAABAAAFAAEAuBMFADswAAABAAAFAAEAbRQFAPIJcmdtYXRjaGVzAQBtOzAAAAEAAAYAAgBsEQAA1xIFADswAAABAAAFAAEALRQFAPYDAAFthoOgMAAAE86gcAAAEfewMBG7AAETBAoggQA7MAAAAQAAJwECANsBAAACAiYBOzAAAAEAAPQAAgAACAAAeQrzAAgnAAD0dAwAAAEASgMBAAAigAIA0AAJugABeHkABHRhdHMAAPIwDQAAAQAAAAxuY29kaW5nZXJyb3IAAPKhEwAAAQAA3gAAHQQB94AJuAABoBAAABFwZXJzdGnyCgYAAAEA8jYNAAABABKAAQFNo0APa0ttZ/KAAgFhb2UABnJ0dXBsZQAAAXMMAAAAAQUAOzAAAAEAAAoAAwBPEwQAJRQEAJAUAAAABHR0ZXIAAAFyDAAAABKAAQKgEAAADHqwcBRhAAFhZXYACG5uZWN0aW9uAAD0jREAAAEAAQABAAAACWtleXVwZGF0ZQAA8tUSAAABAADygAIAZW+ygAICsDAUSQABY21wdfIAAgBydQDyTQ0AACsDAQEA9DwGAABFAyQDAQAAAAAAOzAAAAEAAOMAAQCoAOMAAPKAAQF0dQAMY29udHJvbGVycm9yAADyLRMAAAEAABKAAQGwEBRpAAF3YRKAAQGwIA/QAAFvdQEBAbBgEewAAWYBzhMAAAEAAaCwAAATsmLzQhMAAB8BAQAjgAKwABABA0BkcgEAA6AgAAARX6BAAAASpWBCgTYQTWRlc/MGEAAALAMBABOCAqAgAAAUC2VpABlkbGVuZ3RoY29ubmVjdGlvbmlkcGFyc2VyAAABgRQAAADyAAEAZQDyXxAAAAEAAANyb3IAAAHsDwAAAAAEYXJseQAAAfwQAAAA8gABAGXyiAkAAAEAAZcSAADygAIAZHLyAnNoAQBl83sHAAABAAEAAAAIaXplZXJyb3IAAPKuEgAAAQAAAAJ6ZQAAAbcJAAAA8oFsAgBpc/KAAgBhaXYFAICChrDgE+sAAVLZ0sovsypY62rCZQAgIFwIAQEAADowAAABAAAAAAAKABAAAAD0D/UP2hDbEGIR7BHtEf8SABOGFIcU8wIBefLxEwAAAQAjgAKgUAAAEopldAAKYW5kb21ieXRlcwAA8gEUAAABAAAACnVycmVudHRpbWUAAPL/EwAAAQAA8oRvZ2V0AgBjcvICZWQBAHQA8+QRAADiAAEAEoABAbBAEUsAAWxyMoABAhWqjUGklWljdcY7KADKyOMgAAAApuNAAAAAp8AAAAAAB7BQD/AAAaCwAAATN6AAAAAB00Hcc80Z4SCW7MJ3m6DwAAAUWWRAngSADwBDADowAAABAAAAAAAMABAAAAB3ACwEPwVJBQcGCAbwB/EHvAm9CecJVg1EEQGAAqEgAAAUjLBgEfIAAW5zAYICoCAAABEUsFAS1QABZHUBBAD0sBMAAAEASwABABOAArCQFIoAAWVoAQIBoEAAABGBZQHYDwAAAQcA9LURAAABAAEAAQAAEXNpb25ub3RuZWdvdGlhdGVkAAAB/hMAAAASgAEBsHAUFQABbnLyAAEBZTEA9SUEAAABALMDtAE2AwAEcnJvcgAA8kUUAAABAADyDWV5aW5nbWF0ZXJpYWwBAGX1DRQAAAEAAQABAAEAABrjAQGwABEgAAFrcwAEeXBlcwAA9LQQAAABAAEAAQAAEoJjdAEBddAlmemIdGXygAIAZW/2AAAAAgMTTl6ha7dwdGFjaADzDAYAAAEAfQwBgAKgEAAAFB6wEBETAg9pdROAAqBQAAAR2WxyAQAA9EQQAADRAgEAswD6ZwABc/NWBQAAAQABADswAAABAAAWAAcAggwCAP0PAQC9EAEA+RABAOARAQCNEwcAeRQDADKAAQI4x3y4o1dlYW8BAgKgcAAAEfoL3COn+GNhcgEFCAAAEwACbvBjpD/+cnXy7wEAAAEAAYACSaKLGQO8oLAAABRZYWUBgAKwUBMBAAFvcwdangZlaQEFAPOXEwAAAQABABOAhLAwEFkAAaCgAAATsqAwAAAT7xBASAGIArAAE8oAAbAAE8wAATQ2E4ACoDAAABGEZHMAA29kZQAAOzAAAAEAAAYAAQC4DwYA8kcTAAABAPIBcgEAYzswAAABAAAMAAIASgULAAkGAAA6MAAAAQAAAAAASQAQAAAAkQmSCYEMhwyIDIkMOg1FDUYNRw1XDVgN3g/jD+QP5g/nD+wPchBzEHQQeRB6EH4QfxCeEA8RFRFgEWERbRFuEYERghGFEYYRhxHeEd8R8RHyEfMR9xGlEq4SrxKzErQS+RL6EgkTChMNEw4TLRMuEz0TPhM/E0ATQxNEE6ETohOyEx0UQhRDFEQURRRGFEsUTBRNFPIAAQFvZQF7AAAAAFZwAADBzxsN35uh1A2ft9OTbyIfhpxrpA8CfgVtSisdRh/s+bAgDHcAAXaP1XUq23LVH6R5Enq65kbAN7AACbgAATlMWFotugAAgADATR1rADowAAABAAAAAAA2ABAAAABaBVsFRgZXBlgGWQZKB38HgAerB6wHrQeuB68HgQiaCZsJcgyLDJgMrQyxDLIMtwy4DLkMHw0sDS0NwQ/CD8kPJxAoEEwQ0hAFERQRJREmEV8R6hHrERMTFBMrEzoTQhMKFDsUPBRhFGIUdBSBFAAFYWxlbnQAADswAAABAADuAAIALg0BAMsO7AAAEoABAbAwEoYAAXZjEoABAW5lyq1waWll8gABAHU7MAAAAQAAUgABAAsAUgABgAwAAAEAAPS+CQAAAQCIBwEAAYACoAAAAAyNJonq34HMbW4BBAGgMAAAEed0AZYTAAABAgD2VA0AAAEAAQMBACEBAQDzgAJvciOAA6DwAAAT/rAgEuYAAWl0dQECAbBQEuYAAWX0lgkAACgDAQDGAxOAAqAQAAANGGFvAYACoGAAABOusHATowABYWMjAAOwUBENAAGgsAAAEzZjdHX06AcAAAEAAQD8AQEKAPPDEgAAAQCnAAEBAPekCQAAAQDjBgEAaQABAAwBI4ACsGAT0wABaW8BAgD0Fg0AAAEA7AUBAAEBAaEAAAAUU2TySAYAADcCAQUA9U8QAAABAAEAQwIBAAEFAaAAAAAQ4XP1SA0AAAEAAQADAwEAAQIBoKAAABQqcvT7DwAAAQDqAAEAAQABoCAAABGCcvOLDAAAPgNcASOAAqAAAAAUB2RyAQMCsEATDQABoDAAABNiZXfylRAAALUAE4MCoHAAABOaZ28BAgD17RIAAAEA6QB5AAEAE4ACsDATHgE8aXABBgDzlxMAAAEAAQDzBQJicwFMEAAAAQMA9CkQAAABAAEAAQAABXBwaW5nAADyrBIAAAEAAAgiAAAB+xAAAAD6zgIAYW8IPQABoAAAABPJcwH6EAAAAAAFb25maWcAAPJqEQAAAQAA8gRvaW50AwBjZXP1hQkAAAEAAQAuAAEAAZISAAD2VQAAgYqwcBFLAAGxUBSNAAGgkAAAEpEAgAA7QRoAOjAAAAEAAAAAAAcAEAAAAOoBSQRKBEsEWQzIDEcQGxEBAAEKs7MbeuFj9WIQAAABALoAAQABAAGAAoATlwAQAbBQESAAAXN48wABZQFsBQAACBkAAPJ+EAAAAQAACA8AAAFEFAAAAPILY2xpZW50aGVsbG8BAGUA8mQUAAABAPKAAgBkcvIDeXB0AQBl9GoIAAABAAEAAQAAAQIA9OkTAAABAEkAAQATgAKwcBLkAAFmbgAAAAA7MAAAAQAADAABAG4HDAAAAANibGUAAAFxDAAAADKAAgGwcBOhAAFhZWnygAEBZG62AQCAAgOgAAAADMtvcmVoeQEBAPWcFAAAAQABAAEAAQDzBgFl8n4UAAABABOJArBAFHcAAWFyABJlZ21lbnRhdGlvbm9mZmxvYWQAAPJpFAAAAQAAEoJsZQEBsAATwQDgc2TygAEBYnT2fA0Aw81k2aYhgwQZ2HG+EdSwkBHyAAECgEvpSbBAAwAAgBS3DwA7MAAAAQAAGQALAJEBAwAnBAIALgYAAAMIAQAGCAMAjggBAJMJAQC6DAEAAhEBAE0RAQD0EgEAAQADZQsrn7XEoDAAAAmcSzhUmpIxYm1z9EQEAAABAAEAgQgBgAKwcBKqAAGwMBB5AAFkcvMDAWU7MAAAAQAABQABACcIBQABBAGgUAAAE75zAdASAAABgAJ8xn7YmVqwEBNWAAFhZQEAAaCQAAATdHP0uBAAAAEAAQABABOAAqAwAAAQAWRmAQMBsAAQ7gABcwFhEAAAAAAAAPPYAQAAAQABAAAAAAAA89UBAAABAAEAADYAAAACBaBQAAARiiU3Y6sdiBvOGZh223jIK8YatKAwAAAQqzAxY2VpdHcA8iUIAAABAAAIb2RlcG9pbnQAAPN7EAAAAQABAADyAAEAY/SiAAAAAQABAAEA9DYGAAABAAUHAQC2NwAAwsg+5UIs2bwLnjv5TY4AAAgCAGSTCAABiAAAAAEBADswAAABAAAKAAQAmAcGAOIPAACqEAAACxMBACOAArBwEoYAAWVvAQABoFAAABL4YwEdDQAA8wABbPJHBAAAAQABgAOg8AAAE7OwUBBbAAGgkAAAEYljaHAAC2F0YWFjY2VwdGVkAAD0ghMAAAEAAQABAAAABXJ5cHRvAAD0sBAAAAEAAQABAADyAmx5AgBjZAAB/BAAABYWAICBibBwEe4AARXYbWhRkDtjQ+E2MaEAAAAUWX/Jz9aLFaBwAAARJQAAAS4cBgAAx9Iwz9bbfYQe0PEiA8ZOJmzX731azbuPVkU5iXPnO1YQFpFL4+4OAZwecSwLRxtun2gGxgJZU4R2jTOo4nYU+NRBzfEksZEmTEwWrH2+XDhots0hYpZ5kylPM5Bfsa7mXKJZyz0QjmZhtMWyqJ4Zk1vzA/Imd1REbtgUcdqfI74wqjhD7/U6GpZJBphbmylFOas7Qa1glstAAcgogP43VwA7MAAAAQAA+gGIAAQAAgDiAQAA6wEBAPcBAABOBAEAUgQCAFoEBQBDBQEAXAUAAGgFAQB8BYkACgYBAA4GAQASBgEAHgYAACwGAQAwBgQARQYAAGAHDABuBwwAfgcAAIEHAQCgBwQAsQcIAMEHAADzBwEAEQgTAIIIAAByCQEAjgkCAJgJAQCdCQAApgkBAK0JAgC3CQAAxgkDANsJAgDlCQAA7wkAAFoMAwBkDAIAbgwAAHEMAABzDAMAigwAAMsMAAAcDQAAIA0CACcNAAAyDQAANg0BAEsNAABPDQIAVA0BALgPCADPDwAA0g8CANoPAQDdDwAA4A8AAOUPAAD5DwEABhAAABYQDQAuEAEAShABAFIQBgBbEAEAgBAAAIcQAACMEAEAnxAAAKYQAwCsEAAA3hACAOIQAQD/EAIAEBEDABgRAgAnEQAAMhEKAG8RAABxEQIAeBEBAIgRAQCYEQEAsxEBALkRAQC9EQMA2hEDAPARAACFEgAAjxIAAJESAACcEgMAoRIDAKgSAAC8EgIAwxIBAMgSAQDREgUA5hIBAOsSAwAFEwEAGRMAAB4TAAAiEwAAJxMBADITAQBHEwEATxMGAGsTAAByEwEAdxMCAIcTAQCjEwUArBMBAK8TAACzEwAAvxMBAMQTAgDOEwAA0xMBANYTAQDtEwEA9xMBAP8TAQAZFAEAJRQEADkUAQBQFAEAWRQBAHMUAAB1FAEAghQDAJAUAAABAwGAE2kAEO5zOzAAAAEAAAUAAQBJEwUAI4ICoJAAABRBaXMBgQKwUBN3AAGxABROAAFtdAEFAPNxEQAAAQABACOAArDQEycAAWFlAQQA8wURAADlAAEAAQcBgBNYAIAac/S9EQAAAQABAAEA84ACYWUBBwD3IhEAAAEAAQC8AgEAggAFABOAAqAwAAANOGVyAQcA9U8TAAABAAEAAQABAAGAAqBAAAARYrBQEUsAAWNpM4ADoFAAABFwYW9yAQAA9BETAAABAI0AAQATgAOgYAAAEcGgcAAAEpFpb3IAA2lzdAAAASgRAAAA8gVhdGlvbgEAaPKECQAAMAAB2BEAAFKAAQKwoBPxAAFyY24BgQFBN9NQEAFrAYACsHATBQABF1Ar4ADEcngBAgGgAAAAEOFzOzAAAAEAAAsAAwBIDQIATRABAGsQBgATgAKgIAAAE+JldQg8AAGwcBQ7AAFwOzAAAAEAAAcAAQBiCAcA8tUQAAABAJKAAgLnAAAAB22wIBDpAKNjaWFyVgEAAIGHf+/IpehioIAAABSQIeWvPjo4oEAAABAFsIARGQABAAAEskFAAPR9CQAAcwYBAI4DCEoAADswAAABAAAHAAIA1AEAAPsBBgAAAAV0cmVhbQAAAa8QAAAACE0AAAFhCAAA8sMPAAABAPKCaW4CAGVzMgACAjZ4az/leTKeqXk21WFvZXYAOjAAAAEAAAAAABEAEAAAAPMB9AFHBkwNxQ/GD98P+A+KEIsQMBExEbYStxK4ErkSFRQWFAEBAPOgCQAAAQABAAEAAV3jOWcfXGT0/xAAAAEAmAABABMAA7AwFDUAAbBQEwsAAWVwcvTyDwAAAQDkAAEAAQADsEAUAQABoUAAABRooFAAABHkYmNmAZcHAAAAA2F0cwAAAYwRAAAACCgAAAEnEQAAAPKEb2FkcwIAaXTyAAEAbADzRREAADsCAQDyAAEAbgA7MAAAAQAAJAAIAM0HBwBPEAIAmxENAMIRAgCUEgEAFxMBAGcTAQD5EwIA1gIAgAEFsUAUjQABoMAAABRNd2JtbnB1AQQBsCARWwABZfK4EgAAAQABBAGwIBFZAAFl8rYSAAABAPOBAjQ2AQUA9IEQAAABAKcAAQAAA2xheQAAAdsHAAAAAARyZGVkAADybwwAAAEAABKAAQEYwHuFEy1hbwAEYmxlZAAA8oIJAAABAABygAMBsFAUawABYWNwdAENAPRTFAAADAAeAA8A8gAAAWU7MAAAAQAABQACAHkAAQCeAAMAAe4JAAAAEml0YWxseXNpZ25lZHN0cnVjdAAAAeITAAAA9gIAAIOEfaHI+kN9sDATXAABQAADAaAEADowAAABAAAAAAAHABAAAAB0EXUR4BLhEkUTRhPYE9kTEQACpbCQFBUAAWF08wwGAAABAH0MAQQA83sQAAABAAEAAQEA9T8RAAABAAEAAQABACOBArCQE+sAAWF0AQABoBAAABQHZQEbEQAAo4CEsRAUZAABoIAAABOuDEAEAQAAOjAAAAEAAAAAAAcAEAAAAG8McAxkELATsRP8E/0TBxQAB2luYXRpb24AAPMMEAAAAQABAAAAB3JpcHRpb24AAPIKEAAAAQAA8oACAGN0AQIA9YcMAAABAAEAFQTkAAECAbCAE88AAXQBlwkAAAEAASwrQ8msNXLyqREAABgACEEAAPOBCQAAJAMBAADyAAEAbfNJBQAAvgABAAAygAECoDAAABOyZmRysgABA7AgDHcAAWVpa3IAOjAAAAEAAAAAAA0AEAAAAH8HgAeLDMkPBREUESURXxHqEesROhMKFDsUPBQAEHVwcG9ydGVkdmVyc2lvbnMAAAFYFAAAAAAGdW50aW1lAAABhRIAAAAADWF4aWRsZXRpbWVvdXQAAPKIFAAAAQAAABBlZXBhbGl2ZWludGVydmFsAADylhQAAAEAAPKDYXRoAgBrbQEKAPPlEwAAAQBwAAAhbmN1cnJlbnRtdWx0aXBhdGhwYXRoc3doZW5lbmFibGVkAAABoRQAAADygAEBb2zyBGF1bHSEgASAAzswAAABAAA0AAIA2gcAAC0IMwAACCIAAPJ5EAAAAQAAAAdpbml0aWFsAADyqhIAAAEAAPKAAgBkcvohAQBlOzAAAAEAAAUAAQAnCAUAAAAAAAGwQBMJAAFlOzAAAAEAAAwAAQBgBwwAARARAAAIIQAAAW4MAAAA+pYCAGFl8oACAG9yAAJ1ZwAAAUgFAAAA9mUBAIWIoDAAABD8sEATCQABsCARLQABsFATtgABJgADGagkADswAAABAAAsAAsA6wEBAPcBAABOBAEAYAcMAG4HDADBBwAAcgkBALgPBgAQEQAARxMBAHkTAAABgAKwABPKAAGwABPMAAE0NiMCAhwXrDQxGHJ28wMQAAABAPID8wIBZfSKEAAAAQClAAEAAQIA9CkQAAABAAEAAQABAQGgQAAAERVl9JgJAAABANUCAwATgAKgUAAAEpBscwEEATYPrf+r12Py9BIAAAEAI4ICsFASxgABZnQAB3JhbnNmZXIAAAEmEQAAAAAIbmJsb2NrZWQAAAEsEwAAAAADYWNlAAABWhQAAAAIGAAA8lAUAAABAADyiW5kYnVmZmVycwIAaXAyAAIBoCAAABMZZXVp9W0MAAA0AAEAAQABAAAACXVmZmVyc2l6ZQAA8nUUAAABAAAShmVjZWl2ZQIAwAAAABKpYmQAA25mbwAAAXgQAAAAAANlbnQAAAH5EAAAABKAAQGwMBMNAAF2cvYAAANyYW0EAaAwAAATYmVpcnN39X4JAAABAAEAMgABAPWQEAAABQC0AAEAoAEABmxvY2tlZAAA9OoPAAABAMIAAQD0DxMAAAEAjQABAHYAAACDg06J0yX0HKBwAAATmqBAAAATMEIABAFAAQH6AQAAOzAAAAEAAA8ABgDmBwEA4wkBAP0QAQAIEQEAkhEFAIsSAQBSgAECFOiWHB1LYWVp9gAAgAEEsOAUSQABdGJjZHYAAAcQSokn8C8MGNtF/pfRa4d/K/1iQmsZ9O7wHGCIUqqcNuhslCTYdsccj/zYoDAAABEcCx80C3rRIgnpXy4NOIS5XD3TFgHCbA/QFIh9f2PMC5gL6UE7sPAUigABEdnRwk8RfYX0RhDjHJstAaj0Wof7yxq5BsuA6Q4SUGWBkTDGbCX3vmaxoAAAABMwYWVpb3JzdTBiY2RmZ2hrbG1ucHR2d3kAOzAAAAEAAG8CcwAIAAAAXgANAIQAAADTAQAA6QEBAPgBAQBBBAIASQQCAGUFAABtBQgAQQYAAEkGAwBPBgEAXwYAAFAHAACfBwAAsAcAANUHAwDlBwAA9wcFAP4HAAALCAUAYQgIAIAIAACDCAYAkAjhAIIJAQCICQEAlQkAAJ4JAQDKCQQA8AkBAPQJhABZDAAAXgwBAGIMAQBrDAAAbwwBAHoMAgCwDAAAvQwAAMQMAgDIDAAA1wwAADMNAQA7DQAATg0AAMMPAQDqDwEAARAAACQQAQBHEAAAWRABAGQQAAB1EAIAjhABAKAQAACtEAEAuBADANUQAQDwEAEA9BADAAYRAQAbEQAAPREBAF0RAABwEQAAehECAJoRAACpEQAAwREAAM8RAwDWEQEA5BECAOgRAQDuEQEA+hEEAJcSAACmEgEAqRIAALUSAADGEgEA6BIBAPsSAwAPEwEAGhMDACMTAQApEwEALBMAAC8TAAAxEwAAOxMBAEETAABWEwEAcBMBAHQTAACCEwQAnRMBAK4TAACwEwEAthMBAMETAADPEwMA/BMCAAMUBgAXFAEAHhQGACsUAAA+FAEAVBQBAGYUAQBpFAEAdxQBAJQUAQChFAAAAQACoGAAABM6sKASwQABOGU7MAAAAQAACQACAHAIBwABEwEAAQgA83sQAAABAAEA8wABYzswAAABAAAHAAMAogADADYGAQA8DQEAAIABAfZAAfgAAbAwDSAAAW5yAAJvcgAAAUQGAAAAAQkA810UAAABADUAAYECsIAUawABoGAAABSPZHMBDAGgoAAAFKF38pEUAAABAAABdQAA8wcQAAABAAEAAPKAAQF0dT4CAIQBBaBAAAAUQLBgFBQATLAwE/8AAW1icHN0dfKAAgBycwg4AADy6A8AAAEAAPICaWMBAGP0RQUAAAEAAQC/AAAygAIBoHAAABFeYnJkAQMA9HkQAAABAAQAAQABCgGgQAAAFERl8mQUAAABABOAArBgEqoAAWNp84ACZHIABmVzc2lvbgAAAYsRAAAACDgAAPLmDwAAAQAAAANpemUAAPJUEwAAAQAAAAd4Y2VlZGVkAADy/BMAAAEAAPKFdWZmZXICAGVz8gADAGJlczswAAABAAAMAAIAQgYBAFUHCgA7MAAAAQAABQACAF0QAQCwEAMA8gJwdAEBb2UAOzAAAAEAAAkAAgAnCAUAaggDAAAFZXJpb24AAAFsDAAAABKAAQKgIAAAFHSwQBAKAAF0YnAAB2VzZW5kZXIAAAElEQAAABKBYQEBsAAMYgABdGTygAMAZWl5AYICsDAThwABOUxYWi26YXkBAAJmIbKy/iOwgBM4AAFzdvTsCQAAAQD2CQEAAAZldHJpY3MAAPIBEwAAAQAAAAZhY3RvcnkAAPL/EgAAAQDyhhQAAAEA8gJlcgIAZm3yLA0AAAEAABKCb2wBAbBAEy0AAWxlAAN5cGUAAAHlDwAAACrKAgDAAAAACbFzdBKAAgDnQAAAB9luePKAAgBlcgEJAPOwEgAAAQABAAAHcnZhdGl2ZQAAAawQAAAA8oABAWVpCGsAAAHIDgAAAAADaXRoAAABqxAAAAAIOAAA8uIRAAABAAASAAEBoCAAABN5dGkA8uQSAAABAAAGZWZ1c2VkAADyKRMAAAEAAAADb3N0AAA7MAAAAQAABgABAGMRBgAAAAVhcnNlcgAAASsTAAABgRQAAAAJaW1pdGVycm9yAADyQhQAAAEAAAAIZW5lcmF0b3IAAPLgEwAAAQDyYxQAAAUA+ikDAGdscPN1EAAAAQABADowAAABAAAAAAAPABAAAADlEeYRGhMbEzsTPBNwE3ET0RPSEwgUCRQXFBgUVBRVFAAFYW5kbGUAAAGoEgAAAbMTAAAIHAAA8uARAAABAAAIMAAA8t4RAAABAADygAIAcnYABGxvc2UBANAAEqYAAWQ7MAAAAQAABQACANoRAwDTEgEAAPIBboeAlAkD9SkNAAABAAEAngEBAPcMEQAAgQABAAEAAQCRATgAIoACAOZwAAANKG5vCAIAAPNyEAAAAQABAAAigAIAwAAAAAxrZHIABWxpZW50AAABihEAAAD6Y4SAFAEQ8iUIAAABAADygAIAZXMABWluZG93AADyFxMAAAEAAAAEdGF0ZQAA8tESAAABAAAIXgEA0AATFQABcwA7MAAAAQAACwACAI0TBwB5FAMAABBvbnRyb2xsZXJmYWN0b3J5AADyhhQAAAEAAAAIbGdvcml0aG0AAAFvEwAAAPYBAAVzdGlvbgUBsIAUlAABYWNlc3d09MQOAAABAAEAAQAAAAJsZwAAAbAJAAAA8oACAGFlABByYWNpbmdzdWJzY3JpYmVyAAABdBQAAAAICQAAAdASAAAA8oJyZQIAY3QIKQAA8uMPAAABAADyAAIAZXXyUwcAAAEAOzAAAAEAADUAFQBnDAEAQA0BAOgPAQBlEAUAlhAFAA0RAQBqEQEA9BECAL8SAwDvEgQAHxMBACUTAQA0EwEAtBMBAMcTAQDlEwEA6RMBAAwUAAAzFAEANxQBAFYUAAAADmVudGlhbGl0eWxpbWl0AADy6xMAAAEAADKBaQIBsDATLwBXZGdy9gEAgAUBOIljjaFYZmduc3RjAQIDoEAAABEECPHrUOLWoFAAABFvYWJm94oJAAABAEUA/AYBAF4AAQABBADzvBIAAAEAAQAAAAAAOzAAAAEAABMAAQARCBMAAAhAAAABJw0AAADygAIAYWXygAEBcnQSgAEBsDATowABYWwABG5vcHQAAAFqDAAAAAAAAAGggAAAFJBmAX0JAAABfxMAAAAHcnVwZGF0ZQAA9U8TAAABAAEAAQABAAAABWN0b3J5AAABYhEAAADygAIAYW/yAm5kAgBmczswAAABAAAGAAIA2AcAAAsIBQDyTg0AAEwE8oACAGFv8oACAW1waQACbmcAAbBAE6EAAWXyUQcAAAEAACAAAAKwQBMJAAGdZXAB9wEAADswAAABAAAjAAUAYAcMAG4HDAC4DwYAEBEAAEcTAQAygAIBsDAMbgADZWlh9gQAgAMEsFATgAABsBAUIQABZG1ubG9ydgheAAA7MAAAAQAAEwABAIoAEwA7MAAAAQAAEwABADAQEwAABWVhc29uAADy4g8AAMgAAAgKAADyYBEAAAEAAPpPAQBlAcMOAAAACGEAADswAAABAAAGAAEAaxAGAAAIAQAA9CMNAAABAAEAAQAA8gACAHBz8kEGAAAPATowAAABAAAAAAAKABAAAACVCbAMvQwGEQcRphKnEvsS/BL9Ev4S8gFlAwBkcHL1RAUAAL4AAQABAAEAOzAAAAEAAAoABADPDwAA2hEDAKESAwDTEgEAAAdvdW5pbml0AAA7MAAAAQAAhQACAPgQAAAAEoQAAAADbnRvAAA7MAAAAQAAhAABAD4OhAAAAAJveAAAOzAAAAEAAAcAAQCZDAcAAPoDAwBiaXQ7MAAAAQAAhQACAEMFAAB9BYQAAPKAAgBucwEEAaBAAAAURGXyZBQAAAEACHwAAAHhDwAAABKAAQGgMAAAE75hcvIAAQB0AQoIAAAACEEAADswAAABAAAFAAEAZRAFAPa/EgAAAQAlAQEAUQAfAAADZGRyAAAB3w8AAADyAm50AwFhY3NoOzAAAAEAAAcAAgA+BgIASwcEAPSuDAAAAQDbBEYB8gABAGUBeAAAAADygAIAaW8BgQKwMBAuAAGgkAAAFHRycwAEdWl0ZQABoAAAABM3cwHgDwAA86wTAAABAAIA+jgBAHMBSgcAAADyAAEAaADzugcAAAEAAQAACHhoYXVzdGVkAAD09BAAAAEAAQABAAF0EwAAEoABAX/vyKXoYmViAAZmZXRpbWUAAPSmEAAAAQABAAEAAAgpAAD0BggAAAEAAQABAADygAIAZWkIMAAA8yIRAAABAAEAAHYAAAADArDwFEkAAbBwFDsAAWdsc2FwADswAAABAAAUAAUAYggHAIMIBAAzDQEAuBADANUQAQC2AACAAgOgMAAAEAGwwBQjAAFkcGZudAECAV1EhQgPbmHyyQwAAAEAAAAAAPM9BgAACwEBAPRGEAAAqAABAAkC8gJuawEAc/JBBQAAAQDz1g8AAAEAigABAAD1tRIAAG4AAQBCAQEAAAhuZGluc2VydAAAOzAAAAEAAAUAAQDKEgUAABKBawEBsBATVgABYWU2AAAAAQRSCYtKqtMB83DORTKwUBEgAAFjZG1zeADzwxIAAAEApwABAgKwQBSKAAGwMBMHAAFnaPWzEQAAAQA5AgEAawAABGhhMjAAAAF8CQAAAJKAAQOgEAAACXw6AtclsitjMmxu9gAAAAMCoDAAAA0iYWV1b3MA82wFAACMDAEAAQMA9LATAAABAEsAAQABgAKgcAAAE86gMAAAERhmdAgWAAABHRQAAADyDWV2b2NhdGlvbmxpc3QBAGUA8kcUAAABAAgBAAABpRIAAAAICwAAAV8RAAAAMgZmaWNhdGUCAWBCgTYQTWRlcwEGEAAA8jITAAABAHpDAgJOvz6VqnRpcnN2APT2EgAAAQB1AOYANgAAAIGGNNEyyctFsIAS1QABsLAUZgABoCAAAA01d58ZpBgqAAABEJ0A8wQAAAABAAEAOzAAAAEAABIACAAOBgEAHgYAALEHCADLDAAAUA0BAFQQAQAeEwAAWhQAAAECAPPDEgAAAQCnAAACc2UAAAF8BQAAADYAAIABBDxGf1y/E7AwDG8AATFeHtvtPnVjbHJ09j8GAMrIsCAShgABKW8TaNH6Sjn09hfmf7OSBrSyc4bpsW3Lfpu75rHUQGSWFIABCUsA93IAAAAWAGUBWAMBAAEAvwABAAGwUAxnAAFj9HMAAAABAAEAFAABBgELTo1uDLdz8k4NAABMBAhxAAABewkAAAASAAEBoCAAABKPbWk7MAAAAQAABgACAEAFAAB2BQUA9ugJAADfBQEAJwQSAAEA+hYBAHMAATIRAAASgAEBsDATEwABdHAADnJhbnNwb3J0Y29uZmlnAAABDBQAAAASAmxkAQGgYAAAE7J0ZTswAAABAAAIAAEAbQUIAAABgQEWZxIiKstyCAMAAAE8BgAAAPIAAQFtZgF3AAAA9fAHAAABAMsBAQCHBzKAAwHmsAAAAfagAAAABUhmaWxnAQIBZ8eIdJfkc/JZEAAAAQAAAAAAAXYAAAA7MAAAAQAABwABAJkMBwAIAAAAOzAAAAEAAOIAAgBpDAAAXA3hAADyA3JvdwEAbTswAAABAADiAAIAOwYAAGYG4QAA4oADAebAAAAB9W9yeHUACW10b2tlbmxvZwAA8/IQAAABAAwBAAECAPQREwAAAQCNAAEAAQMA9EUTAAABAJIAAQDyA2tlZAACYnXz1QcAAAEAAQA6MAAAAQAAAAAAEAAQAAAA6g/rD44QjxCtEK4QPRE+EegR6REPExATHBMdEywTnROeE/KAAgBjbwAIZGV0ZWN0ZWQAAPK2EwAAAQAAAAdvb2xkb3duAADygBMAAAEAAPKGY2tob2xlAgBjczKAAgInyaVivQ6ggAAAE/Bhb2V5AQMBP4IF2G17dDswAAABAAAIAAIAYQwAAI8MBwATAAKADKcAEAFyc/KGEgAAAQABAQKw4BRTACqwYBRrAAFyczowAAABAAAAAAAHABAAAAB0EXUR4BLhEkUTRhPYE9kTAQABsFAP6AABY/RFBQAAAQABAL8AAAVvY2tldAAAAQUQAAAAGkgBAaBwAAASkHNhAAGfBwAAdgEAAAEEoGAAABSPbmNkc3TyAgAAAAEA9EgGAAA3AvsAFAMBAAD0ixMAAAEADwABAAEEAPN6EQAAAQABAAAFcG9ydHMAAPIgEQAAAQAAEoABAYATlwAQAXhz8gJjaAEAZQFsBQAAAFYBAIABBLBADTYAAaAAAAAUdG5maHJzCCMAAPJnDAAAAQAA8gFyAQBj9HMAAAABAAEAFAAAAQEA9F4MAAABAI8FAQATAwOwMBQ1AAGwUBMLAAFlcHL08g8AAAEA5AABAAAGY2hzaXplAAABIg0AAAAAD21heGZyYWdtZW50c2l6ZQAA8icTAAABAAAAA2t1cAAAAToGAAD0uhIAAAEAowABAHYBAIADAqCgAAAT8GNkdGJu9l8AAIiEoBAAABHVQnlnD8++E0lIBIADAPLTEQAAAQAABmlsYWJsZQABsGAUSQABZvNkDAAAAQABAPRyEwAAAQCEAAEAEoABAUsbUZo/PWFlAQIA9fQQAAABAAEAAQB9AiOAAqAAAAAFfGV0AAhvcnVwZGF0ZQAAAZAUAAAA8gp1YmNvbW1hbmRzAQBmAX8TAAAACAEAAPUlFAAAAQABAAEAAQAA8gNyZ3MBAGb1oRAAAAEAAQABAAEAAPKEbWVudAIAYXNSgAECf1ElRT7JZ2xzAYACoVAAABRjoAAAAAZGZHLzAAFlOzAAAAEAABUAAgDcAQAAKQMUAAAEcml0ZQAA8iANAAABAAAACGRwc29ja2V0AAABXhEAAAAABGltZXIAAAEfDQAAAAADZWFkAADyYgwAAAEAAPKCbmOEgAAAHQAFbWJsZXIAAAGYDAAAABKAAQGggAAAEZpldQAEc3RscwAAAZcMAAAAIoACAOZgAAAFP2V15gQAgAQG5kAAAAHToDAAAAnPsHAShgABfuEshas6oRAAABQqoIAAABCFZnJzeTBlaG9wdAEAAT+CBdhte3Q7MAAAAQAACAACAGEMAACPDAcAAQEDoHAAABKPoHAAABEPsAATKwFWYmly8hIGAAABAAEBAbDQE7AAAWLzYAYAAAEAaQEjgAKgMAAAEdlhbgEAAqAgAAATMKAAAAAHsGFkOzAAAAEAABMAAQARCBMAAAJheQAAAWsFAAAAAQgA9SUUAAABAAEAAQABAPIAAAFmAdIBAAD1oRAAAAEAAQABAAEAAAZhdGNoZXMAAUbb9zNOc20BHg0AADswAAABAAAMAAMAbBEAANcSBQAtFAUAMoACAbAACaYAAW1zZQAHYWtlcmVzdAAA9HsTAAABAAEAAQAA8gZpdHJhcnkBAHQ7MAAAAQAACAACAGEMAACPDAcAANasAICEiMAAAAAAci47LCZeFrAgDG8AARX0X3hLWUvIvRNaQ0YAARkJRgEIAPOZEgAAAQABAAGHAhjShYkhsR8GjogLcGhtCCoAAPL5EgAAAQAAAAAAAPT7EgAAAQABAAEAAPpeAQBk9KESAAABAAEAAQAAAApiYW5kb25wYXRoAADyNRQAAAEAAHKHaWNhdGlvbgMBsHAT4wABYWNlcPKAAQFscvKAAQFwdQEAAaAAAAAR2XPz4gcAAIsLAQABAQKwQBSKAAGwMBMHAAFnaPZcBQAAVwwBADkCAQBrAAAAAAKggAAAEsWwcBQSAAFhcwFxAAAAOzAAAAEAAAcAAQDTCQcAVgEAgAEELVAAQRtacRo6I3R2eWRnc3QBBQA7MAAAAQAADQABAJsRDQABgAJmlX9EzzegUAAAE/VlcAECAbBAEq4AAWUB3Q8AABOAAmwd2nqg2Wl0AQEA8xMQAAABAAEAAQMBoEAAABQHZfJ3FAAAAQDzgAJvdAECAaBwAAATMWUBOw0AAAEDADswAAABAAAIAAEAvxAIAAGAA6BAAAARELDAFDsAAaBgAAAR2GVzdQGAArCQE6wAAWxCMNqnAmltAQMBeVAmGKUrZfTFDwAAAQBPBAEAAAAAAAFqBQAAAQQRAAAatgEBX6L7/2o+c3ABgAKwUBEGAAGgsAAAE+JjcwEDAPQREQAAAQCnAAEAAANwaW4AAPJaDQAAAQAAEoF3AQGgEAAAE65zZdIAAQOgUAAAEvhvY2V5AAEdDQAAAQEAOzAAAAEAAO4AAgAuDQEAyw7sAAECAPRtEQAAAQDXAgEAAAhyZWNlaXZlZAAA8vAQAAABAADygAEBdHIygAECoAAAAAyAcm5x9kNeAMPQoFAAAAx6Hvg3kRHGWXAnJNnkR3q6PIdMJuXGEcnlsUAUmgABoEAAABCAAAQiAEB7nR0AOjAAAAEAAAAAACEAEAAAAEcESAQQBhEG3QfeB+EH0QnSCS0QhhDqEOsQ7BDtEKoSqxLiEuMSWhNbE4kTihMNFA4UDxQQFBEULBRbFFwUgBSWFJcUAAI1NgAAATkGAAAAAAIyOAAAATgGAAAA8oACADEyAA1kbGltaXRyZWFjaGVkAADyIxMAAAEAAPKAAgBhcwECAaBAAAARgmUBRxAAABOBArAgD8oAAWVtAQgA9L0RAAABAAEAAQABAwGgAAAAEEZz8tYPAAABACOAAqCwAAATzmVoAQMBoEAAABEVZfKYCQAAAQABAQA7MAAAAQAACAADAMUSAAA2EwAAmhQGAAAEb3ZlZAAAAV0RAAAAGrABAWPPx2dGDm1wAAhpc2NvdmVyeQAAAaASAAAAAARkZGVkAAABZBAAAAD2AAACc3MDAluwQKPNGGFkcmV2ADswAAABAAAWAAgAAxABABYRAQBZEQMAyBEGAKkTAgD2EwAAThQBAH4UAQASAAEBMp6peTbVZXby8wEAAAEAOjAAAAEAAAAAAA8AEAAAAEcGTA3FD8YP3w/4D4oQixAwETERthK3ErgSuRIVFBYUABJhdHRyYXZlcnNhbGFkZHJlc3MAAPJOFAAAAQAAAAdhZGRyZXNzAADyWxEAAAEAAAgAAADyWREAAAEAAPKBdgIANDYSgAEBsBAH7AABcG4ABWRyZXNzAADyAxAAAAEAABKAAQGwUBCQALlkdPYAAAAEAaAQAAAQZGFpbnJlAPTPEQAAAQABAAEAtiIAAIGLsCAH8QlTQaFbzqP5sLATIwABsOATJwABOETzCglzoIAAABHBoAAAABMwCAAAlxhOADowAAABAAAAAAALABAAAADpAUEEQgRDBGIMYwzEDMUMxgwxE2kUahQBggKwMBAuAAGgkAAAFHRycwEAAaBAAAASimnyHhMAADwBAYMCsHATgAABsIATtgABY3MAAAAA8vEBAAABADswAAABAAAFAAIAwAwBACkQAwAIKgAA9CUTAAABAI4AAQAA8ghyZXF1ZW5jeQEAY/ViEAAAAQC6AAEAAQAAAQCEsKATCQABsCAMfgABsAAQygABsIAS6AABCAQG85QHAAABAAEAABBpY2l0aW5ndGhyZXNob2xkAADyIxQAAAEAAAgTAADyNgYAAAEA8jwNAAABAHKAAgKwAAffAAFjbHR5dgEAAIODsEARLQABEFC5roLkMAACiAAI8m8AAAABADswAAABAAANAAQAjAcHAO4HAQCBEAEAKREBAAADaXRoAAABAhAAAAAAAm5pAADyYAwAAPkAAAh2AAAB3g8AAAAACWFuZHNoYWtlcwAAAXoTAAAAAANydHQAAAEcEQAAAPIAAgAwaAD1+hEAAIgBAQABAAEA8oACAGRy4gNlcHSEgPaQCXoDFBIAGPU1BgAALQABAAEAAQAAdgEAgAIESxzo1nEkXzaVnS3jY2tlaGl0AQUBoFAAABQ9cwGQEgAACDcAAPJeDAAAAQDy7hEAAAEAEgRuZG9uAQKwMBQ1AAGwUBMLAAFlcHIA9PIPAAABAOQAAQBSgAECOUrqPBGjYWxzAADLzAKYtvpNkWXJDoGdXD09UEkg+3aoPWs+3R3plCGdGiT3fZ2rjQz9DctwOxCq++OzeFKLEd0H6AUFzm5Ld015CDwuD6AQAAAJfE6J0yX0HKEAAAAUWU8cTPoB/VGI6XK9TB+wchbPbRTi6ySNQKBwAAATmjN8JDsbQSc5LyoN+A52hcIKKlJAu/h7SoAHqg1CWBVyADswAAABAAARAAgA+gEAAOYHAQCcCQAA4wkBAP0QAQAIEQEAkhEFAIsSAQAAgBoO4pAAAAAA4+AAAAAB5dAAAAAKIV2b2qEMA0WQ++6EZqOJpkuuOo6AQbnSTf/mcSkWGs0zhBnKclsc7ztjPQNlIdkcH9PHUQIxQCbnsfVgc7BBUVZFSXxgn0apePCXCwupHvS/sAt8dD7Wo7omLcuXSTLWZfd4I/rXQlHpUzxfbdOv2ZXCZZ2pSvwND7Z4e/ExDYiTxEYRGtFvOo/toAAAAAAAVHQzHDTXbw8vVC6JeWaDNCRaGujVLkzhZQTJFMKtoBAAAAY5XFAcD/NGUdsbkTT/oAAAAAABoAAAAAAKUacDYsFQQk1aek+hCAjjgXhzKC1bYWJjZGVmZ2hpa2xtbm9wcXJzdHV2d3opMDEyMzQ1Njg+XWp4eQ==","N":"ADJb","E":"OjAAAAAAAAA=","H":"3Yl/E793"},"crateNames":{"N":"j","E":"OjAAAAAAAAA=","H":"JFDJy0NX"},"name":{"N":"ADJb","E":"OjAAAAAAAAA=","H":"B9bGJJzs"},"path":{"N":"BAj","E":"OzAAAAEAAIcStwACAAQACQAAAAsAYgBvAAEAdQAAAHsABACGAAEAigAbAKgA5ACPAQgAmQEjAL4BDgDRAQAA1QEFAOEBAQDlAQIA6QEAAO8BBQD3ATICKwQAAC0EEgBBBAIARQQDAEoECgBWBAkAYgTcAEcFAABZBQIAXQUDAGIFAABkBQAAagUAAG0FmAAHBgEACwYIABUGCAAfBgIAJQYIADIGAgA2BgQAPgYFAEsGAQBXBgIAWwYCAGIG6ABMBwQAUgcAAFQHAABXBzwAlQcPAKcHAwCsBwMAsQcYAMsHDADdBwEA4QcAAOYHAQDqBwAA7AcDAPgHAAD6BwIA/wcAAAIIAAAFCHQAewgFAIMIBgCLCO4AfAkAAH4JAQCCCQEAiQkAAI8JBQCZCQAAnQkAAKAJAgCmCQEArwkFALcJGADRCRQA6Al1Al8MAABlDAEAawwAAHAMAAB2DAAAegwDAIcMAACODAkAmQwHAKIMGQC9DA0AzAxMABoNAwAiDQAAJA0BADwNAQBZDWoBxg4CAMsOEgHfDwAA6g8BAPAPAwD2DwMA+w8BAAIQAwAHEEAASRACAE4QFgBrEAEAbhACAHkQAQB+EAQAhBACAIsQBACSEAAAoRAxANQQHQD0EAMA/BAAAP8QAQACEQEABREHABERAQAWEQEAHBEDACERAAAjEQIAJxE2AGQRBQBtEQEAchEFAHsRBQCFEQEAihFQANwRAADgEQAA5REBAOgRAQDvEQAA+BEBAPsRAwAAEp8AoRIAAKMSAACmEgEAqRIAAKwSAwCxEgoAvRIBAMUSAgDJEhgA4xIXAPwSAgAFEwEADxMRACITAgAnEwMALBMHADYTAwA7EwUARRMBAEoTAABMEwEATxMbAGwTAgBwEwQAdxMfAJkTCwCnEwQArhMAALQTJQDbEwQA4xMFAOsTGgAHFAIADBQQAB8UFwA5FAoARxQFAE4UFABkFAMAaRQXAIQUHQA=","H":"dyRQuDwW"},"entry":{"N":"ACIj","E":"OjAAAAEAAAAAAAcBEAAAAAAAAQAHAAgACgBuAHEAcgB2AHcAgACEAIUAiACJAKYApwCNAY4BmAHNAc4BzwHQAdIB0wHUAdsB3AHdAeAB4wHoAeoB7QHuAfUBKgRABEQESQRVBGAEYQQ/BUAFQwVEBUgFSQVKBUsFTAVNBU4FTwVQBVEFVgVcBWEFYwVlBWgFaQVrBQYGCQYKBhQGHgYiBiMGJAYuBi8GMAYxBjsGPAZEBkUGRgZHBkkGSgZNBk4GUQZSBlMGVAZVBloGXgZfBksHUQdTB1UHVgeUB6UHpgerB7AHygfYB9kH2gfbB9wH4gflB/AH8QfyB/MH9wf+BwAIAQiBCIIIigh7CYEJhAmFCYgJjAmNCZcJmAmaCZsJowmtCa4JtQm2CdAJ5gleDGEMYgxjDGQMaQxsDG8Mcgx5DIAMggyDDIoMjAyNDJgMoQzLDBkNHg0gDSENIw0nDSkNLg0vDTINNQ02DTgNOQ06DTsNPg1CDUgNSw1MDU4NTw3EDsUOyQ7KDuIP5Q/vD/0PARBIEEwQTRBlEGYQchB1EIMQhxCKEJEQkxCWEJcQnBCdEKAQ0xD4EAERDxETERQRGBEgESIRXxFgEWIRYxFsEXERehHkEeoR7hHwEfER9BH3Ef8RpRKqErASvBLIEuIS+xIJEwsTOhNBE0ITQxNJE2sTlxOYE6UTphOvE7IT2hPiEx0UHhREFE0UghSDFA==","H":"Pnqebaw9"},"desc":{"N":"FMc","E":"OzAAAAEAAM4NgwEAAAEABwABAAoAUwBsAAAAbgAEAHUAAwCAAAEAgwACAIgAFQCmAOUAjQEBAJgBAgC6AQIAvgEBAMMBAQDGAQAAywEJANsBAgDgAQAA4wEAAOYBAgDqAQAA7QEBAPEBAQD1AQEA+wEGABgCAAAjAgAAJgIBACkCAAArAgAAMAIDADsCBABDAgAATAICAFQCAABZAgEAXQIAAGECAABkAgAAawIAAHgCAAB/AgoAnwIDAKQCAACoAgAAvAIAAMECAADHAgAAzQIAAM8CAADWAgAA2wIAAOQCAADpAgAA7AIAAPICAAD5AgAA/gIAAAUDAgAJAwcAGgMAACkDFAAgBAQAJgQAACoEAAAsBAAALgQAADAEAAAyBAwAQAQAAEQEAABGBAAASQQAAFIEAABVBAAAWwQBAF4E4gBDBQEARwUKAFYFAABcBQAAYQUAAGMFAABlBQQAawUBAG4FAgByBQMAfQWEAAYGBAATBgEAFgYCAB4GAAAgBgUAKwYAAC4GAwA2BgMAOwYBAEIGBQBJBgEATQYBAFEGBABaBgEAXQYCAGYG4QBLBwEATgcBAFEHAABTBwAAVQcBAFkHAABdBwAAYAcIAGoHAQBuBwsAfQcAAIQHAQCHBwEAjQcCAJEHAwClBwIAqQcAAKsHAQCuBwsAvwcBAMoHAADOBwIA0gcCANgHBADiBwAA5QcCAOoHAADsBwcA9wcAAP4HAAAACAEACAgAAAsIGQAtCDAAXwgBAHEIAgB1CAIAfAgAAH4IAACBCAEAiggAAIwIAACQCOEAewkEAIEJAACECQEAiAkAAIwJAQCTCQEAlwkBAJoJAQCjCQAArQkBALIJBADBCQAAygkEANAJAADUCQIA2AkCAOMJAQDmCQIA9AmzAKoKAACsCkwA+goAAP0KWwFdDAEAYQwDAGkMAQBsDAAAbwwAAHIMAAB0DAEAeQwAAHsMAQCADAAAggwBAIoMAACMDAEAjwwJAJoMAgCeDAMApQwBAK0MAAC4DAMAwAwBAMMMAgDJDAIAzwwAANEMBQDYDD0AGQ0AAB4NAAAgDQEAIw0AACcNAAApDQAALg0BADINAAA1DQEAOA0GAEINAABIDQAASw0EAFwNZgHEDgEAyQ7uAMkPAADPDwMA1A8BANwPAQDfDwMA5Q8AAOoPAQDvDwAA8g8BAPYPAwD7DwIA/w8CAAMQAwAWEA8AJxABACoQAAAsEAEAMBATAEgQAABMEAEAYhABAGUQAQByEAAAdRAAAHgQAACBEAMAhxAAAIoQAACMEAMAkRAAAJMQAACWEAEAnBABAKAQBQCoEAEArRACALEQAACzEAAAuhABAMEQAQDFEAQA0hACANcQAQDcEAEA5hADAPgQAAD/EAIACBEDAA8RAAAREQMAFhECAB0RAQAgEQAAIhEAACYRAAAoEQIAMBECADQRAgA4EQYARREAAFERAQBVEQcAXxEBAGIRAQBsEQAAcREAAHQRAQB6EQAAfREDAIQRAACMEQAAjhEAAJARAACVEQAAlxEDAJwRAACeEQAAoBEAAKMRAAClEQAApxEAAKsRAACuEQAAsBEAALIRAAC2EQAAuBECAL4RAADAEQAAzREBANURAADYEQIA3BEAAOQRAgDoEQIA7hEAAPARAQD0EQAA9xEAAP8RhQCLEgQAkRIAAJcSAQCaEgEAnRIAAJ8SAAChEgAAoxIAAKUSAACqEgAAsBIAALYSBgDIEgAAyxIBAM4SAgDTEgEA1xIFAOASAgDqEgAA7xIAAPsSAAAJEwAACxMAAA8TAwAaEwMAIRMBADcTAAA6EwIAQRMCAEUTAQBJEwAATxMEAF4TAQBrEwEAbxMEAHsTBACDEwAAhRMAAI4TAgCSEwIAlxMBAJoTAACdEwMApRMBAK8TAACyEwAAuBMGAM4TAADREwEA2BMCANwTAADfEwAA4hMAAPcTAQAIFAEACxQBAA8UAAARFAAAFxQDAB0UAQAlFAQALRQFADcUAQBBFAAARBQAAE0UAABSFAMAXxQAAG0UBQB0FAAAehQAAHwUAQCCFAEAjBQAAJAUAAA=","H":"D2cXtcQv"},"function":{"N":"AABe","E":"OzAAAAEAAHwDVwAAAAoAbgAbAKYAAQCNAQEAmAEAAL0BAADNASMA9QEBACoEAAAsBAAAQAQAAEQEAABJBAAAVQQAAGAEAQA/BSoAawUBAAYGAAAJBgEAFAYAAB4GAAAiBgMAKwYAAC4GAwA1BiwASwcAAE4HAABRBwIAVQcCAFsHAACUBwAApQcGAK4HAACwBwAAygcAANUHMAB6CAAAgQgBAIoIAAB6CTUAtQkBANAJAADmCQEAXgwvAJgMAAChDAAAvAwAAMsMAAAZDQAAHg0DACMNNQDEDgMAyQ4BAN4PAADgDxcA+g8HAAYQAABIEAAATBABAGIQIQCFEBsA0xABAPAQKwAgEQIAJhEAAFkRMADaESUAoBIpAOISAQD5EhsAIRMAACMTEwA4ExYAaxMAAG8TCgCXEwIAoRMSANoTAADdEwAA4BMKAPwTDwAdFAUANRQDAEIUCwBZFAAAYxQFAIEUBAA=","H":"wjSYD1aw"},"type":{"N":"AJj","E":"OzAAAAEAAPcSmwACAAQACQAAAAsAYgBvAAEAdQAAAHsABQCEAAAAhgABAIkAHACoAOQAjgEJAJkBMwDRAQEA1AEIAOEBAQDlAQIA6QEAAO4BBgD2AWkCYgTcAEMFAABHBQIATAUAAE4FAABWBQAAWQUCAF0FAwBiBQAAZAUAAGoFAABsBbEAHwYQADIGAgA2BgUAPgYFAEsGAQBXBgYAYgZ1AdoHBADhBwAA5gcBAOoHAADsBwMA+AcAAPoHAgD+BwQABQh7AIII9wB8CQAAfgkBAIEJAgCJCQAAjAkBAI8JBQCZCQAAnQkAAKAJAgCmCQEArwmuAl8MAABhDAIAZQwBAGkMAABrDAAAcAwAAHYMAAB6DAMAgAwAAIIMAACHDAAAigwAAI0MPQDMDFEAIA0CACQNAQAnDQAALg0BADINAAA7DQIATg0AAFkNhALfDwAA6g8BAO8PBAD2DwMA+w8CAAIQYgBrEAEAbhACAHkQAQB+EAgAixAEAJIQAAChEFAA9BAEAPwQAAD/EAEAAhEBAAURBwAREQEAFhEBABwRQQBiEQAAZBEFAGwRAgByEQUAexEFAIURAQCKEVAA3BEAAOARAADlEQEA6BECAO8RAAD4EQEA+xGkAKESAACjEgAAphIBAKkSAACsEgMAsRIKAL0SAQDFEgIAyRIxAPwSAgAFEwEACRMAAAsTAAAPExUAJxMDACwTBwA2EwMAOxMFAEITAABFEwEAShMAAEwTAQBPEx8AcBMEAHcTLQCnEwQArhMAALQTKwDjEwUA6xMaAAcUAgAMFBAAHxQXADkUCgBHFAUAThQUAGQUAwBpFBcAhBQdAA==","H":"9880COmq"},"alias":{"N":"`","E":"OzAAAAEAAKEUAQAAAKEU","H":"p2IVDFMs"},"generic_inverted_index":{"N":"b","E":"OjAAAAAAAAA=","H":"tRii7/4A"}}') \ No newline at end of file diff --git a/pr/509/docs/search.index/type/f7cf3408e9aa.js b/pr/509/docs/search.index/type/f7cf3408e9aa.js new file mode 100644 index 000000000..fe6106b85 --- /dev/null +++ b/pr/509/docs/search.index/type/f7cf3408e9aa.js @@ -0,0 +1 @@ +rd_("CBg[\"\",\"ADswAAABAAAFAAIAIAQEAHQUAAACgQcAAKAHAAA6MAAAAQAAAAAACgAQAAAAywHMARgGHA2REtUS6hJTFF8UfRSMFDowAAABAAAAAAAaABAAAAD7AfwB/QH+Af8BAAIBAi8EFwYcBk8HfQesB+gJMhGUEtYSZxMSFBMUVhR5FHoUexR8FI8UkxQ7MAAAAQAAIgERAC0EAAAEBgAAFgYAAIIHAACfBwAArwcAANMMAwA+DoQAvRAAADMRBwCREQAAABKEAJUSAACXEgAAHxMAAGgTAACGEwAAOjAAAAEAAAAAAB0AEAAAABkGHwYgBiEGewd8B4MHhAeFB4YHhweIB1oMWwxcDF0MrAzVD9kP5BBKEZASmhPWE9cT7RNOFFIUYBR+FDswAAABAAA9ABQAKQMUAEsEAABXBAIAAgYBAAUGAAAaBgEANAYAAG4HDAB7CAMAWQwAAMIMAAAtEAAAYRAAAOUQAACKEQAAIBMAAGMTAwDOEwAATxQAAH8UAAA7MAAAAQAAVwANAMsAPQB8AQEASgQAACkIAAAsCAAAawgAAG0IAACwEAMAyhIFAL4TAADuEwAALRQFAEAUAAA7MAAAAQAAFQAHADEEAAAdDQAAww4AACkQAwCSEgAAjRMHAG0UBQA7MAAAAQAAEwAJAEcEAQBQBAEAjAcHAGoIAABsCAAAzw8CANgPAABFEQAA+BIAAAPLBwAAzAcAAOcSAAAByAwAAAUmCAAAKAgAACsIAADFDAAARBEAAAA7MAAAAQAABQACALEBAAANFAQAAAABZQYAAA==\"]Bo[\"AAAAAAAAAAACOxQAADwUAAAAAAAAAiMRAAAkEQAA\",\"\"]0Bg[\"\",\"AAAFygkAAMsJAADMCQAAzQkAAM4JAAA=\"]AGg[\"AAAAAAKiAQAAsgEAAAAAAgIGAAADBgAABCkIAAAsCAAAawgAAG0IAAA7MAAAAQAADQAFADIGAQDwCQEAzgwEAB0NAAC9EQMAOjAAAAEAAAAAAAsAEAAAAFAEUQRqCGwI2A/uEO8QrxGwEbERshH4EgRBBAAAywcAAMwHAABGEAAABCcEAAApBAAAGRQAABoUAAAFRgQAACgIAAArCAAA0g8AANQPAAADxAwAAMYMAADTDwAAOzAAAAEAAAYAAwCxAQAAaQcAAA0UBAABbBMAAAHHDAAA\",\"AAAAAdkRAAAAAgcGAAAIBgAAAAKlDAAApgwAAAAFzgwAAM8MAADQDAAA0QwAANIMAAAAAAInBAAAKQQAAA==\"]ANg[\"AAAEPwIAAKICAABYBwAAXAcAAAOnDAAAqAwAAKkMAAACogEAALIBAAA7MAAAAQAAhAABAAAShAAAOzAAAAEAAAcAAwACBgEAYxMDAJ8UAQAEKQgAACwIAABrCAAAbQgAADswAAABAAASAAcAMgYBAKEHAADwCQEAzgwEAB0NAAC9EQMAnBIDADowAAABAAAAAAAKABAAAABHBEgEUARRBGoIbAjYD68RsBGxEbIRBUEEAAAdBgAAywcAAMwHAABGEQAABCcEAAApBAAAGRQAABoUAAAEKAgAACsIAADSDwAA1A8AAAR+BwAAxAwAAMYMAADTDwAABQ0UAAAOFAAADxQAABAUAAARFAAAAWwTAAAAAWUGAAA=\",\"AAAAAAACBwYAAAgGAAAAAqUMAACmDAAAAUcQAAAFzgwAAM8MAADQDAAA0QwAANIMAAAAAAInBAAAKQQAAAAAAAAAA5cHAACYEQAAmREAAA==\"]Dc[\"\",\"AAAAADswAAABAAAHAAEA0wkHADswAAABAAAJAAIAPxEEAJMRBAAAAZIRAAA=\"]ADm[\"AAAABp8BAAChAQAArgEAALABAABdEAAAXhAAAASlAQAAtwEAAN0KAADiCgAAOjAAAAEAAAAAAAkAEAAAAKABpgGnAa8BuAG5AU8LVAsbFBwUOzAAAAEAAA0ABABtBQcAAhAAAOoQAwDtEwAAAqoBAAAgEwAAOzAAAAEAABUACgCVAQEAnAEAAMwMAQAWDQEAVBADANoQAQDwEgMAvhMAADsUAQCGFAEAAT0UAAAAAAAAAAAAAAFlBgAA\",\"AAABmgEAAAJ1BQAAhRIAAAAAAALJDwAA0hAAAAAAAAAAAAAAAAAEWgcAAF4HAAAaDQAAGw0AAA==\",1]Fk[\"AAAABKoFAADOBwAAcQgAAJwRAAAC1AkAAJoMAAABNBEAAAABfAgAAAH7AAAAAioQAACOEwAAAY0HAAA=\",\"AAAAAaoFAAABpQEAAA==\"]Fk[\"AAAABPQFAADSBwAAdQgAAKMRAAAC2AkAAJ4MAAABOBEAAAF+AQAAAX4IAAAAAiwQAACSEwAAAZEHAAA=\",\"AAAAAfQFAAABtwEAAA==\"]ADi[\"AAAAAp0BAACpAQAAOzAAAAEAAAcAAQDTCQcAAAQfBgAAIAYAACEGAACQEgAAAAABPRQAAAAAAAIjEQAAJBEAAA==\",\"AAAAADswAAABAAAQAAMATwcAANMJBwCZDAcAOzAAAAEAAAsABACvBwAAJREAAD8RBACTEQQAOzAAAAEAAA8ABQBtBQcAtwwCAJkSAgDREgAAUhQAADowAAABAAAAAAAKABAAAABQEVIRVBFWEVgRkhG1EbYRtxG4Ec4TBLAQAACxEAAAshAAALMQAAAAAAVPEQAAUREAAFMRAABVEQAAVxEAAAAAAAAAAAFlBgAA\",1]Ec[\"AAAAAAAAOzAAAAEAAAYAAgBlBwMAagcCADswAAABAAAGAAIAYAcEALwJAQADQwQAABIGAAATBgAA\",\"\"]Dg[\"AAAAAAAAAAABLhQAAAFuFAAAAAAAAAAAAAABDQgAAA==\",\"AAAAAAAB2BIAAAG5EwAA\"]Eo[\"AAAAAagFAAABpAEAAAIoAAAAjwAAAAE1EAAAAS4DAAAC9gAAAPcAAAA=\",\"AAABngAAAAGoBQAAAXwTAAAAAZEMAAA=\"]Ic[\"AAAAAeoFAAADtgEAAJYTAAAUFAAAAlUAAACcAAAABmsBAABsAQAAKAQAAGQGAABCEAAAYBQAAAE7AwAA\",\"AAABnwAAAALqBQAAfhMAAAABlQwAAAAAAAAAAAAAAAAAAAKgAAAAoQAAAA==\"]Ik[\"AAAAAn0FAADvEgAAAawHAAABTAcAAAGoAAAAAYoRAAABLRQAAAKSEgAAbRQAAAAAAAAAAAAAOjAAAAEAAAAAAAgAEAAAACYGSgcKCIsIDxAmEFgQJxHBEQ==\",\"AAAAAX0FAAAAAdcSAAABuBMAAA==\"]Ck[\"AAAAAcsBAAAAAAAAATEUAAABcRQAAA==\",\"AAAAAAAB2xIAAAG8EwAA\"]Ck[\"AAAAAcwBAAAAAAAAATIUAAABchQAAA==\",\"AAAAAAAB3BIAAAG9EwAA\"]Kc[\"AAAAAAAABBkGAAAfBgAAIAYAACEGAAAAOzAAAAEAAAkAAQBPDAkAOzAAAAEAAA0AAQAxBA0ABcMMAADPDwAA0A8AANEPAABFEQAAAAHIDAAAA8UMAADSDwAA1A8AAALEDAAA0w8AAA==\",\"AAAAAAABJREAAAO3DAAAuAwAALkMAAA=\"]Nk[\"AAAAAAAAAvYSAAD3EgAABGMTAABkEwAAZRMAAGYTAAACaRMAAGoTAAAFoQcAAJwSAACdEgAAnhIAAJ8SAAACRwQAAEgEAAACHQYAAEYRAAAAAAF+BwAAAAAAAWUGAAA=\",\"AAAAAAIVBgAAQRQAAAAFdxQAAJoUAACbFAAAnBQAAJ0UAAABzhMAAAJHEAAAeBQAAAAAAAAAAAAAAAS0EAAAtRAAALYQAAC3EAAA\"]Bg[\"AAAAAAAAAATWEQAA1xEAAPETAADyEwAA\",\"\"]Bk[\"\",\"AAAAAdURAAAAAAAAAAAAAAAAAAAAAAHcDwAA\"]Cc[\"AAAAAAAAAeQQAAAB5RAAAA==\",\"AAAAAAAC6gkAAOsJAAA=\"]FGk[\"AAAAADswAAABAAAVAAkA+wEGAC8EAAAqBgAAjQgAAKMMAAC4EAMA1hIAABIUAQB5FAMAOzAAAAEAAAQCEQAmBAAAPwQAAEUEAACCBwAAkwwDAFwNZgFJEAAAThAAAL0QAADZEAAAMxEHAJERAAAAEoQAlRIAAJcSAABoEwAAyRMAADswAAABAAC9AA8AqAAiAAkBcgB+AQ0AKAQAABkGAABkBgAAZQcDAGoHAgB7BwEAjwwDAM0PAACaEwAAuBMFAGAUAABzFAAAOzAAAAEAAKIAQgApAxQAQgQAAEwEAQBXBAIABQYAABoGAQA0BgAAYAcEAG4HDAB4CAEAewgDAHQJAQC8CQEA0QkBANwJAQClDAEAtQwBAMIMAABaDQEAyg8BANoPAQAQEAEAExABAC0QAABPEAEAWRABAGEQAAC+EAAAzBABAOIQAQDlEAAASxEBAJIRAACdEQAAnxEAAKERAACkEQAAphEAAKgRAACzEQUAwhEBAMURAgDWEQEAhhIBAOgSAQAgEwAAVBMBAFoTAQBjEwMAgBMBAL8TAQDPEwEA0xMBAPETAQD5EwEAIxQBADMUAQBPFAIAWxQDAGkUAwB/FAAAjRQBAJEUAQCUFAEAmBQBAJ8UAQA7MAAAAQAAjwAmAMsAPQB8AQEAlQEBACUEAABDBAAASgQAABIGAQAsBgEAKQgAACwIAABrCAAAbQgAAE8MCQDMDAEAFg0BANYPAQAKEAEARxAAAFQQAwDaEAEALREBAEcRAQCTEgAA3hIBAPASAwBpEwEAiRMBALQTAQDHEwEAyhMDAO4TAAAtFAUAOxQBAEAUAAB1FAEAeBQAAIYUAwCWFAEAOzAAAAEAADEADQAxBA0AMgYBAKEHAADwCQEAvwwAAB0NAADDDgAAKRADAL0RAwCcEgMAjRMHAMQTAQBtFAUAOjAAAAEAAAAAABgAEAAAAEcESARQBFEEjAeNB44HjweQB5EHkgeTB2oIbAjDDM8P0A/RD9gP7hDvEEUR+BJXFIAUB0EEAAAdBgAARhAAAEYRAAC7EQAAvBEAAOcSAAADJwQAACkEAADIDAAAOjAAAAEAAAAAAAkAEAAAAEYEJggoCCsIxQzSD9QPIxEkEUQRBH4HAADEDAAAxgwAANMPAAA7MAAAAQAABQACAGkHAAANFAQAAAHHDAAAAWUGAAA=\",\"AAAAAAA7MAAAAQAA4QABAFwN4QAAOjAAAAEAAAAAAFkAEAAAAEwETQR4CHkIdAl1CdEJ0gncCd0JpQymDLUMtgxaDVsNyg/LD9oP2w8QEBEQExAUEE8QUBBZEFoQzBDNEOIQ4xBLEUwRnRGfEaERpBGmEagRsxG0EcIRwxHWEdcRhhKHEugS6RJUE1UTWhNbE4ATgRO/E8ATzxPQE9MT1BPxE/IT+RP6EyMUJBQzFDQUUBRRFFsUXBRdFF4UaRRqFGsUbBSNFI4UkRSSFJQUlRSYFJkUnxSgFDswAAABAAAvABQAlQEBACwGAQDMDAEAFg0BAAoQAQBUEAMA2hABAC0RAQBHEQEA3hIBAPASAwBpEwEAiRMBALQTAQDHEwEAyhMDADsUAQB1FAEAhhQDAJYUAQAE8AkAAPEJAADEEwAAxRMAAAACuxEAALwRAAAAAiMRAAAkEQAA\"]Ho[\"AAAAAZoRAAAAAxYGAADBCQAAjxIAADswAAABAAAMAAMAWgQFAIMHBQAMFAAAAAMlCAAARxEAAEgRAAABqxAAAAAHTxEAAFERAABTEQAAVREAAFcRAAC7EQAAvBEAAAABJggAAA==\",\"\"]Lk[\"AAACOwIAAKQCAAAAAqUBAAC3AQAAOzAAAAEAAAsAAwCmAQEAuAEBADMRBwA7MAAAAQAABwABAG0FBwA6MAAAAQAAAAAABwAQAAAAeAh5CBAQERBZEFoQsxG0EQACxBMAAMUTAAA=\",\"AAAAAQgQAAAAAAAAAAAAAAAAAAAAAAUHEAAACRAAAA8QAAASEAAAxhMAAA==\"]AAg[\"AAAEPgIAAKECAADCCQAAwwkAAASbAQAAowEAAKgBAAC1AQAAAkMCAACoAgAAAAA6MAAAAQAAAAAAEwAQAAAATARNBHQJdQnAEMIQxBDGEMcQUBFSEVQRVhFYEegS6RKRFJIUlBSVFAJpEwAAahMAAAXODAAAzwwAANAMAADRDAAA0gwAAAAFTxEAAFERAABTEQAAVREAAFcRAAAAAAACsQEAAGkHAAA=\",\"AAAAAAAAAAAAAAAAAAAAAAAABHYJAAB3CQAAeAkAAHkJAAA=\"]BKo[\"AAADwAkAAFYTAABXEwAABZkBAADBCgAA1REAAFwTAABdEwAAOjAAAAEAAAAAAAcAEAAAAKQBtgHECcUJqAr8CiQLlBIBlRIAAAGaEwAAOjAAAAEAAAAAABEAEAAAAHsIfAh9CH4IyQ9PEFAQ0hCdEZ8RoRGkEaYRqBG/E8ATWxRcFAG+EwAAOzAAAAEAAAsAAgApEAMAjRMHADswAAABAAAJAAMAjAcHAGoIAABsCAAAAAACKAgAACsIAAA=\",\"AAAEDAYAAA0GAADBDwAAwg8AADswAAABAAAWAAsAzQcHAJsRAQCeEQAAoBEAAKIRAQClEQAApxEAAIgSAQDrEwEA7xMAADkUAQAClhMAABQUAAABjxIAAAAAAAAAAAAAAAAAADowAAABAAAAAABrABAAAACPAZABkgGTAfEB8gH4AfkBLgQwBFIEdwV6BVkHXQeKB4sHvwfAB7IJswnjCeQJogykDLoMuwzADMEMyQzKDMcPyA/dDwMQBBAkECUQWBCtEK4QyhDLENcQ2BDcEN0Q5hDnEB0RHhEnESkRKhEwETEROxE8ET0RPhG5EboRzRHOEYsSjBKNEo4S0xLUEuAS4RLsEu4SFRMWExcTGBMaExsTHBMdE1gTXhNfE2ATehOLE4wTlRObE5wTnROeE58ToBO2E7cT0RPSE9gT2RP3E/gTFxQYFFQUVRQ=\"]Ac[\"AAAAAAEaAwAA\",\"\"]Ca[\"AAAAAAJDAgAAqAIAADswAAABAAAFAAEAzxEFAA==\",\"\",1]Ek[\"AAAAAAAAAAAAAAAAAAAAAAAAAn8HAACVBwAA\",\"AAAAAAACvxAAAMEQAAAAAsAQAADCEAAAAAKcEgAAnRIAAA==\"]Eo[\"AAAAAAAAAAAAAAAAAAAAAAAAAoAHAACWBwAA\",\"AAAAAAACwxAAAMUQAAAAA8QQAADGEAAAxxAAAAACnhIAAJ8SAAA=\"]Ck[\"AAAAAAAAAAAAAAAAAAAAAAAABW0AAACTAQAAZggAAIUIAAB4CQAA\",\"\"]Bg[\"AAAAAAAAAdUPAAAAAAAAArsRAAC8EQAA\",\"\"]Gk[\"AAAABF4AAACEBQAAKQYAAFwQAAAB/QEAAAYNAAAALQQAAAQGAAC9BwAAyxEAABYUAAACtwAAANkPAAAAAtUTAAAsFAAA\",\"AAAAAYQFAAABcwkAAAErBAAA\"]Ho[\"\",\"AAAAAAAAAAA7MAAAAQAACQABAE8MCQA7MAAAAQAADQABADEEDQAFwwwAAM8PAADQDwAA0Q8AAEURAAAAAcgMAAAExQwAANIPAADUDwAARBEAAAPEDAAAxgwAANMPAAAAAAHHDAAA\"]Bg[\"AAAAAAAAAAAAAAAAAAIjEQAAJBEAAA==\",\"\"]Hk[\"AAADvgEAAK4MAACxDAAAAacFAAABpAEAAAQnAAAAjgAAAL8QAADBEAAAATQQAAABLQMAAAP1AAAAjREAAI4RAAA=\",\"AAACvgEAAKoMAAACTgQAAKcFAAABexMAAAABkAwAAA==\"]Hk[\"AAADvwEAAK8MAACyDAAAAekFAAABtgEAAARUAAAAmwAAAMMQAADFEAAAAmoBAABBEAAAAToDAAADQwQAAI8RAACQEQAA\",\"AAACvwEAAKsMAAADTwQAAOkFAAB9EwAAAAGUDAAA\"]2ALo[\"AAAAAAGvEAAAAAA6MAAAAQAAAAAAEQAQAAAAqgFCBLUMtgxaDVsNyg/LD0sRTBGGEocSaRRqFI0UjhSYFJkUAkoEAAC+EwAAOzAAAAEAAAsAAgApEAMAjRMHADswAAABAAAHAAEAjAcHAAAAAAACsQEAAGkHAAA=\",\"AAAHDgYAAA8GAACuDAAArwwAALEMAACyDAAAlhIAADowAAABAAAAAAAXABAAAABuCG8IzwnbCbAMswy0DMMPxA/OEM8Q0BAcEZoRqhGrEawRrRGuEcETKhQrFD4UPxQ7MAAAAQAA8AACAMsO7ACCEwMAOzAAAAEAAFYABAALAFIAWRMAAIcTAQDwEwAAAXMUAAAAAAS9EQAAvhEAAL8RAADAEQAABK8RAACwEQAAsREAALIRAAAB5xIAAAAAAAAAAAdWBAAAJgYAAIkHAADIDgAAHxEAAC8RAADBEQAA\"]Dk[\"AAAAAAKrCgAA+woAAAAAADswAAABAAAFAAEAyhIFAAACRwQAAEgEAAAAAicEAAApBAAA\",\"\"]BNo[\"\",\"ADswAAABAAAFAAIAIAQEAHQUAAACgQcAAKAHAAA6MAAAAQAAAAAACgAQAAAAywHMARgGHA2REtUS6hJTFF8UfRSMFDowAAABAAAAAAAaABAAAAD7AfwB/QH+Af8BAAIBAi8EFwYcBk8HfQesB+gJMhGUEtYSZxMSFBMUVhR5FHoUexR8FI8UkxQ7MAAAAQAAHgEQAC0EAAAEBgAAFgYAAIIHAACfBwAArwcAAD4OhAC9EAAAMxEHAJERAAAAEoQAlRIAAJcSAAAfEwAAaBMAAIYTAAA6MAAAAQAAAAAAGwAQAAAAGQYfBiAGIQZ7B3wHgweEB4UHhgeHB4gHWgxbDFwMXQzVD9kP5BBKEZASmhPWE9cT7RNOFGAUfhQ7MAAAAQAAOwASACkDFABLBAAAVwQCAAIGAQAFBgAAGgYBADQGAABuBwwAewgDAFkMAADCDAAALRAAAGEQAADlEAAAIBMAAGMTAwBPFAAAfxQAADswAAABAABSAAsAywA9AHwBAQBKBAAAKQgAACwIAABrCAAAbQgAAMoSBQDuEwAALRQFAEAUAAA7MAAAAQAAFAAGADEEAAAdDQAAww4AACkQAwCNEwcAbRQFADswAAABAAATAAkARwQBAFAEAQCMBwcAaggAAGwIAADPDwIA2A8AAEURAAD4EgAAAssHAADMBwAAAcgMAAAEKAgAACsIAADFDAAARBEAAAAFDRQAAA4UAAAPFAAAEBQAABEUAAA=\"]Bc[\"AAAAAAAAAAAAAAACuxEAALwRAAA=\",\"\"]Kc[\"AAAAAAFiEwAAADswAAABAAAGAAIAgwcFAEoRAAACBQYAAGEQAAABSgQAAAAD7hAAAO8QAAD4EgAAAUYQAAA=\",\"AAAAAAABRQQAADswAAABAAAFAAEAWgQFAAAAAAAAAAAAAAAAB/oBAAB2BQAAeQUAAJgHAACZBwAAmgcAAJsHAAA=\"]Do[\"AAAAAAABFwAAAAAAAc8AAAAAAAAAAAAAAAACdgUAAIoHAAA=\",\"AAAAAAAAAAAC1g8AANcPAAA=\"]Dk[\"AAAAAAABLgAAAAEPAQAAAAAAAAAAAAAAAAACeQUAAIsHAAA=\",\"AAAAAAAAAAABJQQAAA==\"]Ac[\"AAABTQIAAA==\",\"\"]Fg[\"AAAABKwFAADPBwAAcggAAJ4RAAAD1QkAAJsMAAB6FAAAATURAAAAAAH9AAAAAY8TAAABjgcAAA==\",\"AAAAAawFAAAAAaYBAAA=\"]Fg[\"AAAABPYFAADTBwAAdggAAKURAAAD2QkAAJ8MAAB8FAAAATkRAAABgAEAAAAAAZMTAAABkgcAAA==\",\"AAAAAfYFAAAAAbgBAAA=\"]Bk[\"\",\"AAAAAAAAAAA7MAAAAQAAPwACAMsAPQB8AQEA\"]Ck[\"\",\"AAAAAAFPBwAAOzAAAAEAAAkAAgCvBwAAsQcIAAFSFAAAAc4TAAA=\"]Bo[\"\",\"AAAAAAAAAAAAAAAAAhkUAAAaFAAAAAAAAWwTAAA=\"]Ck[\"AAACTgIAAF0CAAA=\",\"AAAAAAAAAtsTAADcEwAAAAACvREAAL4RAAA=\"]Eo[\"\",\"AAAAAAA7MAAAAQAABQABANcSBQA7MAAAAQAABQABALgTBQAAOzAAAAEAAAUAAQAtFAUAOzAAAAEAAAUAAQBtFAUA\"]Cc[\"\",\"AAAAAAJ7EwAAfBMAAAAEjwwAAJAMAACRDAAAkgwAAA==\"]Eg[\"AAAAAbMFAAAAASsAAAAAAAIHAQAACAEAAA==\",\"AAA7MAAAAQAACgABAH8CCgACswUAAGEUAAAAAAH2EgAA\"]Eo[\"AAAAAfMFAAAAAVwAAAAAAAJ8AQAAfQEAAA==\",\"AAA7MAAAAQAACgACAAUDAgAJAwcAAvMFAABiFAAAAAAB9xIAAA==\"]Dg[\"AAAB6QIAAAHvBQAAAAFaAAAAAnUBAAB2AQAA\",\"AAAB+QIAAAHvBQAAAAAAAAFDBAAA\"]Eo[\"AAAAAAAAAoUBAACGAQAA\",\"AAAAAAAAB04UAAB3FAAAfhQAAJoUAACbFAAAnBQAAJ0UAAACTxQAAH8UAAABeBQAAA==\"]Bk[\"AAAAAAAAATUBAAA=\",\"AAABwQIAAAAAAT8EAAA=\"]Bo[\"AAAAAfoFAAAAAAGHAQAA\",\"AAAAAfoFAAAB9RMAAA==\"]Bg[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAARgNAAA=\"]Cc[\"AAAAAAAAAAAAAAAAAAAAAAAAA2cIAACGCAAAeQkAAA==\",\"\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAlYEAABoCAAA\",\"\"]Cc[\"AAAAAAAAAAK8CQAAvQkAAAAAAAAAAAACsQEAAGkHAAA=\",\"\"]Fo[\"AAAAAAACFgYAAJcSAAAAAAAAAAAAAAAAAAAEjAgAAN0PAADoEAAA2BEAAA==\",\"AAEuCAAAAAAAAAAAAAAAAAAAAAAAAALpEAAAjBEAAA==\"]Bk[\"AAAAARgGAAAAAAHVDwAAAS0QAAA=\",\"AAFfCAAA\"]Bg[\"AAAAAAAABOoQAADrEAAA7BAAAO0QAAA=\",\"\"]Ag[\"AAAAAp0BAACpAQAA\",\"\"]Dg[\"AAAAAAAAAAAAAvAJAADxCQAA\",\"AAAAAAAAAAAABc4MAADPDAAA0AwAANEMAADSDAAA\"]AEg[\"AAAAAAKiAQAAsgEAAAAAAgIGAAADBgAABCkIAAAsCAAAawgAAG0IAAA7MAAAAQAACwAEADIGAQDODAQAHQ0AAL0RAwA6MAAAAQAAAAAACwAQAAAAUARRBGoIbAjYD+4Q7xCvEbARsRGyEfgSBEEEAADLBwAAzAcAAEYQAAAEJwQAACkEAAAZFAAAGhQAAAVGBAAAKAgAACsIAADSDwAA1A8AAAPEDAAAxgwAANMPAAA7MAAAAQAABgADALEBAABpBwAADRQEAAFsEwAAAccMAAA=\",\"AAAAAdkRAAAAAgcGAAAIBgAAAAKlDAAApgwAAAAAAAACJwQAACkEAAA=\"]He[\"\",\"AAAAAAAE0wwAANQMAADVDAAA1gwAAAKsDAAAUhQAAAKKEQAAzhMAAAWwEAAAsRAAALIQAACzEAAAvhMAAAGSEgAAAAHnEgAAAAEmCAAAAAGxAQAAAAABZQYAAA==\",1]AMk[\"AAACqwEAALQBAAAGmwEAAKMBAACoAQAAtQEAAFwTAABdEwAABBcGAADoCQAAqQoAAPkKAAAE0wwAANQMAADVDAAA1gwAAALWEwAA1xMAADowAAABAAAAAAAHABAAAAC8Cb0JzBDNEFQTVRNQFFEUOjAAAAEAAAAAAAkAEAAAACUEKQgsCGsIbQjWD9cPRxB1FHYUAAJqCAAAbAgAAA==\",\"AAAAOjAAAAEAAAAAABAAEAAAAJEBBggHCAgICQiOCI8I1wxKEEsQ4RBNEU4R5BLlEh4TWhQD5QkAAK8QAAAZEwAABtMMAADUDAAA1QwAANYMAADrEgAA7RIAAAAAAAIyBgAAMwYAAAACQQQAAEYQAAAAA0YEAADSDwAA1A8AAAPEDAAAxgwAANMPAAAAAALHDAAAIg0AADowAAABAAAAAAALABAAAACUAVMEVAR4BXsFSAdJB8EHCgiLCIwIJhA=\"]Bg[\"AAAAAAAAAAAAATwEAAA=\",\"AAAAAWIGAAA=\"]Bk[\"AAAAAAAAADswAAABAAANAAIANAYAAG4HDAA=\",\"\"]Cc[\"AAABtwkAAAABAQIAAAAAAAElBAAA\",\"AAAAAAAAAAFCBAAA\"]Bc[\"AAAAAAAAAAAAAAAAAAAAAWkHAAA=\",\"\"]Be[\"AAAAAAAAAx8GAAAgBgAAIQYAAA==\",\"\",1]Bg[\"AAAAAAAAADswAAABAAAUAAEAKQMUAA==\",\"\"]Ck[\"\",\"AAAAAATeCQAA3wkAAOAJAADhCQAAAAAAAAAAAAAAAAAAAYQQAAA=\"]Bg[\"AAAAAAAAAAAAATcEAAA=\",\"AAAAAX8IAAA=\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAWMAAAA=\",\"\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAaAAAAA=\",\"\"]BOa[\"AAAAAAABHxMAAAHtEwAAAqoBAAAgEwAAOjAAAAEAAAAAABsAEAAAAJwBLAYtBgoQCxAtES4R3hLfEokTihO0E7UTxxPIE8oTyxPME80T1RPuEywUdRR2FIgUiRSWFJcUOzAAAAEAAAYAAwDwCQEAKRADAD0UAAACVxQAAIAUAAAAAAABfgcAAAAAAAFlBgAA\",\"AAAFygkAAMsJAADMCQAAzQkAAM4JAAAEpwwAAKgMAACpDAAAhRIAADowAAABAAAAAAAVABAAAABqBScIKgiACHIJcwneCd8J4AnhCeUJphCnEKgQqRCqEBkTIhOCE4MThBOFEzswAAABAAAhAA8AxwEAAMkBAAAmBAAAKwQAAD8EAABFBAAAsQcIANMMAwDMDwAA2RAAAD8RBACTEQQAYRMAAMkTAAAVFAAAOzAAAAEAACMACQAoBAAAWgQFAGQGAACJCAAAzQ8AADAQEwDREgEA2xMBAN4TAQA7MAAAAQAACAAEAL4QAAC1EQMAxRECAPYTAAA7MAAAAQAADAAGACUEAADWDwEAsBADAI0RAwDVEwAALBQAAAScEgAAnRIAAJ4SAACfEgAAA8MMAABXFAAAgBQAAANBBAAARhAAAEYRAAAAAAF+BwAAAbEBAAAAADowAAABAAAAAAAdABAAAACiAKMApAClAF8HbQe6B7sHvAewCeIJ7AntCe4J7wnyCfMJFRAuEC8QURBSEFMQ3hDfEOAQxBGKEvsTnhQ=\",1]Bg[\"AAAAAAAAAAAAAAAAAAIjEQAAJBEAAA==\",\"\"]Jo[\"AAAAAY4FAAAAOjAAAAEAAAAAAAcAEAAAABgAiwDHASsEzA/QEdMRhxMCMRAAANESAAACKgMAAG8HAAAC0AAAANEAAAAAAWoIAAAAAAEoCAAA\",\"AAEzCAAAAANeAAAAXwAAAI4FAAACQwIAAIAIAAAC0BEAANMRAAAAAWEHAAA=\"]A@c[\"AAAAAbcFAAAABzAAAACTAAAAyQEAANIRAADUEQAAYRMAAIgTAAAHEgEAABMBAADNDwAAORAAAEUQAADREAAA0hIAAAQyAwAAdAcAAMIMAAD2EwAAAZMSAAABww4AAANsCAAAVxQAAIAUAAAAAAErCAAA\",\"AAFICAAAAAG3BQAAAqgCAAAVBgAAAtIRAADUEQAAAWYHAAAAAAG/DAAAAAHnEgAAAAAAAAAABWMAAABkAAAAZQAAAGYAAABnAAAA\"]Bo[\"\",\"AAAAAAAAOzAAAAEAAKMAAwCoACIACQFyAH4BDQA=\"]CDi[\"\",\"AAA7MAAAAQAA4QABAG0L4QA7MAAAAQAA6AAOAHkKLgCqCgAArAowAN4KAwDjChUA+goAAP0KHgAdCwYAJQspAFELAABTCwAAVQsXANASAABhFAEAOjAAAAEAAAAAABIAEAAAABwGYwZPB8QJxQmoCqkKqwrdCuIK+Qr7CvwKHAskC1ALUgt7E3wTOjAAAAEAAAAAABIAEAAAAC0EBAavB70H6gnrCU8LVAvLEdcS2BLZEtoS2xLcEoYTFhQbFBwUOjAAAAEAAAAAACkAEAAAAHsHfAeJCFoMWwxcDF0MjwyQDJEMkgysDNkPAhBFENEQ5BDqEOsQ7BDtEEoR9hL3ErgTuRO6E7sTvBO9E9sT3BPeE98TThRSFHcUfhSaFJsUnBSdFDowAAABAAAAAAAaABAAAABCBFcEWARZBBoGGwZgB2EHYgdjB2QHwgxhEMAQwhDEEMYQxxDlEJIRxRHGEccRzhP2E08UfxQ7MAAAAQAAWQAPAMsAPQB8AQEAJQQAAEMEAABKBAAAEgYBACUIAADWDwEARxAAAJMSAADKEgUA1RMAACwUBgBAFAAAeBQAADswAAABAAARAAgAMgYBAL8MAAAdDQAAww4AAKsQAAC9EQMAxBMBAG0UBQA6MAAAAQAAAAAABwAQAAAARwRIBNgP7hDvEPgSVxSAFDowAAABAAAAAAAKABAAAABBBB0GywfMB0YQTxFREVMRVRFXEecSBCcEAAApBAAAGRQAABoUAAAEJggAACgIAAArCAAARBEAAALGDAAA0w8AADswAAABAAAGAAMAsQEAAGkHAAANFAQAAWwTAAAAAWUGAAA=\",1]Le[\"\",\"AAAAAroBAABNBwAAA60HAADFDwAAxg8AAAOfBwAA6xIAAO0SAAA6MAAAAQAAAAAABwAQAAAA1Q+QEpkSmhKbEtYT1xMMFAJLBAAAWQwAAAGcAQAAAT0UAAAEzw8AANAPAADRDwAARREAAAAByAwAAARGBAAAxQwAANIPAADUDwAAAcQMAAAAAAHHDAAA\",1]Fo[\"\",\"AAAAOjAAAAEAAAAAAAcAEAAAAMsBzAGREuoSUxRfFH0UjBQCrAcAAK8QAAAETAcAAMEJAAAFEAAAjxIAAAGaEwAAAYoRAAABvhMAAA==\"]Bo[\"\",\"AAAAAn0TAAB+EwAAAASTDAAAlAwAAJUMAACWDAAA\"]Bk[\"\",\"AAAAAAAAOzAAAAEAAAYAAgBlBwMAagcCAA==\"]Fc[\"AAAAAbMBAAAAAAAABCwGAAAtBgAAChAAAAsQAAA=\",\"AAA7MAAAAQAAPQABANgMPQAAAAAAAAAAAAAAAAAAAAABnAcAAA==\"]Ao[\"\",\"AAA7MAAAAQAA4QABAJAI4QA=\"]A@o[\"AAAEVAIAAGQCAABrAgAAwQ8AAAGMBQAAA2cTAACPFAAAkxQAAAMVAAAAigAAAM8RAAABMBAAADowAAABAAAAAAAMABAAAAApA1cEWAQCBgMGGgZuB8IRzxP5EyMUXRRrFALLAAAAzAAAAA==\",\"AAEyCAAAOzAAAAEAAAYAAwA7AgQAwgkAAFYTAAABjAUAAATECQAAqAoAAKkKAACrCgAAAc8RAAABjwwAAANgBwAAxREAAMYRAAAAAAAAAAAAAAAAAbkPAAA=\"]ABc[\"AAAExwIAAOwCAADyAgAAwg8AAAG0BQAAAAQsAAAAkQAAANERAABoEwAABAsBAAAMAQAANxAAAGAUAAA6MAAAAQAAAAAACgAQAAAAMANZBAUGGwZzB8MR0BP6EyQUXhRsFAABww4AAA==\",\"AAFHCAAAB58CAACgAgAAoQIAAKICAACkAgAAwwkAAFcTAAABtAUAAATFCQAA+QoAAPsKAAD8CgAAApMMAADREQAAAmUHAACJCAAAAccRAAAAAAAAAAAAAAAABLsPAAC8DwAAvQ8AAL4PAAA=\"]Gc[\"AAA7MAAAAQAACwAEAKEQBAB/EwAAJRQEAJAUAAA=\",\"ADswAAABAAAJAAMACwgBAA4IAgBPEwQAOzAAAAEAAAsABAChEAQAfxMAACUUBACQFAAA\"]Ho[\"AAAAAAAAAAA7MAAAAQAACQABAE8MCQA7MAAAAQAADQABADEEDQAFwwwAAM8PAADQDwAA0Q8AAEURAAAAAcgMAAAExQwAANIPAADUDwAARBEAAAPEDAAAxgwAANMPAAAAAAHHDAAA\",\"\"]Cc[\"AAAAAp8BAABNEQAAAAAAAAHMDAAAAAFQBAAAAcsHAAA=\",\"\"]Cc[\"AAAAAq4BAABOEQAAAAAAAAHNDAAAAAFRBAAAAcwHAAA=\",\"\"]ABc[\"AAAAAm0TAABuEwAAAAWmAQAApwEAALgBAAC5AQAAkREAADswAAABAAALAAMAbQUHABkGAAC3DAIAOjAAAAEAAAAAAAcAEAAAAAUGewh8CH0IfgjaD9sPLRAHjREAAI4RAACPEQAAkBEAAO4TAABAFAAAeBQAADswAAABAAANAAQAvwwAAMMOAAApEAMAjRMHADswAAABAAAHAAEAjAcHAAJGEQAA5xIAAAABJggAAAF+BwAAAAAAAWUGAAA=\",\"AAAAA8EBAADCAQAAwwEAAAAB2RAAAA==\"]Ek[\"AAAAAAAAAAAAAAAAAAAAAAACEAYAAKIHAAA=\",\"AAAAAAAAAAK1EQAAthEAAAAAAAAAAAAAAAACfwcAAJUHAAA=\"]Ek[\"AAAAAAAAAAAAAAAAAAAAAAACEQYAAKQHAAA=\",\"AAAAAAAAAAK3EQAAuBEAAAAAAAAAAAAAAAACgAcAAJYHAAA=\"]Ak[\"\",\"AAAAAAInEAAAKBAAAA==\"]Eo[\"AAAABK4FAADQBwAAcwgAAKARAAAC1gkAAJwMAAABNhEAAAAAAf8AAAABkBMAAAGPBwAA\",\"AAAAAa4FAAAAAacBAAA=\"]Eo[\"AAAABPgFAADUBwAAdwgAAKcRAAAC2gkAAKAMAAABOhEAAAGCAQAAAAABlBMAAAGTBwAA\",\"AAAAAfgFAAAAAbkBAAA=\"]Bg[\"AAAAAAAAAAAAATYEAAA=\",\"AAAAAekJAAA=\"]Ag[\"AAACngEAAK0BAAA=\",\"\"]Bg[\"AAAAAAAAAAAAAAAAAALFDAAARBEAAA==\",\"\"]Ag[\"\",\"AAACyBAAAMkQAAA=\"]Bc[\"AAAAAAAAAAAAAZISAAA=\",\"AAFsAAAA\"]Dg[\"AAAAAsEBAADCAQAAAAADHwYAALcMAACZEgAAAAGcAQAAAT0UAAA=\",\"AAAAAYUSAAA=\"]Hg[\"AAAAAaoRAAABghMAAAO/EAAAQBEAAJQRAAAB2xMAAAG1EQAAAbAQAAACvREAAJwSAAABrxEAAAAAAAABDhQAAA==\",\"AAAAAAAAAAJQEQAAUhEAAAAAAAJPEQAAUREAAA==\"]Ig[\"AAAAAa0RAAABhBMAAAPDEAAAQhEAAJYRAAAB3hMAAAG3EQAAAbIQAAACvxEAAJ4SAAABsREAAAAAAAABEBQAAA==\",\"AAAAAAGLEQAAAAADVBEAAFYRAABYEQAAAAAAA1MRAABVEQAAVxEAAA==\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAmsAAAC+DwAA\",\"\"]Bk[\"AAAAAAAAAcoAAAAAAAE+BAAA\",\"AAAAAYgIAAA=\"]Cc[\"AAAAAeEQAAABlhMAAAACKAQAAGQGAAA=\",\"AAAAAAGNCAAA\"]Ek[\"AAAAAZMFAAAAARsAAAAAAAHWAAAAAAAAAAAAAAAAAngFAABIBwAA\",\"AAE4CAAAAAGTBQAAAAAAAAAAAe4QAAA=\"]Ek[\"AAAAAcQFAAAAAToAAAABJAEAAAAAAAAAAAAAAAAAAnsFAABJBwAA\",\"AAFPCAAAAAHEBQAAAAAAAAAAAe8QAAA=\"]Be[\"AAAAAAA7MAAAAQAAhAABAAAShAA=\",\"\",1]Bg[\"AAAAAAAAAAAAATkEAAA=\",\"AAAAAY4MAAA=\"]Ec[\"AAAAAAAAAAAAAAAAAAIoCAAAKwgAAAF+BwAAArEBAABpBwAA\",\"AAAAAAAAAAAAAAAAAAAAAbEBAAA=\"]Dk[\"AAAAAAAAAAABLxQAAAFvFAAA\",\"AAAAAAAB2RIAAAG6EwAAAAAAAAAAAAAAAAABDQgAAA==\"]Dg[\"AAAAAdwFAAAAAAJPAQAAUAEAAAF3BwAAAAAAAAAAAAAAAAH6AQAA\",\"AAAAAdwFAAA=\"]ABc[\"AAAAA5kBAAC6CgAA1REAAAN9BwAAHAsAADIRAAAAADowAAABAAAAAAANABAAAADRCdIJ3AndCRMQFBDiEOMQWhNbE4ATgRPTE9QTOzAAAAEAAA8ABwAtEQEAyhIFAIkTAQDVEwAALBQAAIgUAQCWFAEAAAJXFAAAgBQAAA==\",\"AAAAA4wBAACXAQAArBAAAASmEAAApxAAAKgQAACpEAAAAscBAADJAQAAAAAC1RMAACwUAAAAAlcUAACAFAAAAAAAAAAAAATIAQAAygEAALQJAADYEQAA\"]Bk[\"AAAAAAGsBwAA\",\"AAAAAAAAAAGKEQAAAAGSEgAA\"]Ho[\"AAAABYIFAAAoBgAAYgYAABwNAADkEgAAAsYPAABWFAAAAp8HAAAfEwAAA7EAAACQEgAA7RMAAAEDBgAAASUIAAABqxAAAA==\",\"AAAAAoIFAABNBwAAAa0HAAAAAAABnAEAAAE9FAAA\"]Gc[\"AAAAAuUSAADvEwAAAVQHAAABggcAAAEKAQAAAr4QAAAgEwAAAAGhBwAAAAEdBgAAAAEmCAAAAX4HAAAAAAABZQYAAA==\",\"AAAAAAAAAAGqAQAA\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAWoAAAA=\",\"\"]Fg[\"AAADgQcAAKAHAAAhEAAABLMMAADVEAAAyhEAAD4UAAAF/AEAABwGAABjBgAAJwgAAOAJAAAAArMAAAACEAAA\",\"AAABKxEAAA==\"]Fg[\"AAAABLQMAADWEAAAzBEAAD8UAAACKggAAOEJAAABggcAAAFSAQAAAAABoQcAAAABHQYAAAAAAAAAAAFlBgAA\",\"AAABLBEAAA==\"]Dg[\"AAAAAeEFAAAAAU4AAAABWwEAAA==\",\"AAAAAeEFAAAAAAAAAAAAAAAAAAAAAAFtAAAA\"]Bk[\"AAAAAAH7AQAAAAAAAAEzBAAA\",\"AAAAAb0MAAA=\"]Cc[\"AAAAAAGACAAAAAAAAAE0BAAA\",\"AAAAAAACvgwAAOYSAAA=\"]Dk[\"\",\"AAAAADswAAABAAATAAEAEQgTADswAAABAAATAAEAigATADswAAABAAATAAEAMBATAA==\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAWgAAAA=\",\"\"]Ak[\"\",\"AAAAAAJEEAAA9RMAAA==\"]Ag[\"\",\"AAAAAAABFRQAAA==\"]Fg[\"AAAAAfsFAAAAAAGJAQAAAAAAAAAAAUYEAAAAAAABxwwAAAakAAAAlAEAAPQBAABtBwAAwQcAAIoSAAA=\",\"AAFeCAAAAAH7BQAA\"]Hk[\"AAAEngAAAD0CAAAMBgAAqgwAAAGpBQAAAAIpAAAAkAAAAAE2EAAAAi8DAAByBwAAAvgAAAD5AAAA\",\"AAABawIAAARgAAAAYQAAAGIAAACpBQAAAaQBAAAAAZIMAAABZAcAAA==\"]Kc[\"AAAEnwAAAKACAAANBgAAqwwAAAHrBQAAAARWAAAAnQAAAEkQAABOEAAAA20BAABuAQAAQxAAAAI8AwAAeQcAAA==\",\"AAAB8gIAAAHrBQAAAbYBAAABlgwAAAMoBAAAZAYAAGsHAAAAAAAAAAAAAAAAAARoAAAAaQAAAGoAAABrAAAA\"]Ak[\"AAAAAAJfEAAAYBAAAA==\",\"\"]Ao[\"AAAAAAAAAAAClQEAAMoSAAA=\",\"\"]Ao[\"AAAAAAAAAAAClgEAAM0SAAA=\",\"\"]Dc[\"AAAAAAAAAAAB+gAAAAAAAAAAAAAAAAWiAAAAUwQAALoHAAAMEAAA3hAAAA==\",\"\"]Fc[\"AAAAAAAAAW8BAAAAAAAAAAAAAAAAAAWjAAAAVAQAALsHAAANEAAA3xAAAA==\",\"AAAAAAAAAAAAAaEHAAAAAh0GAABGEQAA\"]Fc[\"AAAAAfwFAAAAAAGKAQAAAksEAABZDAAAAAAAAAHIDAAAAAAAAAAFpQAAALwHAACxCQAADhAAAOAQAAA=\",\"AAAAAfwFAAA=\"]Go[\"AAAAAY8FAAAAARkAAAAAAAHSAAAAAAAAAAAAAAAAA48BAAB3BQAAogwAAA==\",\"AAE0CAAAAAGPBQAAAAAAAAAAAAAAAAAAAAAEwgcAAMMHAADGBwAAxwcAAA==\"]Go[\"AAAAAb4FAAAAATYAAAABHAEAAAAAAAAAAAAAAAAAA5ABAAB6BQAApAwAAA==\",\"AAFLCAAAAAG+BQAAAAAAAAAAAAAAAAAAAAAExAcAAMUHAADIBwAAyQcAAA==\"]Bc[\"AAAAAAAAAAAAAAAAAAAAAAFsEwAA\",\"\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAWkAAAA=\",\"\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAmQAAACeBwAA\",\"\"]Bg[\"AAAAAAAAAAAAATgEAAA=\",\"AAAAAVkNAAA=\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAaEAAAA=\",\"\"]Dk[\"AAAAAasFAAABpQEAAAABbgUAAAGdEQAAAfwAAAA=\",\"AAFBCAAAAAGrBQAAAAAAAZ0RAAA=\"]Dk[\"AAAAAfUFAAABtwEAAAACfwEAAHIFAAABpBEAAA==\",\"AAFaCAAAAAH1BQAAAAAAAaQRAAA=\"]Hc[\"AAAAAAABBRAAAAEMFAAAAAEwFAAAAXAUAAAAAAAAAAAAADowAAABAAAAAAANABAAAACJB7AJ7gnIDhIQFRBREB8RLxHEEewS7hLGE/sT\",\"AAAAAAAB2hIAAAG7EwAA\"]Ak[\"AAAAAAISFAAAExQAAA==\",\"\"]Cg[\"AAAAAR4TAAAB5QkAAAFFBAAAAAABSgQAAA==\",\"AAAAAAGjDAAA\"]Ag[\"\",\"AAAAAAAAAWwHAAA=\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAmUAAADODwAA\",\"\"]Ag[\"AAAAAAAAAAF6BwAA\",\"\"]Dk[\"AAAAAAAAOzAAAAEAAKMAAwCoACIACQFyAH4BDQAAOzAAAAEAAD8AAgDLAD0AfAEBAA==\",\"\"]Go[\"AAAAAewFAAAABFcAAACHEwAAiBMAAPATAAADcAEAAHEBAABzFAAAAT0DAAAAAb8MAAAAAecSAAAAAAF+BwAAAAAAAqMHAADiCQAA\",\"AAAAAewFAAAAAAHREAAA\"]Dk[\"AAAAAZ8FAAAAASAAAAAAAXAHAAAC5gAAAOcAAAA=\",\"AAAAAp8FAADBCgAAAAAAAWIHAAA=\"]Dk[\"AAAAAdYFAAAAAUUAAAACQQEAAEIBAAABdQcAAA==\",\"AAAAAdYFAAABJAsAAAABZwcAAA==\"]Ck[\"AAAAAUoQAAAAAAAAAikIAABrCAAA\",\"AAAAAAAAAAACsBAAALEQAAA=\"]Ck[\"AAAAAUsQAAAAAAAAAiwIAABtCAAA\",\"AAAAAAAAAAACshAAALMQAAA=\"]Bk[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAAigRAACYEgAA\"]Ek[\"AAAAA44IAACIEgAA6xMAAAAAAAAAAAFqCAAAAAABKAgAAA==\",\"AAAAAAAAAAK1EQAAthEAAAKwEAAAsRAAAA==\"]Ek[\"AAAAA48IAACJEgAA7BMAAAAAAAAAAAFsCAAAAAABKwgAAA==\",\"AAAAAAAAAAK3EQAAuBEAAAKyEAAAsxAAAA==\"]Dg[\"AAAAAcoFAAAAAUEAAAACLwEAADABAAA=\",\"AAAAAcoFAAAAAAAAAAG/DAAAAAHnEgAA\"]Ac[\"\",\"AAAAAAFEEAAA\"]Cc[\"AAABwQIAAAG6BQAAAAEyAAAAARYBAAA=\",\"AAAAAboFAAA=\"]Hc[\"AAAAAZEFAAAAARoAAAAAAAHUAAAAAAAAAAAAAAAAOjAAAAEAAAAAAAoAEAAAAMgB+AHCB8YHxw8HEMoQFROLE5sTthM=\",\"AAE2CAAAAAKRBQAAKQYAAAABzA8AAA==\"]Ic[\"AAAAAcAFAAAAATcAAAABHgEAAAAAAAAAAAAAAAAAOjAAAAEAAAAAAAoAEAAAAMoB+QHEB8gHyA8JEMsQFhOME5wTtxM=\",\"AAFNCAAAAAHABQAAAAABzQ8AAAAAAAAAAAAAAAAAAc4PAAA=\"]Gk[\"AAACIwIAACsCAAABhwUAAAACEAAAALMHAAACvQAAAL4AAAA=\",\"AAACJgIAACcCAAABhwUAAAAAAAAC1g8AANcPAAAAAAJBBAAARhAAAAABRBEAAAHGDAAA\"]Do[\"AAAAAcIFAAAAAjgAAACUAAAAAyABAAAhAQAAOhAAAAEzAwAA\",\"AAAAAcIFAAAAAAAAASUEAAA=\"]Ck[\"AAAAAvMTAAD0EwAAAAAAAAAAAAAByAwAAA==\",\"AAAAAAABJREAAA==\"]Co[\"AAAAAAABnwcAAAOZEgAAmhIAAJsSAAAAAZwBAAA=\",\"AAAAAAABBRAAAA==\"]JOi[\"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\",1]Eo[\"AAAAAAA7MAAAAQAABQABANcSBQA7MAAAAQAABQABALgTBQAAOzAAAAEAAAUAAQAtFAUAOzAAAAEAAAUAAQBtFAUA\",\"\"]Ck[\"AAAAAAAAARkGAAAAAAExBAAA\",\"AAAAAAAAA7cMAAC4DAAAuQwAAA==\"]Cc[\"\",\"AAAAAAAELQQAAL0HAADLEQAAFhQAAAAAAtUTAAAsFAAA\"]Fc[\"AAAAAb0FAAAAATUAAAACGgEAABsBAAA=\",\"AAFKCAAAAAG9BQAAAAACRRAAANEQAAACwgwAAPYTAAAAAAJXFAAAgBQAAA==\"]Eo[\"AAAAAcgRAAAB3gkAAAACrAAAAKwMAAABkhEAAAABNQQAAA==\",\"AAAAAAFjBgAAAAKsDAAAAhAAAAABJQgAAAGrEAAA\"]Dk[\"AAAABpESAADqEgAAUxQAAF8UAAB9FAAAjBQAAAAAAAABvhMAAA==\",\"AAAAAAAAAAGKEQAA\"]A@g[\"AAAAOjAAAAEAAAAAAA8AEAAAAE4EfwUnBlAHfwjpCY4MvQzXDFkNWxBJEckR1RLBE1oUOjAAAAEAAAAAAAkAEAAAAN8JRBCqEJQSGRNiE2cT9ROPFJMUOjAAAAEAAAAAAAgAEAAAAMcBKwS+DMwPPxGTEeYShhMVFDowAAABAAAAAAAHABAAAACtAEoR0RJOFHcUfhSaFJwUAQIGAAAAAAAAAAAAAQ0UAAA=\",\"AAAAAX8FAAABcgkAAAABrAwAAA==\"]ACk[\"AAAAOjAAAAEAAAAAAAgAEAAAAE8EzwmwDMMPCBAcEawRKhQrFDowAAABAAAAAAAHABAAAAAvBBUGKgaNCKMMixHWEhQUOjAAAAEAAAAAAAoAEAAAAMkBPwRJEE4QvRDZEJERlRJhE2gTyRM6MAAAAQAAAAAABwAQAAAACQHND0UQ0RDSEmAUmxSdFAUFBgAAwgwAAPYTAABPFAAAfxQAAASTEgAA7hMAAEAUAAB4FAAAAr8MAADDDgAAAlcUAACAFAAAAkYRAADnEgAA\",\"AAAAAAAAAAAAAAAAAAEmCAAAAAAAAAFlBgAA\"]Ho[\"AAAAA80HAABwCAAAmxEAAAPTCQAAmQwAAHkUAAABMxEAAAABewgAAAACKRAAAI0TAAABjAcAAA==\",\"AAAAAASZDAAAmgwAAJsMAACcDAAAAAVtBQAAbgUAAG8FAABwBQAA0RIAAA==\"]Ho[\"AAAAA9EHAAB0CAAAohEAAAPXCQAAnQwAAHsUAAABNxEAAAABfQgAAAACKxAAAJETAAABkAcAAA==\",\"AAAAAASdDAAAngwAAJ8MAACgDAAAAAVxBQAAcgUAAHMFAAB0BQAA0hIAAA==\"]Mc[\"AAAAAZAFAAAAAAAAAdMAAAAAAAAAAAAAAAA6MAAAAQAAAAAAJAAQAAAA8QEuBFkHvweyCeMJugzADMkMAxAkEK0Q1xDcEOYQHREpETAROxE9EZgRuRHNEYsSjRLTEuASGhMcE14TnROfE9ET2BP3ExcUVBQ=\",\"AAE1CAAAAAGQBQAAAAAAAAAAAAAAAAAAAAACuAkAALoJAAA=\"]Mc[\"AAAAAb8FAAAAAAEdAQAAAAAAAAAAAAAAAAA6MAAAAQAAAAAAJAAQAAAA8gEwBF0HwAezCeQJuwzBDMoMBBAlEK4Q2BDdEOcQHhEqETERPBE+EZkRuhHOEYwSjhLUEuESGxMdE18TnhOgE9IT2RP4ExgUVRQ=\",\"AAFMCAAAAAG/BQAAAAAAAAAAAAAAAAAAAAACuQkAALsJAAA=\"]Cc[\"AAAAAaYFAAAAAAAAAfMAAAA=\",\"AAAAAaYFAAAAAAFaDAAA\"]Ck[\"AAAAAecFAAAAAAFnAQAA\",\"AAAAAecFAAAAAANbDAAAXAwAAF0MAAA=\"]Bk[\"AAAAAAAAAAAAAAAAAAFGBAAAAAAAAccMAAA=\",\"\"]Bg[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAAb8PAAA=\"]Eo[\"AAAAAdcFAAAAAUcAAAACRQEAAEYBAAABdgcAAA==\",\"AAAAAdcFAAAAAAFoBwAAAAAAAAAAAAAAAAACCwYAAMAPAAA=\"]Bk[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAAooUAACLFAAA\"]Ck[\"AAAAAAAAAAABQBQAAA==\",\"AAAAAAAAAAAAAAAAAAAAAAABoRQAAA==\"]Ag[\"AAACgQIAAA8DAAA=\",\"\"]Cc[\"AAAAAAACvgwAAOYSAAAB2Q8AAA==\",\"AAAAAAABvQcAAA==\"]Gc[\"AAAAAbkFAAAAATEAAAABFQEAAAABkxIAAAG/DAAAAAHnEgAA\",\"AAFJCAAAAAG5BQAAAAFhEwAAAUUQAAAAAZMSAAAAAAAAAAAAAAABvgcAAA==\"]Do[\"AAAAAAAAAAHaDwAAAiwGAAAKEAAA\",\"AAAAAZ0BAAAAAAAB2g8AAAQsBgAAChAAAI0RAACOEQAA\"]Do[\"AAAAAAAAAAHbDwAAAi0GAAALEAAA\",\"AAAAAakBAAAAAAAB2w8AAAQtBgAACxAAAI8RAACQEQAA\"]Ao[\"AAAAAAGvEAAAAAAB0hAAAA==\",\"\"]Gc[\"AAABqREAAAJgAAAAbggAAAH/AQAAAAG5AAAAAlcEAADFEQAAAtYPAABHEAAAAAHYDwAAAkEEAABGEAAAAALFDAAARBEAAALEDAAAxgwAAA==\",\"\"]Cc[\"AAAAAAAAAANCBAAAWQQAAMcRAAA=\",\"AAAAAAABSRAAAA==\"]Ec[\"AAABJwIAAAGIBQAAAAIRAAAAtAcAAAK/AAAAwAAAAA==\",\"AAABKQIAAAGIBQAAAAAAAsURAADGEQAA\"]Bo[\"AAABKxEAAAAAAAK0AAAAtQAAAA==\",\"AAAAAAEcBgAA\"]Ck[\"AAABLBEAAAAAAAJWAQAAVwEAAA==\",\"AAAAAAAAAAAAAAABHQYAAA==\"]Ao[\"AAAAAAAAAZoTAAAByQ8AAA==\",\"\"]Hg[\"AAAAAmIAAACICAAAAQACAAAB6gkAAAPFAAAAewcAAOQQAAACGgYAAGEQAAAAAjIGAAAdDQAABs8PAADQDwAA0Q8AAO4QAABFEQAA+BIAAAAAAtIPAADUDwAAAdMPAAA=\",\"\"]Dk[\"AAAAAAAB6wkAAAJ8BwAAiQgAAAIbBgAA5RAAAAABMwYAAAHvEAAA\",\"AAAAAAABThAAAA==\"]Bc[\"AAAAAAAAAAAAAAAAAAAAAAFsEwAA\",\"\"]AAg[\"AAAAAU0HAAABrQcAAARMBwAAvgwAAAUQAADmEgAAAk4UAAB+FAAAA4oRAABPFAAAfxQAAAUlCAAAjREAAI4RAACPEQAAkBEAAAGrEAAAAAAAASYIAAA=\",\"AAAABMgRAADJEQAAyhEAAMwRAAACxQ8AAMYPAAACyxEAABUUAAAFdxQAAJoUAACbFAAAnBQAAJ0UAAAB9hMAAAF4FAAAAZISAAAAAAAAAAAAAt8PAAD4DwAABvMBAAD0AQAAowcAAAwQAAANEAAADhAAAA==\"]Cc[\"AAAAAdgFAAAAAUgAAAACRwEAAEgBAAA=\",\"AAAAAdgFAAA=\"]Dc[\"AAAAAAAAAAA7MAAAAQAABQABAMoSBQA=\",\"AAAABMABAADEAQAAxQEAAMYBAAA=\"]Ao[\"AAAAAcABAAAAAAAAAVQQAAA=\",\"\"]Ao[\"AAAAAcUBAAAAAAAAAVUQAAA=\",\"\"]Eo[\"AAAAAasRAAABgxMAAAPBEAAAQREAAJURAAAB3BMAAAG2EQAAAbEQAAACvhEAAJ0SAAABsBEAAAAAAAABDxQAAA==\",\"\"]Eo[\"AAAAAa4RAAABhRMAAAPFEAAAQxEAAJcRAAAB3xMAAAG4EQAAAbMQAAACwBEAAJ8SAAABshEAAAAAAAABERQAAA==\",\"\"]Bc[\"AAAAAAAAAloEAACDBwAAAAFWEAAA\",\"\"]Bc[\"AAAAAAAAAl0EAACGBwAAAAFXEAAA\",\"\"]Ec[\"AAAAAboBAAAAAusSAADtEgAAAtYTAADXEwAAAksEAABZDAAAAAAAAAABRgQAAA==\",\"AAAAAAEaAwAA\"]Gg[\"AAAAAYoFAAAAAhMAAAC2BwAAAsYAAADHAAAA\",\"AAADMAIAADECAAAyAgAAAYoFAAAAAAABYRAAAAACMgYAAB0NAAAC7hAAAPgSAAAAAAAB0w8AAA==\"]Eg[\"AAAAAcUFAAAAAjsAAACWAAAAAyUBAAAmAQAAPBAAAAE1AwAA\",\"AAAAAcUFAAAAAAAAAAEzBgAAAe8QAAA=\"]Ec[\"AAAAAAAAAVUBAAAAAAAAAAAAAAAAAAJ8BQAA7wkAAA==\",\"AAAAAAAAAAAAAAAAAAAAAAAAAWUGAAA=\"]Ec[\"AAABlwwAAAF+BQAAAAELAAAAAakAAAA=\",\"AAAAAX4FAAAAAcEJAAAAAAAAAAAAAAAAAAABSgcAAA==\"]Dg[\"AAAAAAEXBgAAAAAAAAAAAAAAAAAAAALpEAAAjBEAAA==\",\"AAEvCAAAAAAAAUwHAAA=\"]Ac[\"AAABTAIAAA==\",\"\"]Do[\"AAAAAZ4FAAAAAR8AAAAAAALkAAAA5QAAAA==\",\"AAACWQIAAFoCAAABngUAAAAAAAAAAcQTAAA=\"]Dk[\"AAAAAdUFAAAAAUQAAAACPwEAAEABAAA=\",\"AAACzQIAAM8CAAAB1QUAAAAAAAAAAcUTAAA=\"]Bk[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAAsYJAADICQAA\"]Dk[\"AAAAAAAAAAABAwEAAA==\",\"AAABeAIAAAAAAAAAAAABRwQAAAHLBwAAAScEAAABKAgAAA==\"]Dg[\"AAAAAAAAAXgBAAA=\",\"AAAB/gIAAAAAAAAAAAABSAQAAAHMBwAAASkEAAABKwgAAA==\"]Dk[\"AAAAAa0FAAAAAaYBAAABbwUAAAGfEQAAAf4AAAA=\",\"AAFCCAAAAAGtBQAAAAAAAZ8RAAA=\"]Dk[\"AAAAAfcFAAAAAbgBAAACgQEAAHMFAAABphEAAA==\",\"AAFbCAAAAAH3BQAAAAAAAaYRAAA=\"]Ck[\"AAAAAcYFAAAAATwAAAACJwEAACgBAAA=\",\"AAAAAcYFAAAAAAF8BwAA\"]Eg[\"AAAAAZkFAAAAAh4AAACMAAAAATIQAAABKwMAAALfAAAA3hIAAA==\",\"AAE9CAAAAVQCAAACmQUAALoKAAA=\"]Eg[\"AAAAAdAFAAAAAkMAAACXAAAAAjoBAAA9EAAAATYDAAAB3xIAAA==\",\"AAFVCAAAAccCAAAB0AUAAAEcCwAA\"]Ao[\"AAAAAAAAAAAC2hAAAI0RAAA=\",\"\"]Ao[\"AAAAAAAAAAAC2xAAAI8RAAA=\",\"\"]Bg[\"AAAAAAAABMQAAAAhBgAAuQwAAJsSAAA=\",\"\"]Ak[\"AAAAAAABPQAAAAEpAQAA\",\"\"]Do[\"AAAAAAAAAAAAAAAAAAAAAAAAAigRAACYEgAA\",\"AAEtCAAAAAAAAAAAAAAAAAAAAAAAAAHoEAAA\"]Cg[\"AAAAAAN9BwAA6AkAADIRAAA=\",\"AAAAAAAAAALJDwAA0hAAAA==\"]Ag[\"AAACeAIAAP4CAAA=\",\"\"]Cc[\"AAAAAd8KAAAC3QoAAFALAAABTwsAAALqEAAA7BAAAA==\",\"\"]Ac[\"\",\"AAAAAdASAAA=\"]Gg[\"AAAAAZ0FAAABVhQAAAAAAXQJAAAE4wAAAFYQAADxEgAAOxQAAAGrEAAA\",\"AAAAA6EBAACdBQAAYRQAAAABGxQAAAABdAkAAANWEAAA8RIAADsUAAA=\"]Bc[\"AAAAAaEBAAAAAAAAAAAAAU8RAAA=\",\"\"]Gg[\"AAAAAdQFAAAAAAE+AQAAAXUJAAADVxAAAPMSAAA8FAAAAAAAAAEmCAAA\",\"AAAAA7ABAADUBQAAYhQAAAABHBQAAAABdQkAAANXEAAA8xIAADwUAAA=\"]Bc[\"AAAAAbABAAAAAAAAAAAAAVMRAAA=\",\"\"]Fg[\"AAABMQIAAAGUBQAAAAEcAAAAAAAC1wAAANgAAAA=\",\"AAE5CAAAAAGUBQAAAAHqCQAAAnsHAADkEAAAA1cEAABYBAAAGgYAAA==\"]Eo[\"AAAAAcgFAAAAAT8AAAACKwEAACwBAAA=\",\"AAFRCAAAAAHIBQAAAAHrCQAAAYkIAAAEWQQAABsGAADlEAAAxxEAAA==\"]Ao[\"\",\"AAAAAAAAAAAAAAABVxEAAA==\"]Ek[\"AAAAAaIFAAAAASMAAAAAAALsAAAA7QAAAA==\",\"AAABYQIAAAGiBQAAAAAAAAElCAAAAasQAAAAAk8RAABREQAA\"]Eg[\"AAAAAd4FAAAAAU0AAAACUwEAAFQBAAA=\",\"AAAB2wIAAAHeBQAAAAAAAAAAAAJTEQAAVREAAAABJggAAA==\"]Ao[\"AAAAAAAAAAHHEAAAAAHSDAAA\",\"\"]Jk[\"AAAAAaMFAAAABSQAAACNAAAABwYAAL8QAADBEAAAAloMAAAzEAAABCwDAAClDAAAwBAAAMIQAAAE7gAAAO8AAACNEQAAjhEAAALODAAAzwwAAAKvEQAAsBEAAA==\",\"AAAAAqMFAADVEAAAAqIBAAC4EAAAAAABvAkAAA==\"]Og[\"AAAAAeMFAAAABVAAAACaAAAACAYAAMMQAADFEAAABmABAABhAQAAWwwAAFwMAABdDAAAQBAAAAQ5AwAApgwAAMQQAADGEAAAAo8RAACQEQAAAtAMAADRDAAAArERAACyEQAA\",\"AAAABOMFAABiCAAAYwgAANYQAAAEsgEAALkQAAC6EAAAuxAAAAAAAb0JAAACEgYAABMGAAAAAAAAAAAAAAA7MAAAAQAACgACAGQIBQCDCAQA\"]Cc[\"AAAAAdkFAAAAAUkAAAACSQEAAEoBAAA=\",\"AAAAAdkFAAA=\"]Ck[\"AAABDgYAAAGkBQAAAAElAAAAAAAB8AAAAA==\",\"AAAAAqQFAACnDAAA\"]Dg[\"AAABDwYAAAHkBQAAAAFRAAAAAWIBAAAAAAAAAAAAAX4HAAA=\",\"AAAAAuQFAACoDAAA\"]Cc[\"AAAAAf0FAAAAAV0AAAABiwEAAA==\",\"AAAAAv0FAACpDAAA\"]Dg[\"AAAAAAAAAAAAAAABRhEAAA==\",\"AAAABrwQAADdEgAAwhMAAMMTAADzEwAA9BMAAA==\"]Ak[\"AAAAAAAAAAABzBIAAA==\",\"\"]Ak[\"AAAAAAAAAAABzxIAAA==\",\"\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAmcAAADzAQAA\",\"\"]Bg[\"AAAAAAAAAAAAAToEAAA=\",\"AAAAAUkRAAA=\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAmYAAAC+BwAA\",\"\"]Bk[\"AAAAAAAAAAAAAASMBwAAjQcAAI4HAACPBwAA\",\"\"]Ec[\"AAAABIwBAACXAQAAuAUAAKwQAAAAAAEUAQAAAAAABJAHAACRBwAAkgcAAJMHAAA=\",\"AAAAAbgFAAA=\"]Bg[\"AAAAAAAAAAAAATsEAAA=\",\"AAAAAAFiEwAA\"]Cc[\"AAAAAeMKAAAC4goAAFILAAABVAsAAALrEAAA7RAAAA==\",\"\"]Ag[\"AAAAAAABrwcAAA==\",\"\"]No[\"AAAAAZsFAAABrQcAAAEfEwAAAQIQAAAGtQwAAMwQAAC/EwAA0xMAADMUAABbFAAAB+EAAACcAQAAFg0AAFQQAADwEgAAyhMAAMwTAAABPRQAAAAAAAAAAAAAA1oHAAAaDQAA9BIAAA==\",\"AAAAApsFAABdEAAAAAGgAQAAAfYSAAAGtQwAAMwQAAC/EwAA0xMAADMUAABbFAAABRYNAABUEAAA8BIAAMoTAADMEwAA\"]Bo[\"AAAAAV0QAAAAAaABAAAAAsAQAABQEQAAAAHODAAA\",\"\"]Nc[\"AAAAAdIFAAAAAAE8AQAAOjAAAAEAAAAAAAcAEAAAAKoBtgzNECATwBPUEzQUXBQFFw0AAFUQAADyEgAAyxMAAM0TAAAAAAAAAAAAAAAEZQYAAF4HAAAbDQAA9RIAAA==\",\"AAAAAtIFAABeEAAAAAGvAQAAAfcSAAAGtgwAAM0QAADAEwAA1BMAADQUAABcFAAABRcNAABVEAAA8hIAAMsTAADNEwAA\"]Bo[\"AAAAAV4QAAAAAa8BAAAAAsQQAABUEQAAAAHQDAAA\",\"\"]Ao[\"AAAAAAAAAAACyhMAAMsTAAA=\",\"\"]Ao[\"AAAAAAAAAAACzBMAAM0TAAA=\",\"\"]Dc[\"AAABMgIAAAGLBQAAAAIUAAAAtwcAAALIAAAAyQAAAA==\",\"AAABMwIAAAGLBQAA\"]Bk[\"AAAAAcMBAAAAAATDAAAAIAYAALgMAACaEgAA\",\"\"]Ec[\"AAAAAn0TAAB+EwAAAnsTAAB8EwAABJMMAACUDAAAlQwAAJYMAAAEjwwAAJAMAACRDAAAkgwAAA==\",\"\"]Cc[\"AAAAAAAAAAAByxIAAA==\",\"AAEwCAAAAAKbAQAAXBMAAA==\"]Cc[\"AAAAAAAAAAABzhIAAA==\",\"AAFFCAAAAAKoAQAAXRMAAA==\"]Bc[\"\",\"AAAAAAAAAAAAAAAAAAABfgcAAA==\"]Cg[\"AAAAAcQPAAAAAAFfAQAAAb4QAAA=\",\"AAFhCAAAAAAAAckTAAA=\"]Ek[\"AAAAAYMFAAAAAAGyAAAAAAAAAAAAAAAAAAAEWBMAAGATAAB6EwAAlRMAAA==\",\"AAExCAAAAAKDBQAAKAYAAA==\"]Cc[\"AAAAAAAAAAAAAAAAAAAAAAAAA+wJAABSEAAAihQAAA==\",\"\"]Cc[\"AAAAAAAAAAAAAAAAAAAAAAAAA+0JAABTEAAAixQAAA==\",\"\"]Bk[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAAscJAADJCQAA\"]Bc[\"AAAAAAABJgQAAA==\",\"AAAAAAFqBQAA\"]Ec[\"AAAAAa8FAAAAAacBAAABcAUAAAKhEQAA8RMAAAEAAQAA\",\"AAFDCAAAAAGvBQAAAAAAAqERAADxEwAA\"]Ec[\"AAAAAfkFAAAAAbkBAAACgwEAAHQFAAACqBEAAPITAAA=\",\"AAFcCAAAAAH5BQAAAAAAAqgRAADyEwAA\"]Ag[\"\",\"AAAAAAABjxIAAA==\"]Co[\"AAAAApEBAABiCAAAAAABWAEAAA==\",\"AAAAAAAAAAAAAAAAAAAAAbEBAAA=\"]Ac[\"\",\"AAAAAAEiEwAA\"]Cg[\"AAAAAAAAAvYSAAD3EgAAAAABkhIAAA==\",\"AAAAAAAAAAHOEwAA\"]Do[\"AAAAAcMFAAAAAjkAAACVAAAAAyIBAAAjAQAAOxAAAAE0AwAA\",\"AAAAAcMFAAAAAAABQgQAAA==\"]Ak[\"AAAAAAACGxQAABwUAAA=\",\"\"]Ac[\"AAAAAcQBAAA=\",\"\"]Ac[\"AAAAAcYBAAA=\",\"\"]Eo[\"AAAC5AIAAPkCAAAB7QUAAAABWAAAAAJyAQAAcwEAAA==\",\"AAAAAe0FAAAAAAAFYAcAAGEHAABiBwAAYwcAAGQHAAA=\"]Fk[\"AAAABM4QAADPEAAAwhMAAMMTAAABxQ8AAAElEQAAAZASAAAAAAE9FAAAAAAAAAAAAAHHDAAA\",\"AAAAAAAAA5kSAACaEgAAmxIAAA==\"]Fc[\"AAAAAAABTAcAAAL2EgAA9xIAAAGKEQAAAAGSEgAAAAACGRQAABoUAAAAAAABbBMAAA==\",\"AAAAAAAAAVIUAAABzhMAAA==\"]Ag[\"\",\"AAAAAAABBAYAAA==\"]Co[\"AAAAAcsFAAAAAUIAAAACMQEAADIBAAA=\",\"AAAAAcsFAAAAAAAAAAHDDgAA\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAZ0HAAA=\",\"\"]Jc[\"AAAAApoFAAA5FAAAAAAAAoYSAABaEwAABOAAAACcAQAAzAwAAGkTAAAD8AkAAMQTAAA9FAAAAAAAASMRAAA=\",\"AAE+CAAAAAKfAQAAmgUAAAAAAAKGEgAAWhMAAALMDAAAaRMAAALwCQAAxBMAAAAAAAEjEQAA\"]Jg[\"AAAAAtEFAAA6FAAAAAABOwEAAAOqAQAAhxIAAFsTAAACzQwAAGoTAAAC8QkAAMUTAAAAAAABJBEAAA==\",\"AAFWCAAAAAKuAQAA0QUAAAFUBwAAAAAChxIAAFsTAAACzQwAAGoTAAAC8QkAAMUTAAAAAAABJBEAAA==\"]Ao[\"AAAAAAAAAbYAAAAAAAE9BAAA\",\"\"]Bc[\"AAAAAAABRgAAAAJDAQAARAEAAA==\",\"\"]Ag[\"AAAChAIAAAwDAAA=\",\"\"]Ak[\"AAAAAAAAAlsEAACEBwAA\",\"\"]Ak[\"AAAAAAAAAl4EAACHBwAA\",\"\"]Ag[\"AAAChQIAAAsDAAA=\",\"\"]Dk[\"AAAAAYkFAAAAAhIAAAC1BwAAAsEAAADCAAAA\",\"AAABKwIAAAGJBQAAAAAAAAAAAdgPAAA=\"]Dg[\"AAAAAYYFAAAAAg8AAACyBwAAArsAAAC8AAAA\",\"AAABIwIAAAGGBQAAAAAAAAFHEAAA\"]Co[\"AAABlhIAAAHHBQAAAAE+AAAAASoBAAA=\",\"AAFQCAAAAbcJAAABxwUAAA==\"]Ec[\"AAAAAAABlxIAAAGqAAAAAAAAAAAAAAAAAAADUgQAALQJAADcDwAA\",\"AAAAAZkBAAAAAAAAAb4TAAA=\"]Hk[\"AAACvAIAANYCAAAB5QUAAAABUgAAAAJjAQAAZAEAAAAAAAAAAAAAAAAABPcBAAALBgAAXwcAAJwHAAA=\",\"AAAB6QIAAAKzAQAA5QUAAAAAAt4TAADfEwAAAAACvxEAAMARAAA=\"]Fc[\"AAAAA7wQAADQEAAA3RIAAAAC6xIAAO0SAAADiAEAANYTAADXEwAAAksEAABZDAAAAAAAAAABRgQAAA==\",\"AAAAAboBAAA=\"]Dc[\"AAAAAl8AAACFBQAAAv4BAABzCQAAAQ4AAAABuAAAAA==\",\"AAAAAoUFAABcEAAA\"]Eg[\"AAAAAaAFAAAAASEAAAAAAALoAAAA6QAAAAAAAAAAAAAAAAOYBwAAuA8AAL8PAAA=\",\"AAABXQIAAAGgBQAA\"]Eg[\"AAAAAdoFAAAAAUoAAAACSwEAAEwBAAAAAAAAAAAAAAAAAAOaBwAAug8AAMAPAAA=\",\"AAAB1gIAAAHaBQAA\"]Hk[\"AAAFGAIAACYCAAApAgAAMAIAADMCAAABlQUAAAACHQAAALgHAAAAAALZAAAA2gAAAA==\",\"AAADTAIAAE0CAABOAgAAApUFAABQBwAAAmMGAACqEAAAAYYTAAABAhAAAAGSEQAA\"]Ek[\"AAAAAckFAAAAAkAAAAC5BwAAAi0BAAAuAQAA\",\"AAABvAIAAAHJBQAAAAAAAAAAAAAAAAAAAAACfAUAAJ0HAAA=\"]Bk[\"AAAAAAABvRAAAAFeAQAA\",\"AAAAAAAAAAG+EAAA\"]Ek[\"AAAAAZIFAAAAAAAAAdUAAAAAAAAAAAAAAAAEwwcAAMcHAAC4CQAAugkAAA==\",\"AAE3CAAAAAKSBQAAJwYAAA==\"]Ek[\"AAAAAcEFAAAAAAEfAQAAAAAAAAAAAAAAAAAExQcAAMkHAAC5CQAAuwkAAA==\",\"AAFOCAAAAAHBBQAAASoGAAA=\"]Ag[\"AAACgwIAABADAAA=\",\"\"]Cg[\"AAAAAf8FAAAAAAADyQ8AAC0QAADSEAAA\",\"AAFgCAAAAAH/BQAA\"]Dc[\"AAAAAt8FAABjCAAAAAABWQEAAA==\",\"AAAAAd8FAAAAAAAAAAAAAAAAAAFpBwAA\"]Dg[\"AAAAAW0TAAABXxAAAAAAAALbAAAAjhEAAA==\",\"AAG+CQAAAZ4BAAABbRMAAAFfEAAA\"]Dg[\"AAAAAW4TAAABYBAAAAABNgEAAAABkBEAAA==\",\"AAG/CQAAAa0BAAABbhMAAAFgEAAA\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAb0PAAA=\",\"\"]Ac[\"\",\"AAAAAAFBFAAA\"]Ag[\"AAAChgIAAAcDAAA=\",\"\"]Bc[\"\",\"AAAAAAAAAAADyhIAAMsSAADMEgAA\"]Bc[\"\",\"AAAAAAAAAAADzRIAAM4SAADPEgAA\"]Ak[\"\",\"AAAAAe8SAAAAAAEMFAAA\"]ADc[\"AAAAAZYFAAAAAAA6MAAAAQAAAAAAFAAQAAAAeAhaDcoPEBATEE8QSxHCEdYR6BJUE/kTUBRdFGkUaxSNFJEUlBSYFJ8UOjAAAAEAAAAAAAwAEAAAANwA2hBHEd4S8BLxEokTtBPHE3UUhhSIFJYUAAABuxEAAA==\",\"AAE6CAAAAAGWBQAAAAAAOjAAAAEAAAAAABQAEAAAAHgIWg3KDxAQExBPEEsRwhHWEegSVBP5E1AUXRRpFGsUjRSRFJQUmBSfFDowAAABAAAAAAAJABAAAADaEEcR3hKJE7QTxxN1FIYUiBSWFAAAAbsRAAA=\"]ADc[\"AAAAAc0FAAAAAAE3AQAAOjAAAAEAAAAAABQAEAAAAHkIWw3LDxEQFBBQEEwRwxHXEekSVRP6E1EUXhRqFGwUjhSSFJUUmRSgFDowAAABAAAAAAALABAAAADbEEgR3xLyEvMSihO1E8gTdhSHFIkUlxQAAAG8EQAA\",\"AAFSCAAAAAHNBQAAAAAAOjAAAAEAAAAAABQAEAAAAHkIWw3LDxEQFBBQEEwRwxHXEekSVRP6E1EUXhRqFGwUjhSSFJUUmRSgFDowAAABAAAAAAAJABAAAADbEEgR3xKKE7UTyBN2FIcUiRSXFAAAAbwRAAA=\"]Ag[\"AAACWQIAAM8CAAA=\",\"\"]Bk[\"AAAAAAAAAAAAATIEAAA=\",\"AAAAAAAAAawMAAA=\"]Bk[\"AAAAAAAAAAAAAAHDDAAA\",\"AAAAAAABFhQAAA==\"]Do[\"AAAAAaEFAAAAASIAAAAAAALqAAAA6wAAAAAAAAAAAAAAAAKZBwAAuQ8AAA==\",\"AAAAAaEFAAA=\"]Do[\"AAAAAdsFAAAAAUsAAAACTQEAAE4BAAAAAAAAAAAAAAAAAAKbBwAAuw8AAA==\",\"AAAAAdsFAAA=\"]Ag[\"AAACiQIAAAkDAAA=\",\"\"]Fc[\"AAAAAd0FAAAAAkwAAACYAAAAAlEBAAA+EAAAAjcDAAC+EAAA\",\"AAAAAd0FAAAAAAAAAAAAAAABJggAAAAAAAABZQYAAA==\"]Ac[\"\",\"AAAAAAEnCAAA\"]Ao[\"AAAAAAFqBQAA\",\"AAAAAAEqCAAA\"]Ag[\"AAACfwIAAA0DAAA=\",\"\"]Ag[\"AAACgAIAAAoDAAA=\",\"\"]Dg[\"AAAAAbAFAAAAAAABYxMAAAAAAAFREQAA\",\"AAAAArAFAADfCgAAAd0KAAAAAeoQAAA=\"]Do[\"AAABuwEAAAHwBQAAAAAAAWUTAAAAAAABVREAAA==\",\"AAAAAfAFAAABUAsAAAFPCwAAAewQAAA=\"]Do[\"AAAAAbIFAAAAAAADwhAAAFIRAABkEwAAAAHPDAAA\",\"AAAAArIFAADjCgAAAeIKAAAAAesQAAA=\"]Eg[\"AAABvAEAAAHyBQAAAAAAA8YQAABWEQAAZhMAAAAB0QwAAA==\",\"AAAAAfIFAAABUgsAAAFUCwAAAe0QAAA=\"]Cc[\"AAAAAAAAAlwEAACFBwAAAAH0AAAA\",\"AAFACAAAAAGjAQAA\"]Cc[\"AAAAAAAAA2kBAABfBAAAiAcAAA==\",\"AAFZCAAAAAG1AQAA\"]Cc[\"AAAAAAAAAAAAAAAAAAAAAAAAA5cHAABpCAAAhwgAAA==\",\"\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAbwPAAA=\",\"\"]Ao[\"AAAAAAAAAW0FAAAAAYYUAAA=\",\"\"]Bk[\"AAAAAAAAAXEFAAAAAYcUAAA=\",\"AAAAAXUFAAA=\"]Eg[\"AAAAAAAAAAABAQEAAAAAAAAAAAAAAAPyCQAALhAAABcTAAA=\",\"AAFECAAAAARwCAAAcQgAAHIIAABzCAAA\"]Eg[\"AAAAAAAAAYQBAAAAAAAAAAAAAAAAAAPzCQAALxAAABgTAAA=\",\"AAFdCAAAAAR0CAAAdQgAAHYIAAB3CAAA\"]Bo[\"AAAAAAABoAEAAAAAARYNAAAAAUcEAAAAAScEAAA=\",\"\"]Bo[\"AAAAAAABrwEAAAAAARcNAAAAAUgEAAAAASkEAAA=\",\"\"]Ak[\"AAAAAAABhxMAAAHtEwAA\",\"\"]Ao[\"AAAAAAABiBMAAAAAAe4TAAA=\",\"\"]Fo[\"AAAAAeIFAAAAAk8AAACZAAAAA1wBAABdAQAAPxAAAAE4AwAA\",\"AAAB5AIAAAHiBQAAAAAAAAISBgAAEwYAAAAAAAAAAAKxAQAAaQcAAA==\"]Dg[\"AAAAAbsFAAAAATMAAAABFwEAAA==\",\"AAAAAbsFAAAAAAAAAAAAAAAAAAAAAAGeBwAA\"]Dg[\"AAAAAYEFAAAAAgwAAACxBwAAAq8AAACwAAAA\",\"AAABGAIAAAGBBQAAAAABShEAAA==\"]Do[\"AAAAAbYFAAAAAi8AAACSAAAAAxABAAARAQAAOBAAAAExAwAA\",\"AAAAAbYFAAAAAAAAAUoEAAA=\"]Ek[\"AAAAAZcFAAAAAAACzxMAACMUAAAD3QAAAC0RAAC0EwAA\",\"AAE7CAAAAAGXBQAAAAAAAs8TAAAjFAAAAS0RAAA=\"]Ek[\"AAAAAc4FAAAAAAE4AQAAAtATAAAkFAAAAi4RAAC1EwAA\",\"AAFTCAAAAAHOBQAAAAAAAtATAAAkFAAAAS4RAAA=\"]Bo[\"AAAAAAAAAAABEgYAAAAAAAAAAAKxAQAAaQcAAA==\",\"\"]Fg[\"AAAAAZgFAAAAAAAE0QkAAFkQAACzEQAAgBMAAALeAAAAxxMAAA==\",\"AAE8CAAAAAGYBQAAAAAABNEJAABZEAAAsxEAAIATAAA=\"]Fg[\"AAAAAc8FAAAAAAE5AQAABNIJAABaEAAAtBEAAIETAAAByBMAAA==\",\"AAFUCAAAAAHPBQAAAAAABNIJAABaEAAAtBEAAIETAAA=\"]Ag[\"\",\"AAAAAAAAAVIUAAA=\"]Bc[\"\",\"AAAAAAAAAAAAAAAAAAAAAAFsEwAA\"]Ag[\"\",\"AAAAAAAAAdkPAAA=\"]Co[\"AAAAAbwFAAAAATQAAAACGAEAABkBAAA=\",\"AAAAAbwFAAAAAAAAAZMSAAA=\"]Fg[\"AAABPAIAAAGlBQAAAAEmAAAAAAFxBwAAAvEAAADyAAAA\",\"AAACZAIAAFgHAAABpQUAAAAAAAFjBwAAAAAAAAAAAAAAAAG4DwAA\"]Gc[\"AAABnwIAAAKzAQAA5gUAAAABUwAAAAJlAQAAZgEAAAF4BwAA\",\"AAAC7AIAAFwHAAAB5gUAAAAAAWoHAAAAAAAAAAAAAAAAAAL3AQAAug8AAA==\"]Cc[\"AAAAAAAAAAAAAAAAAAAAAAAAA2UIAACECAAAdwkAAA==\",\"\"]Cg[\"AAABYQIAAAAAAAAAAQQBAAA=\",\"AAAAAAAAAALAEAAAwhAAAA==\"]Cg[\"AAAB2wIAAAAAAAF5AQAA\",\"AAAAAAAAAAPEEAAAxhAAAMcQAAA=\"]Bo[\"AAAAAnUFAAD+BQAAASITAAA=\",\"AAAAAv4FAADZEQAA\"]Io[\"AAAAAe4FAAAAAVkAAAABdAEAAAY0BgAAUBEAAFIRAABUEQAAVhEAAFgRAAAAAAAFTxEAAFERAABTEQAAVREAAFcRAAAAAAAAAAABnhQAAA==\",\"AAAAAe4FAAAAAATbEwAA3BMAAN4TAADfEwAAAAFDBAAA\"]Co[\"AAAAAmEAAABvCAAAAAABugAAAAJYBAAAxhEAAAHXDwAA\",\"AAABqREAAA==\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAsYJAAC0EAAA\",\"\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAsgJAAC2EAAA\",\"\"]Co[\"AAAAAbEFAAAAAAAAAgUBAAAGAQAA\",\"AAAAAbEFAAAAAALqEAAA6xAAAA==\"]Dc[\"AAAAAfEFAAAAAAJ6AQAAewEAAA==\",\"AAAAAvEFAADQEgAAAAAC7BAAAO0QAAA=\"]Ac[\"\",\"AAABlwwAAA==\"]Ec[\"AAABWgIAAAGNBQAAAAEWAAAAAAACzQAAAM4AAAA=\",\"AAAAAY0FAAAExAkAAKgKAACpCgAAqwoAAA==\"]Do[\"AAABzQIAAAG1BQAAAAEtAAAAAg0BAAAOAQAA\",\"AAAAAbUFAAAExQkAAPkKAAD7CgAA/AoAAA==\"]Ak[\"\",\"AAAAAAACGxQAABwUAAA=\"]Ck[\"AAAAAoAFAADbCQAAAXIJAAABWRMAAAGuAAAA\",\"AAAAAoAFAABbEAAA\"]Cg[\"AAAAAQYIAAACphAAALgQAAAAAVoMAAAAAAAAAAABIxEAAA==\",\"\"]Ck[\"AAAAAQcIAAACpxAAALkQAAAAAlsMAABcDAAAAAAAAAAAASQRAAA=\",\"\"]Bc[\"AAAAAAAAAAAAAAAAAhkUAAAaFAAA\",\"\"]Fo[\"AAAAAZwFAAAAAAADTAQAANwJAAAzFAAAAuIAAACVAQAA\",\"AAE/CAAAAAGcBQAAAAAAAkwEAADcCQAAAZUBAAAAAAAAAAAAAAAB9BIAAA==\"]Fo[\"AAAAAdMFAAAAAAE9AQAAA00EAADdCQAANBQAAAGWAQAA\",\"AAFXCAAAAAHTBQAAAAAAAk0EAADdCQAAAZYBAAAAAAAAAAAAAAAB9RIAAA==\"]Ck[\"AAAAAcwFAAAAAAIzAQAANAEAAA==\",\"AAAAAcwFAAAAAAAAAUAUAAA=\"]Ec[\"AAAAAeAFAAAAAAFaAQAAAAAAAAAAAAAAAAAFkgEAAGQIAACDCAAAdgkAABgNAAA=\",\"AAAAAeAFAAA=\"]Dg[\"AAAAAAFBFAAAAAGrAAAAAAAAAAACGRQAABoUAAAAAAABbBMAAA==\",\"AAABmgEAAA==\"]Ag[\"AAACiAIAAAUDAAA=\",\"\"]Bc[\"\",\"AAAAAAAAAAAAAAAAAhkUAAAaFAAA\"]Cc[\"AAAAAQAGAAAAAAAAAAAAAVcRAAA=\",\"AAABuwEAAAEABgAA\"]Ck[\"AAAAAQEGAAAAAAACxxAAAFgRAAAAAdIMAAA=\",\"AAABvAEAAAEBBgAA\"]Ag[\"AAAChwIAAAYDAAA=\",\"\"]Dg[\"AAAAAAABKgAAAAAAAQIBAAA=\",\"AAAAAAAAAAAAAAAAAAAAAw0UAAAOFAAADxQAAA==\"]Ck[\"AAAAAAABWwAAAAF3AQAA\",\"AAAAAAAAAAAAAAAAAAAAAhAUAAARFAAA\"]Ag[\"AAACggIAAA4DAAA=\",\"\"]Do[\"AAAAAQgIAAACqBAAALoQAAAAAV0MAAAB4hAAAA==\",\"AAKsAQAARggAAAHACQAAAAAAAAHiEAAA\"]Ec[\"AAAAAugFAAAJCAAAAqkQAAC7EAAAAAFoAQAAAeMQAAA=\",\"AAFYCAAAAbQBAAAB6AUAAAAAAAHjEAAA\"]Bg[\"AAAAAAAAAAABEwYAAA==\",\"AAABqwEAAA==\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAscJAAC1EAAA\",\"\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAskJAAC3EAAA\",\"\"]") \ No newline at end of file diff --git a/pr/509/docs/set_up_connection/all.html b/pr/509/docs/set_up_connection/all.html new file mode 100644 index 000000000..93799f9a4 --- /dev/null +++ b/pr/509/docs/set_up_connection/all.html @@ -0,0 +1 @@ +List of all items in this crate

All

\ No newline at end of file diff --git a/pr/509/docs/set_up_connection/constant.CLIENT_ADDR.html b/pr/509/docs/set_up_connection/constant.CLIENT_ADDR.html new file mode 100644 index 000000000..480b4b634 --- /dev/null +++ b/pr/509/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/509/docs/set_up_connection/constant.LOCALHOST_V4.html b/pr/509/docs/set_up_connection/constant.LOCALHOST_V4.html new file mode 100644 index 000000000..49f7c0f99 --- /dev/null +++ b/pr/509/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/509/docs/set_up_connection/constant.SERVER_ADDR.html b/pr/509/docs/set_up_connection/constant.SERVER_ADDR.html new file mode 100644 index 000000000..046828f6b --- /dev/null +++ b/pr/509/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/509/docs/set_up_connection/constant.SERVER_NAME.html b/pr/509/docs/set_up_connection/constant.SERVER_NAME.html new file mode 100644 index 000000000..84fc92733 --- /dev/null +++ b/pr/509/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/509/docs/set_up_connection/fn.client.html b/pr/509/docs/set_up_connection/fn.client.html new file mode 100644 index 000000000..0b2708bd9 --- /dev/null +++ b/pr/509/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/509/docs/set_up_connection/fn.main.html b/pr/509/docs/set_up_connection/fn.main.html new file mode 100644 index 000000000..23244837f --- /dev/null +++ b/pr/509/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/509/docs/set_up_connection/fn.server.html b/pr/509/docs/set_up_connection/fn.server.html new file mode 100644 index 000000000..5c49aee37 --- /dev/null +++ b/pr/509/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/509/docs/set_up_connection/index.html b/pr/509/docs/set_up_connection/index.html new file mode 100644 index 000000000..9d78e0b7f --- /dev/null +++ b/pr/509/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/509/docs/set_up_connection/sidebar-items.js b/pr/509/docs/set_up_connection/sidebar-items.js new file mode 100644 index 000000000..3f14772bc --- /dev/null +++ b/pr/509/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/509/docs/settings.html b/pr/509/docs/settings.html new file mode 100644 index 000000000..1adf3f67d --- /dev/null +++ b/pr/509/docs/settings.html @@ -0,0 +1 @@ +Settings

All

Rustdoc settings

Back
\ No newline at end of file diff --git a/pr/509/docs/src-files.js b/pr/509/docs/src-files.js new file mode 100644 index 000000000..b8d7393c6 --- /dev/null +++ b/pr/509/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","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",[["bbr",[],["bw_estimation.rs","min_max.rs","mod.rs"]]],["cubic.rs","new_reno.rs"]],["connection",[["streams",[],["mod.rs","recv.rs","send.rs","state.rs"]]],["ack_frequency.rs","assembler.rs","cid_state.rs","datagrams.rs","mod.rs","mtud.rs","pacing.rs","packet_builder.rs","packet_crypto.rs","paths.rs","qlog.rs","send_buffer.rs","spaces.rs","state.rs","stats.rs","timer.rs","transmit_buf.rs"]],["crypto",[],["ring_like.rs","rustls.rs"]],["range_set",[],["array_range_set.rs","mod.rs"]]],["address_discovery.rs","bloom_token_log.rs","cid_generator.rs","cid_queue.rs","coding.rs","congestion.rs","constant_time.rs","crypto.rs","endpoint.rs","frame.rs","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,190,33,885,73,81,55]} \ No newline at end of file diff --git a/pr/509/docs/src/bench/lib.rs.html b/pr/509/docs/src/bench/lib.rs.html new file mode 100644 index 000000000..a695acb6f --- /dev/null +++ b/pr/509/docs/src/bench/lib.rs.html @@ -0,0 +1,256 @@ +lib.rs - source

bench/
lib.rs

1use core::str;
+2use std::{
+3    convert::TryInto,
+4    net::{IpAddr, Ipv6Addr, SocketAddr},
+5    num::ParseIntError,
+6    str::FromStr,
+7    sync::Arc,
+8};
+9
+10use anyhow::{Context, Result};
+11use bytes::Bytes;
+12use clap::Parser;
+13use 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_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 {
+151    Builder::new_current_thread().enable_all().build().unwrap()
+152}
+153
+154pub fn transport_config(opt: &Opt) -> noq::TransportConfig {
+155    // High stream windows are chosen because the amount of concurrent streams
+156    // is configurable as a parameter.
+157    let mut config = noq::TransportConfig::default();
+158    config.max_concurrent_uni_streams(opt.max_streams.try_into().unwrap());
+159    config.initial_mtu(opt.initial_mtu);
+160
+161    let mut acks = noq::AckFrequencyConfig::default();
+162    acks.ack_eliciting_threshold(10u32.into());
+163    config.ack_frequency_config(Some(acks));
+164
+165    config
+166}
+167
+168#[derive(Parser, Debug, Clone, Copy)]
+169#[clap(name = "bulk")]
+170pub struct Opt {
+171    /// The total number of clients which should be created
+172    #[clap(long = "clients", short = 'c', default_value = "1")]
+173    pub clients: usize,
+174    /// The total number of streams which should be created
+175    #[clap(long = "streams", short = 'n', default_value = "1")]
+176    pub streams: usize,
+177    /// The amount of concurrent streams which should be used
+178    #[clap(long = "max_streams", short = 'm', default_value = "1")]
+179    pub max_streams: usize,
+180    /// Number of bytes to transmit from server to client
+181    ///
+182    /// This can use SI suffixes for sizes. For example, 1M will transfer
+183    /// 1MiB, 10G will transfer 10GiB.
+184    #[clap(long, default_value = "1G", value_parser = parse_byte_size)]
+185    pub download_size: u64,
+186    /// Number of bytes to transmit from client to server
+187    ///
+188    /// This can use SI suffixes for sizes. For example, 1M will transfer
+189    /// 1MiB, 10G will transfer 10GiB.
+190    #[clap(long, default_value = "0", value_parser = parse_byte_size)]
+191    pub upload_size: u64,
+192    /// Show connection stats the at the end of the benchmark
+193    #[clap(long = "stats")]
+194    pub stats: bool,
+195    /// Whether to use the unordered read API
+196    #[clap(long = "unordered")]
+197    pub read_unordered: bool,
+198    /// Allows to configure the desired cipher suite
+199    ///
+200    /// Valid options are: aes128, aes256, chacha20
+201    #[clap(long = "cipher", default_value = "aes128")]
+202    pub cipher: CipherSuite,
+203    /// Starting guess for maximum UDP payload size
+204    #[clap(long, default_value = "1200")]
+205    pub initial_mtu: u16,
+206}
+207
+208fn parse_byte_size(s: &str) -> Result<u64, ParseIntError> {
+209    let s = s.trim();
+210
+211    let multiplier = match s.chars().last() {
+212        Some('T') => 1024 * 1024 * 1024 * 1024,
+213        Some('G') => 1024 * 1024 * 1024,
+214        Some('M') => 1024 * 1024,
+215        Some('k') => 1024,
+216        _ => 1,
+217    };
+218
+219    let s = match multiplier {
+220        1 => s,
+221        _ => &s[..s.len() - 1],
+222    };
+223
+224    Ok(u64::from_str(s)? * multiplier)
+225}
+226
+227#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+228pub enum CipherSuite {
+229    Aes128,
+230    Aes256,
+231    Chacha20,
+232}
+233
+234impl CipherSuite {
+235    pub fn as_rustls(self) -> rustls::SupportedCipherSuite {
+236        use rustls::crypto::ring::cipher_suite;
+237        match self {
+238            Self::Aes128 => cipher_suite::TLS13_AES_128_GCM_SHA256,
+239            Self::Aes256 => cipher_suite::TLS13_AES_256_GCM_SHA384,
+240            Self::Chacha20 => cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
+241        }
+242    }
+243}
+244
+245impl FromStr for CipherSuite {
+246    type Err = anyhow::Error;
+247
+248    fn from_str(s: &str) -> Result<Self, Self::Err> {
+249        match s.to_lowercase().as_str() {
+250            "aes128" => Ok(Self::Aes128),
+251            "aes256" => Ok(Self::Aes256),
+252            "chacha20" => Ok(Self::Chacha20),
+253            _ => Err(anyhow::anyhow!("Unknown cipher suite {}", s)),
+254        }
+255    }
+256}
\ No newline at end of file diff --git a/pr/509/docs/src/bench/stats.rs.html b/pr/509/docs/src/bench/stats.rs.html new file mode 100644 index 000000000..b8c881d8d --- /dev/null +++ b/pr/509/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/509/docs/src/bulk/bulk.rs.html b/pr/509/docs/src/bulk/bulk.rs.html new file mode 100644 index 000000000..485a5e5ac --- /dev/null +++ b/pr/509/docs/src/bulk/bulk.rs.html @@ -0,0 +1,219 @@ +bulk.rs - source

bulk/
bulk.rs

1use std::{
+2    net::SocketAddr,
+3    sync::{Arc, Mutex},
+4    time::Instant,
+5};
+6
+7use anyhow::{Context, Result};
+8use clap::Parser;
+9use rustls::pki_types::{CertificateDer, PrivatePkcs8KeyDer};
+10use tokio::sync::Semaphore;
+11use tracing::{info, trace};
+12
+13use bench::{
+14    Opt, configure_tracing_subscriber, connect_client, drain_stream, rt, send_data_on_stream,
+15    server_endpoint,
+16    stats::{Stats, TransferResult},
+17};
+18
+19fn main() {
+20    let opt = Opt::parse();
+21    configure_tracing_subscriber();
+22
+23    let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()]).unwrap();
+24    let key = PrivatePkcs8KeyDer::from(cert.signing_key.serialize_der());
+25    let cert = CertificateDer::from(cert.cert);
+26
+27    let server_span = tracing::error_span!("server");
+28    let runtime = rt();
+29    let (server_addr, endpoint) = {
+30        let _guard = server_span.enter();
+31        server_endpoint(&runtime, cert.clone(), key.into(), &opt)
+32    };
+33
+34    let server_thread = std::thread::spawn(move || {
+35        let _guard = server_span.entered();
+36        if let Err(e) = runtime.block_on(server(endpoint, opt)) {
+37            eprintln!("server failed: {e:#}");
+38        }
+39    });
+40
+41    let mut handles = Vec::new();
+42    for id in 0..opt.clients {
+43        let cert = cert.clone();
+44        handles.push(std::thread::spawn(move || {
+45            let _guard = tracing::error_span!("client", id).entered();
+46            let runtime = rt();
+47            match runtime.block_on(client(server_addr, cert, opt)) {
+48                Ok(stats) => Ok(stats),
+49                Err(e) => {
+50                    eprintln!("client failed: {e:#}");
+51                    Err(e)
+52                }
+53            }
+54        }));
+55    }
+56
+57    for (id, handle) in handles.into_iter().enumerate() {
+58        // We print all stats at the end of the test sequentially to avoid
+59        // them being garbled due to being printed concurrently
+60        if let Ok(stats) = handle.join().expect("client thread") {
+61            stats.print(id);
+62        }
+63    }
+64
+65    server_thread.join().expect("server thread");
+66}
+67
+68async fn server(endpoint: 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_idle().await;
+166
+167    if opt.stats {
+168        println!("\nClient connection stats:\n{:#?}", connection.stats());
+169    }
+170
+171    match first_error {
+172        None => Ok(stats),
+173        Some(e) => Err(e),
+174    }
+175}
+176
+177async fn handle_client_stream(
+178    connection: Arc<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/509/docs/src/certificate/certificate.rs.html b/pr/509/docs/src/certificate/certificate.rs.html new file mode 100644 index 000000000..b22a50d14 --- /dev/null +++ b/pr/509/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/509/docs/src/data_transfer/data-transfer.rs.html b/pr/509/docs/src/data_transfer/data-transfer.rs.html new file mode 100644 index 000000000..2b1581d24 --- /dev/null +++ b/pr/509/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/509/docs/src/noq/connection.rs.html b/pr/509/docs/src/noq/connection.rs.html new file mode 100644 index 000000000..dbb79bdee --- /dev/null +++ b/pr/509/docs/src/noq/connection.rs.html @@ -0,0 +1,1839 @@ +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::{Arc, Weak},
+10    task::{Context, Poll, Waker, ready},
+11};
+12
+13use bytes::Bytes;
+14use pin_project_lite::pin_project;
+15use rustc_hash::FxHashMap;
+16use thiserror::Error;
+17use tokio::sync::{Notify, futures::Notified, mpsc, oneshot, watch};
+18use tracing::{Instrument, Span, debug_span};
+19
+20use crate::{
+21    ConnectionEvent, Duration, Instant, Path, VarInt,
+22    endpoint::ensure_ipv6,
+23    mutex::Mutex,
+24    path::OpenPath,
+25    recv_stream::RecvStream,
+26    runtime::{AsyncTimer, Runtime, UdpSender},
+27    send_stream::SendStream,
+28    udp_transmit,
+29};
+30use proto::{
+31    ConnectionError, ConnectionHandle, ConnectionStats, Dir, EndpointEvent, FourTuple, PathError,
+32    PathEvent, PathId, PathStats, PathStatus, Side, StreamEvent, StreamId, TransportError,
+33    TransportErrorCode, congestion::Controller, n0_nat_traversal,
+34};
+35
+36/// In-progress connection attempt future
+37#[derive(Debug)]
+38pub struct Connecting {
+39    conn: Option<ConnectionRef>,
+40    connected: oneshot::Receiver<bool>,
+41    handshake_data_ready: Option<oneshot::Receiver<()>>,
+42}
+43
+44impl Connecting {
+45    pub(crate) fn new(
+46        handle: ConnectionHandle,
+47        conn: proto::Connection,
+48        endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+49        conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
+50        sender: Pin<Box<dyn UdpSender>>,
+51        runtime: Arc<dyn Runtime>,
+52    ) -> Self {
+53        let (on_handshake_data_send, on_handshake_data_recv) = oneshot::channel();
+54        let (on_connected_send, on_connected_recv) = oneshot::channel();
+55
+56        let conn = ConnectionRef(Arc::new(Arc::new(ConnectionInner {
+57            state: Mutex::new(State::new(
+58                conn,
+59                handle,
+60                endpoint_events,
+61                conn_events,
+62                on_handshake_data_send,
+63                on_connected_send,
+64                sender,
+65                runtime.clone(),
+66            )),
+67            shared: Shared::default(),
+68        })));
+69
+70        let driver = ConnectionDriver(conn.clone());
+71        runtime.spawn(Box::pin(
+72            async {
+73                if let Err(e) = driver.await {
+74                    tracing::error!("I/O error: {e}");
+75                }
+76            }
+77            .instrument(Span::current()),
+78        ));
+79
+80        Self {
+81            conn: Some(conn),
+82            connected: on_connected_recv,
+83            handshake_data_ready: Some(on_handshake_data_recv),
+84        }
+85    }
+86
+87    /// Convert into a 0-RTT or 0.5-RTT connection at the cost of weakened security
+88    ///
+89    /// Returns `Ok` immediately if the local endpoint is able to attempt sending 0/0.5-RTT data.
+90    /// If so, the returned [`Connection`] can be used to send application data without waiting for
+91    /// the rest of the handshake to complete, at the cost of weakened cryptographic security
+92    /// guarantees. The returned [`ZeroRttAccepted`] future resolves when the handshake does
+93    /// complete, at which point subsequently opened streams and written data will have full
+94    /// cryptographic protection.
+95    ///
+96    /// ## Outgoing
+97    ///
+98    /// For outgoing connections, the initial attempt to convert to a [`Connection`] which sends
+99    /// 0-RTT data will proceed if the [`crypto::ClientConfig`][crate::crypto::ClientConfig]
+100    /// attempts to resume a previous TLS session. However, **the remote endpoint may not actually
+101    /// _accept_ the 0-RTT data**--yet still accept the connection attempt in general. This
+102    /// possibility is conveyed through the [`ZeroRttAccepted`] future--when the handshake
+103    /// completes, it resolves to true if the 0-RTT data was accepted and false if it was rejected.
+104    /// If it was rejected, the existence of streams opened and other application data sent prior
+105    /// to the handshake completing will not be conveyed to the remote application, and local
+106    /// operations on them will return `ZeroRttRejected` errors.
+107    ///
+108    /// A server may reject 0-RTT data at its discretion, but accepting 0-RTT data requires the
+109    /// relevant resumption state to be stored in the server, which servers may limit or lose for
+110    /// various reasons including not persisting resumption state across server restarts.
+111    ///
+112    /// If manually providing a [`crypto::ClientConfig`][crate::crypto::ClientConfig], check your
+113    /// implementation's docs for 0-RTT pitfalls.
+114    ///
+115    /// ## Incoming
+116    ///
+117    /// For incoming connections, conversion to 0.5-RTT will always fully succeed. `into_0rtt` will
+118    /// always return `Ok` and the [`ZeroRttAccepted`] will always resolve to true.
+119    ///
+120    /// If manually providing a [`crypto::ServerConfig`][crate::crypto::ServerConfig], check your
+121    /// implementation's docs for 0-RTT pitfalls.
+122    ///
+123    /// ## Security
+124    ///
+125    /// On outgoing connections, this enables transmission of 0-RTT data, which is vulnerable to
+126    /// replay attacks, and should therefore never invoke non-idempotent operations.
+127    ///
+128    /// On incoming connections, this enables transmission of 0.5-RTT data, which may be sent
+129    /// before TLS client authentication has occurred, and should therefore not be used to send
+130    /// data for which client authentication is being used.
+131    pub fn into_0rtt(mut self) -> Result<(Connection, ZeroRttAccepted), Self> {
+132        // This lock borrows `self` and would normally be dropped at the end of this scope, so we'll
+133        // have to release it explicitly before returning `self` by value.
+134        let conn = (self.conn.as_mut().unwrap()).state.lock("into_0rtt");
+135
+136        let is_ok = conn.inner.has_0rtt() || conn.inner.side().is_server();
+137        drop(conn);
+138
+139        if is_ok {
+140            let conn = self.conn.take().unwrap();
+141            Ok((Connection(conn), ZeroRttAccepted(self.connected)))
+142        } else {
+143            Err(self)
+144        }
+145    }
+146
+147    /// Parameters negotiated during the handshake
+148    ///
+149    /// The dynamic type returned is determined by the configured
+150    /// [`Session`](proto::crypto::Session). For the default `rustls` session, the return value can
+151    /// be [`downcast`](Box::downcast) to a
+152    /// [`crypto::rustls::HandshakeData`](crate::crypto::rustls::HandshakeData).
+153    pub async fn handshake_data(&mut self) -> Result<Box<dyn Any>, ConnectionError> {
+154        // Taking &mut self allows us to use a single oneshot channel rather than dealing with
+155        // potentially many tasks waiting on the same event. It's a bit of a hack, but keeps things
+156        // simple.
+157        if let Some(x) = self.handshake_data_ready.take() {
+158            let _ = x.await;
+159        }
+160        let conn = self.conn.as_ref().unwrap();
+161        let inner = conn.state.lock("handshake");
+162        inner
+163            .inner
+164            .crypto_session()
+165            .handshake_data()
+166            .ok_or_else(|| {
+167                inner
+168                    .error
+169                    .clone()
+170                    .expect("spurious handshake data ready notification")
+171            })
+172    }
+173
+174    /// The local IP address which was used when the peer established
+175    /// the connection
+176    ///
+177    /// This can be different from the address the endpoint is bound to, in case
+178    /// the endpoint is bound to a wildcard address like `0.0.0.0` or `::`.
+179    ///
+180    /// This will return `None` for clients, or when the platform does not expose this
+181    /// information. See [`noq_udp::RecvMeta::dst_ip`](udp::RecvMeta::dst_ip) for a list of
+182    /// supported platforms when using [`noq_udp`](udp) for I/O, which is the default.
+183    ///
+184    /// Will panic if called after `poll` has returned `Ready`.
+185    pub fn local_ip(&self) -> Option<IpAddr> {
+186        let conn = self.conn.as_ref().unwrap();
+187        let inner = conn.state.lock("local_ip");
+188
+189        inner
+190            .inner
+191            .network_path(PathId::ZERO)
+192            .expect("path exists when connecting")
+193            .local_ip
+194    }
+195
+196    /// The peer's UDP addresses
+197    ///
+198    /// Will panic if called after `poll` has returned `Ready`.
+199    pub fn remote_address(&self) -> SocketAddr {
+200        let conn_ref: &ConnectionRef = self.conn.as_ref().expect("used after yielding Ready");
+201        // TODO: another unwrap
+202        conn_ref
+203            .state
+204            .lock("remote_address")
+205            .inner
+206            .network_path(PathId::ZERO)
+207            .expect("path exists when connecting")
+208            .remote
+209    }
+210}
+211
+212impl Future for Connecting {
+213    type Output = Result<Connection, ConnectionError>;
+214    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+215        Pin::new(&mut self.connected).poll(cx).map(|_| {
+216            let conn = self.conn.take().unwrap();
+217            let inner = conn.state.lock("connecting");
+218            if inner.connected {
+219                drop(inner);
+220                Ok(Connection(conn))
+221            } else {
+222                Err(inner
+223                    .error
+224                    .clone()
+225                    .expect("connected signaled without connection success or error"))
+226            }
+227        })
+228    }
+229}
+230
+231/// Future that completes when a connection is fully established
+232///
+233/// For clients, the resulting value indicates if 0-RTT was accepted. For servers, the resulting
+234/// value is meaningless.
+235pub struct ZeroRttAccepted(oneshot::Receiver<bool>);
+236
+237impl Future for ZeroRttAccepted {
+238    type Output = bool;
+239    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+240        Pin::new(&mut self.0).poll(cx).map(|x| x.unwrap_or(false))
+241    }
+242}
+243
+244/// A future that drives protocol logic for a connection
+245///
+246/// This future handles the protocol logic for a single connection, routing events from the
+247/// `Connection` API object to the `Endpoint` task and the related stream-related interfaces.
+248/// It also keeps track of outstanding timeouts for the `Connection`.
+249///
+250/// If the connection encounters an error condition, this future will yield an error. It will
+251/// terminate (yielding `Ok(())`) if the connection was closed without error. Unlike other
+252/// connection-related futures, this waits for the draining period to complete to ensure that
+253/// packets still in flight from the peer are handled gracefully.
+254#[must_use = "connection drivers must be spawned for their connections to function"]
+255#[derive(Debug)]
+256struct ConnectionDriver(ConnectionRef);
+257
+258impl Future for ConnectionDriver {
+259    type Output = Result<(), io::Error>;
+260
+261    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+262        let conn = &mut *self.0.state.lock("poll");
+263
+264        let span = debug_span!("drive", id = conn.handle.0);
+265        let _guard = span.enter();
+266
+267        if let Err(e) = conn.process_conn_events(&self.0.shared, cx) {
+268            conn.terminate(e, &self.0.shared);
+269            return Poll::Ready(Ok(()));
+270        }
+271        let mut keep_going = conn.drive_transmit(cx)?;
+272        // If a timer expires, there might be more to transmit. When we transmit something, we
+273        // might need to reset a timer. Hence, we must loop until neither happens.
+274        keep_going |= conn.drive_timer(cx);
+275        conn.forward_endpoint_events();
+276        conn.forward_app_events(&self.0.shared);
+277
+278        if !conn.inner.is_drained() {
+279            if keep_going {
+280                // If the connection hasn't processed all tasks, schedule it again
+281                cx.waker().wake_by_ref();
+282            } else {
+283                conn.driver = Some(cx.waker().clone());
+284            }
+285            return Poll::Pending;
+286        }
+287        if conn.error.is_none() {
+288            unreachable!("drained connections always have an error");
+289        }
+290        Poll::Ready(Ok(()))
+291    }
+292}
+293
+294/// A QUIC connection.
+295///
+296/// If all references to a connection (including every clone of the `Connection` handle, streams of
+297/// incoming streams, and the various stream types) have been dropped, then the connection will be
+298/// automatically closed with an `error_code` of 0 and an empty `reason`. You can also close the
+299/// connection explicitly by calling [`Connection::close()`].
+300///
+301/// Closing the connection immediately abandons efforts to deliver data to the peer.  Upon
+302/// receiving CONNECTION_CLOSE the peer *may* drop any stream data not yet delivered to the
+303/// application. [`Connection::close()`] describes in more detail how to gracefully close a
+304/// connection without losing application data.
+305///
+306/// May be cloned to obtain another handle to the same connection.
+307///
+308/// [`Connection::close()`]: Connection::close
+309#[derive(Debug, Clone)]
+310pub struct Connection(ConnectionRef);
+311
+312impl Connection {
+313    /// Returns a weak reference to the inner connection struct.
+314    pub fn weak_handle(&self) -> WeakConnectionHandle {
+315        self.0.weak_handle()
+316    }
+317
+318    /// Initiate a new outgoing unidirectional stream.
+319    ///
+320    /// Streams are cheap and instantaneous to open unless blocked by flow control. As a
+321    /// consequence, the peer won't be notified that a stream has been opened until the stream is
+322    /// actually used.
+323    pub fn open_uni(&self) -> OpenUni<'_> {
+324        OpenUni {
+325            conn: &self.0,
+326            notify: self.0.shared.stream_budget_available[Dir::Uni as usize].notified(),
+327        }
+328    }
+329
+330    /// Initiate a new outgoing bidirectional stream.
+331    ///
+332    /// Streams are cheap and instantaneous to open unless blocked by flow control. As a
+333    /// consequence, the peer won't be notified that a stream has been opened until the stream is
+334    /// actually used. Calling [`open_bi()`] then waiting on the [`RecvStream`] without writing
+335    /// anything to [`SendStream`] will never succeed.
+336    ///
+337    /// [`open_bi()`]: crate::Connection::open_bi
+338    /// [`SendStream`]: crate::SendStream
+339    /// [`RecvStream`]: crate::RecvStream
+340    pub fn open_bi(&self) -> OpenBi<'_> {
+341        OpenBi {
+342            conn: &self.0,
+343            notify: self.0.shared.stream_budget_available[Dir::Bi as usize].notified(),
+344        }
+345    }
+346
+347    /// Accept the next incoming uni-directional stream
+348    pub fn accept_uni(&self) -> AcceptUni<'_> {
+349        AcceptUni {
+350            conn: &self.0,
+351            notify: self.0.shared.stream_incoming[Dir::Uni as usize].notified(),
+352        }
+353    }
+354
+355    /// Accept the next incoming bidirectional stream
+356    ///
+357    /// **Important Note**: The `Connection` that calls [`open_bi()`] must write to its [`SendStream`]
+358    /// before the other `Connection` is able to `accept_bi()`. Calling [`open_bi()`] then
+359    /// waiting on the [`RecvStream`] without writing anything to [`SendStream`] will never succeed.
+360    ///
+361    /// [`accept_bi()`]: crate::Connection::accept_bi
+362    /// [`open_bi()`]: crate::Connection::open_bi
+363    /// [`SendStream`]: crate::SendStream
+364    /// [`RecvStream`]: crate::RecvStream
+365    pub fn accept_bi(&self) -> AcceptBi<'_> {
+366        AcceptBi {
+367            conn: &self.0,
+368            notify: self.0.shared.stream_incoming[Dir::Bi as usize].notified(),
+369        }
+370    }
+371
+372    /// Receive an application datagram
+373    pub fn read_datagram(&self) -> ReadDatagram<'_> {
+374        ReadDatagram {
+375            conn: &self.0,
+376            notify: self.0.shared.datagram_received.notified(),
+377        }
+378    }
+379
+380    /// Opens a new path if no path exists yet for the remote address.
+381    ///
+382    /// Otherwise behaves exactly as [`open_path`].
+383    ///
+384    /// [`open_path`]: Self::open_path
+385    pub fn open_path_ensure(&self, addr: SocketAddr, initial_status: PathStatus) -> OpenPath {
+386        let mut state = self.0.state.lock("open_path");
+387
+388        // If endpoint::State::ipv6 is true we want to keep all our IP addresses as IPv6.
+389        // If not, we do not support IPv6.  We can not access endpoint::State from here
+390        // however, but either all our paths use an IPv6 address, or all our paths use an
+391        // IPv4 address.  So we can use that information.
+392        let ipv6 = state
+393            .inner
+394            .paths()
+395            .iter()
+396            .filter_map(|id| {
+397                state
+398                    .inner
+399                    .network_path(*id)
+400                    .map(|addrs| addrs.remote.is_ipv6())
+401                    .ok()
+402            })
+403            .next()
+404            .unwrap_or_default();
+405        if addr.is_ipv6() && !ipv6 {
+406            return OpenPath::rejected(PathError::InvalidRemoteAddress(addr));
+407        }
+408        let addr = if ipv6 {
+409            SocketAddr::V6(ensure_ipv6(addr))
+410        } else {
+411            addr
+412        };
+413
+414        let now = state.runtime.now();
+415        // TODO(matheus23): For now this means it's impossible to make use of short-circuiting path validation currently.
+416        // However, changing that would mean changing the API.
+417        let addrs = FourTuple {
+418            remote: addr,
+419            local_ip: None,
+420        };
+421        let open_res = state.inner.open_path_ensure(addrs, initial_status, now);
+422        state.wake();
+423        match open_res {
+424            Ok((path_id, existed)) if existed => {
+425                let recv = state.open_path.get(&path_id).map(|tx| tx.subscribe());
+426                drop(state);
+427                match recv {
+428                    Some(recv) => OpenPath::new(path_id, recv, self.0.clone()),
+429                    None => OpenPath::ready(path_id, self.0.clone()),
+430                }
+431            }
+432            Ok((path_id, _)) => {
+433                let (tx, rx) = watch::channel(Ok(()));
+434                state.open_path.insert(path_id, tx);
+435                drop(state);
+436                OpenPath::new(path_id, rx, self.0.clone())
+437            }
+438            Err(err) => OpenPath::rejected(err),
+439        }
+440    }
+441
+442    /// Opens an additional path if the multipath extension is negotiated.
+443    ///
+444    /// The returned future completes once the path is either fully opened and ready to
+445    /// carry application data, or if there was an error.
+446    ///
+447    /// Dropping the returned future does not cancel the opening of the path, the
+448    /// [`PathEvent::Opened`] event will still be emitted from [`Self::path_events`] if the
+449    /// path opens.  The [`PathId`] for the events can be extracted from
+450    /// [`OpenPath::path_id`].
+451    ///
+452    /// Failure to open a path can either occur immediately, before polling the returned
+453    /// future, or at a later time.  If the failure is immediate [`OpenPath::path_id`] will
+454    /// return `None` and the future will be ready immediately.  If the failure happens
+455    /// later, a [`PathEvent`] will be emitted.
+456    pub fn open_path(&self, addr: SocketAddr, initial_status: PathStatus) -> OpenPath {
+457        let mut state = self.0.state.lock("open_path");
+458
+459        // If endpoint::State::ipv6 is true we want to keep all our IP addresses as IPv6.
+460        // If not, we do not support IPv6.  We can not access endpoint::State from here
+461        // however, but either all our paths use an IPv6 address, or all our paths use an
+462        // IPv4 address.  So we can use that information.
+463        let ipv6 = state
+464            .inner
+465            .paths()
+466            .iter()
+467            .filter_map(|id| {
+468                state
+469                    .inner
+470                    .network_path(*id)
+471                    .map(|addrs| addrs.remote.is_ipv6())
+472                    .ok()
+473            })
+474            .next()
+475            .unwrap_or_default();
+476        if addr.is_ipv6() && !ipv6 {
+477            return OpenPath::rejected(PathError::InvalidRemoteAddress(addr));
+478        }
+479        let addr = if ipv6 {
+480            SocketAddr::V6(ensure_ipv6(addr))
+481        } else {
+482            addr
+483        };
+484
+485        let (on_open_path_send, on_open_path_recv) = watch::channel(Ok(()));
+486        let now = state.runtime.now();
+487        // TODO(matheus23): For now this means it's impossible to make use of short-circuiting path validation currently.
+488        // However, changing that would mean changing the API.
+489        let addrs = FourTuple {
+490            remote: addr,
+491            local_ip: None,
+492        };
+493        let open_res = state.inner.open_path(addrs, initial_status, now);
+494        state.wake();
+495        match open_res {
+496            Ok(path_id) => {
+497                state.open_path.insert(path_id, on_open_path_send);
+498                drop(state);
+499                OpenPath::new(path_id, on_open_path_recv, self.0.clone())
+500            }
+501            Err(err) => OpenPath::rejected(err),
+502        }
+503    }
+504
+505    /// Returns the [`Path`] structure of an open path
+506    pub fn path(&self, id: PathId) -> Option<Path> {
+507        Path::new(&self.0, id)
+508    }
+509
+510    /// A broadcast receiver of [`PathEvent`]s for all paths in this connection
+511    pub fn path_events(&self) -> tokio::sync::broadcast::Receiver<PathEvent> {
+512        self.0.state.lock("path_events").path_events.subscribe()
+513    }
+514
+515    /// A broadcast receiver of [`n0_nat_traversal::Event`]s for updates about server addresses
+516    pub fn nat_traversal_updates(
+517        &self,
+518    ) -> tokio::sync::broadcast::Receiver<n0_nat_traversal::Event> {
+519        self.0
+520            .state
+521            .lock("nat_traversal_updates")
+522            .nat_traversal_updates
+523            .subscribe()
+524    }
+525
+526    /// Wait for the connection to be closed for any reason
+527    ///
+528    /// Despite the return type's name, closed connections are often not an error condition at the
+529    /// application layer. Cases that might be routine include [`ConnectionError::LocallyClosed`]
+530    /// and [`ConnectionError::ApplicationClosed`].
+531    pub async fn closed(&self) -> ConnectionError {
+532        {
+533            let conn = self.0.state.lock("closed");
+534            if let Some(error) = conn.error.as_ref() {
+535                return error.clone();
+536            }
+537            // Construct the future while the lock is held to ensure we can't miss a wakeup if
+538            // the `Notify` is signaled immediately after we release the lock. `await` it after
+539            // the lock guard is out of scope.
+540            self.0.shared.closed.notified()
+541        }
+542        .await;
+543        self.0
+544            .state
+545            .lock("closed")
+546            .error
+547            .as_ref()
+548            .expect("closed without an error")
+549            .clone()
+550    }
+551
+552    /// Wait for the connection to be closed without keeping a strong reference to the connection
+553    ///
+554    /// Returns a future that resolves, once the connection is closed, to a tuple of
+555    /// ([`ConnectionError`], [`ConnectionStats`]).
+556    ///
+557    /// Calling [`Self::closed`] keeps the connection alive until it is either closed locally via [`Connection::close`]
+558    /// or closed by the remote peer. This function instead does not keep the connection itself alive,
+559    /// so if all *other* clones of the connection are dropped, the connection will be closed implicitly even
+560    /// if there are futures returned from this function still being awaited.
+561    pub fn on_closed(&self) -> OnClosed {
+562        let (tx, rx) = oneshot::channel();
+563        let mut state = self.0.state.lock("on_closed");
+564        if let Some(error) = &state.error {
+565            // Connection already closed, send immediately
+566            let _ = tx.send((error.clone(), state.inner.stats()));
+567        } else {
+568            state.on_closed.push(tx);
+569        }
+570        drop(state);
+571        OnClosed {
+572            conn: self.weak_handle(),
+573            rx,
+574        }
+575    }
+576
+577    /// If the connection is closed, the reason why.
+578    ///
+579    /// Returns `None` if the connection is still open.
+580    pub fn close_reason(&self) -> Option<ConnectionError> {
+581        self.0.state.lock("close_reason").error.clone()
+582    }
+583
+584    /// Close the connection immediately.
+585    ///
+586    /// Pending operations will fail immediately with [`ConnectionError::LocallyClosed`]. No
+587    /// more data is sent to the peer and the peer may drop buffered data upon receiving
+588    /// the CONNECTION_CLOSE frame.
+589    ///
+590    /// `error_code` and `reason` are not interpreted, and are provided directly to the peer.
+591    ///
+592    /// `reason` will be truncated to fit in a single packet with overhead; to improve odds that it
+593    /// is preserved in full, it should be kept under 1KiB.
+594    ///
+595    /// # Gracefully closing a connection
+596    ///
+597    /// Only the peer last receiving application data can be certain that all data is
+598    /// delivered. The only reliable action it can then take is to close the connection,
+599    /// potentially with a custom error code. The delivery of the final CONNECTION_CLOSE
+600    /// frame is very likely if both endpoints stay online long enough, and
+601    /// [`Endpoint::wait_idle()`] can be used to provide sufficient time. Otherwise, the
+602    /// remote peer will time out the connection, provided that the idle timeout is not
+603    /// disabled.
+604    ///
+605    /// The sending side can not guarantee all stream data is delivered to the remote
+606    /// application. It only knows the data is delivered to the QUIC stack of the remote
+607    /// endpoint. Once the local side sends a CONNECTION_CLOSE frame in response to calling
+608    /// [`close()`] the remote endpoint may drop any data it received but is as yet
+609    /// undelivered to the application, including data that was acknowledged as received to
+610    /// the local endpoint.
+611    ///
+612    /// [`ConnectionError::LocallyClosed`]: crate::ConnectionError::LocallyClosed
+613    /// [`Endpoint::wait_idle()`]: crate::Endpoint::wait_idle
+614    /// [`close()`]: Connection::close
+615    pub fn close(&self, error_code: VarInt, reason: &[u8]) {
+616        let conn = &mut *self.0.state.lock("close");
+617        conn.close(error_code, Bytes::copy_from_slice(reason), &self.0.shared);
+618    }
+619
+620    /// Wait for the handshake to be confirmed.
+621    ///
+622    /// As a server, who must be authenticated by clients,
+623    /// this happens when the handshake completes
+624    /// upon receiving a TLS Finished message from the client.
+625    /// In return, the server send a HANDSHAKE_DONE frame.
+626    ///
+627    /// As a client, this happens when receiving a HANDSHAKE_DONE frame.
+628    /// At this point, the server has either accepted our authentication,
+629    /// or, if client authentication is not required, accepted our lack of authentication.
+630    pub async fn handshake_confirmed(&self) -> Result<(), ConnectionError> {
+631        {
+632            let conn = self.0.state.lock("handshake_confirmed");
+633            if let Some(error) = conn.error.as_ref() {
+634                return Err(error.clone());
+635            }
+636            if conn.handshake_confirmed {
+637                return Ok(());
+638            }
+639            // Construct the future while the lock is held to ensure we can't miss a wakeup if
+640            // the `Notify` is signaled immediately after we release the lock. `await` it after
+641            // the lock guard is out of scope.
+642            self.0.shared.handshake_confirmed.notified()
+643        }
+644        .await;
+645        if let Some(error) = self.0.state.lock("handshake_confirmed").error.as_ref() {
+646            Err(error.clone())
+647        } else {
+648            Ok(())
+649        }
+650    }
+651
+652    /// Transmit `data` as an unreliable, unordered application datagram
+653    ///
+654    /// Application datagrams are a low-level primitive. They may be lost or delivered out of order,
+655    /// and `data` must both fit inside a single QUIC packet and be smaller than the maximum
+656    /// dictated by the peer.
+657    ///
+658    /// Previously queued datagrams which are still unsent may be discarded to make space for this
+659    /// datagram, in order of oldest to newest.
+660    pub fn send_datagram(&self, data: Bytes) -> Result<(), SendDatagramError> {
+661        let conn = &mut *self.0.state.lock("send_datagram");
+662        if let Some(ref x) = conn.error {
+663            return Err(SendDatagramError::ConnectionLost(x.clone()));
+664        }
+665        use proto::SendDatagramError::*;
+666        match conn.inner.datagrams().send(data, true) {
+667            Ok(()) => {
+668                conn.wake();
+669                Ok(())
+670            }
+671            Err(e) => Err(match e {
+672                Blocked(..) => unreachable!(),
+673                UnsupportedByPeer => SendDatagramError::UnsupportedByPeer,
+674                Disabled => SendDatagramError::Disabled,
+675                TooLarge => SendDatagramError::TooLarge,
+676            }),
+677        }
+678    }
+679
+680    /// Transmit `data` as an unreliable, unordered application datagram
+681    ///
+682    /// Unlike [`send_datagram()`], this method will wait for buffer space during congestion
+683    /// conditions, which effectively prioritizes old datagrams over new datagrams.
+684    ///
+685    /// See [`send_datagram()`] for details.
+686    ///
+687    /// [`send_datagram()`]: Connection::send_datagram
+688    pub fn send_datagram_wait(&self, data: Bytes) -> SendDatagram<'_> {
+689        SendDatagram {
+690            conn: &self.0,
+691            data: Some(data),
+692            notify: self.0.shared.datagrams_unblocked.notified(),
+693        }
+694    }
+695
+696    /// Compute the maximum size of datagrams that may be passed to [`send_datagram()`].
+697    ///
+698    /// Returns `None` if datagrams are unsupported by the peer or disabled locally.
+699    ///
+700    /// This may change over the lifetime of a connection according to variation in the path MTU
+701    /// estimate. The peer can also enforce an arbitrarily small fixed limit, but if the peer's
+702    /// limit is large this is guaranteed to be a little over a kilobyte at minimum.
+703    ///
+704    /// Not necessarily the maximum size of received datagrams.
+705    ///
+706    /// [`send_datagram()`]: Connection::send_datagram
+707    pub fn max_datagram_size(&self) -> Option<usize> {
+708        self.0
+709            .state
+710            .lock("max_datagram_size")
+711            .inner
+712            .datagrams()
+713            .max_size()
+714    }
+715
+716    /// Bytes available in the outgoing datagram buffer
+717    ///
+718    /// When greater than zero, calling [`send_datagram()`](Self::send_datagram) with a datagram of
+719    /// at most this size is guaranteed not to cause older datagrams to be dropped.
+720    pub fn datagram_send_buffer_space(&self) -> usize {
+721        self.0
+722            .state
+723            .lock("datagram_send_buffer_space")
+724            .inner
+725            .datagrams()
+726            .send_buffer_space()
+727    }
+728
+729    /// The side of the connection (client or server)
+730    pub fn side(&self) -> Side {
+731        self.0.state.lock("side").inner.side()
+732    }
+733
+734    /// The peer's UDP address
+735    ///
+736    /// If [`ServerConfig::migration`] is `true`, clients may change addresses at will,
+737    /// e.g. when switching to a cellular internet connection.
+738    ///
+739    /// If [`multipath`] is enabled this will return the address of *any*
+740    /// path, and may not be consistent. Prefer [`Path::remote_address`] instead.
+741    ///
+742    /// [`ServerConfig::migration`]: crate::ServerConfig::migration
+743    /// [`multipath`]: crate::TransportConfig::max_concurrent_multipath_paths
+744    pub fn remote_address(&self) -> SocketAddr {
+745        // TODO: an unwrap again
+746        let state = self.0.state.lock("remote_address");
+747        state
+748            .inner
+749            .paths()
+750            .iter()
+751            .filter_map(|id| state.inner.network_path(*id).ok())
+752            .next()
+753            .unwrap()
+754            .remote
+755    }
+756
+757    /// The local IP address which was used when the peer established
+758    /// the connection
+759    ///
+760    /// This can be different from the address the endpoint is bound to, in case
+761    /// the endpoint is bound to a wildcard address like `0.0.0.0` or `::`.
+762    ///
+763    /// This will return `None` for clients, or when the platform does not expose this
+764    /// information. See [`noq_udp::RecvMeta::dst_ip`](udp::RecvMeta::dst_ip) for a list of
+765    /// supported platforms when using [`noq_udp`](udp) for I/O, which is the default.
+766    pub fn local_ip(&self) -> Option<IpAddr> {
+767        // TODO: an unwrap again
+768        let state = self.0.state.lock("remote_address");
+769        state
+770            .inner
+771            .paths()
+772            .iter()
+773            .filter_map(|id| state.inner.network_path(*id).ok())
+774            .next()
+775            .unwrap()
+776            .local_ip
+777    }
+778
+779    /// Current best estimate of this connection's latency (round-trip-time)
+780    pub fn rtt(&self, path_id: PathId) -> Option<Duration> {
+781        self.0.state.lock("rtt").inner.rtt(path_id)
+782    }
+783
+784    /// Returns connection statistics
+785    pub fn stats(&self) -> ConnectionStats {
+786        self.0.state.lock("stats").inner.stats()
+787    }
+788
+789    /// Returns path statistics
+790    pub fn path_stats(&self, path_id: PathId) -> Option<PathStats> {
+791        self.0.state.lock("path_stats").path_stats(path_id)
+792    }
+793
+794    /// Current state of the congestion control algorithm, for debugging purposes
+795    pub fn congestion_state(&self, path_id: PathId) -> Option<Box<dyn Controller>> {
+796        self.0
+797            .state
+798            .lock("congestion_state")
+799            .inner
+800            .congestion_state(path_id)
+801            .map(|c| c.clone_box())
+802    }
+803
+804    /// Parameters negotiated during the handshake
+805    ///
+806    /// Guaranteed to return `Some` on fully established connections or after
+807    /// [`Connecting::handshake_data()`] succeeds. See that method's documentations for details on
+808    /// the returned value.
+809    ///
+810    /// [`Connection::handshake_data()`]: crate::Connecting::handshake_data
+811    pub fn handshake_data(&self) -> Option<Box<dyn Any>> {
+812        self.0
+813            .state
+814            .lock("handshake_data")
+815            .inner
+816            .crypto_session()
+817            .handshake_data()
+818    }
+819
+820    /// Cryptographic identity of the peer
+821    ///
+822    /// The dynamic type returned is determined by the configured
+823    /// [`Session`](proto::crypto::Session). For the default `rustls` session, the return value can
+824    /// be [`downcast`](Box::downcast) to a <code>Vec<[rustls::pki_types::CertificateDer]></code>
+825    pub fn peer_identity(&self) -> Option<Box<dyn Any>> {
+826        self.0
+827            .state
+828            .lock("peer_identity")
+829            .inner
+830            .crypto_session()
+831            .peer_identity()
+832    }
+833
+834    /// A stable identifier for this connection
+835    ///
+836    /// Peer addresses and connection IDs can change, but this value will remain
+837    /// fixed for the lifetime of the connection.
+838    pub fn stable_id(&self) -> usize {
+839        self.0.stable_id()
+840    }
+841
+842    /// Update traffic keys spontaneously
+843    ///
+844    /// This primarily exists for testing purposes.
+845    pub fn force_key_update(&self) {
+846        self.0
+847            .state
+848            .lock("force_key_update")
+849            .inner
+850            .force_key_update()
+851    }
+852
+853    /// Derive keying material from this connection's TLS session secrets.
+854    ///
+855    /// When both peers call this method with the same `label` and `context`
+856    /// arguments and `output` buffers of equal length, they will get the
+857    /// same sequence of bytes in `output`. These bytes are cryptographically
+858    /// strong and pseudorandom, and are suitable for use as keying material.
+859    ///
+860    /// See [RFC5705](https://tools.ietf.org/html/rfc5705) for more information.
+861    pub fn export_keying_material(
+862        &self,
+863        output: &mut [u8],
+864        label: &[u8],
+865        context: &[u8],
+866    ) -> Result<(), proto::crypto::ExportKeyingMaterialError> {
+867        self.0
+868            .state
+869            .lock("export_keying_material")
+870            .inner
+871            .crypto_session()
+872            .export_keying_material(output, label, context)
+873    }
+874
+875    /// Modify the number of remotely initiated unidirectional streams that may be concurrently open
+876    ///
+877    /// No streams may be opened by the peer unless fewer than `count` are already open. Large
+878    /// `count`s increase both minimum and worst-case memory consumption.
+879    pub fn set_max_concurrent_uni_streams(&self, count: VarInt) {
+880        let mut conn = self.0.state.lock("set_max_concurrent_uni_streams");
+881        conn.inner.set_max_concurrent_streams(Dir::Uni, count);
+882        // May need to send MAX_STREAMS to make progress
+883        conn.wake();
+884    }
+885
+886    /// See [`proto::TransportConfig::send_window()`]
+887    pub fn set_send_window(&self, send_window: u64) {
+888        let mut conn = self.0.state.lock("set_send_window");
+889        conn.inner.set_send_window(send_window);
+890        conn.wake();
+891    }
+892
+893    /// See [`proto::TransportConfig::receive_window()`]
+894    pub fn set_receive_window(&self, receive_window: VarInt) {
+895        let mut conn = self.0.state.lock("set_receive_window");
+896        conn.inner.set_receive_window(receive_window);
+897        conn.wake();
+898    }
+899
+900    /// Modify the number of remotely initiated bidirectional streams that may be concurrently open
+901    ///
+902    /// No streams may be opened by the peer unless fewer than `count` are already open. Large
+903    /// `count`s increase both minimum and worst-case memory consumption.
+904    pub fn set_max_concurrent_bi_streams(&self, count: VarInt) {
+905        let mut conn = self.0.state.lock("set_max_concurrent_bi_streams");
+906        conn.inner.set_max_concurrent_streams(Dir::Bi, count);
+907        // May need to send MAX_STREAMS to make progress
+908        conn.wake();
+909    }
+910
+911    /// Track changed on our external address as reported by the peer.
+912    pub fn observed_external_addr(&self) -> watch::Receiver<Option<SocketAddr>> {
+913        let conn = self.0.state.lock("external_addr");
+914        conn.observed_external_addr.subscribe()
+915    }
+916
+917    /// Is multipath enabled?
+918    // TODO(flub): not a useful API, once we do real things with multipath we can remove
+919    // this again.
+920    pub fn is_multipath_enabled(&self) -> bool {
+921        let conn = self.0.state.lock("is_multipath_enabled");
+922        conn.inner.is_multipath_negotiated()
+923    }
+924
+925    /// Registers one address at which this endpoint might be reachable
+926    ///
+927    /// When the NAT traversal extension is negotiated, servers send these addresses to clients in
+928    /// `ADD_ADDRESS` frames. This allows clients to obtain server address candidates to initiate
+929    /// NAT traversal attempts. Clients provide their own reachable addresses in `REACH_OUT` frames
+930    /// when [`Self::initiate_nat_traversal_round`] is called.
+931    pub fn add_nat_traversal_address(
+932        &self,
+933        address: SocketAddr,
+934    ) -> Result<(), n0_nat_traversal::Error> {
+935        let mut conn = self.0.state.lock("add_nat_traversal_addresses");
+936        conn.inner.add_nat_traversal_address(address)
+937    }
+938
+939    /// Removes one or more addresses from the set of addresses at which this endpoint is reachable
+940    ///
+941    /// When the NAT traversal extension is negotiated, servers send address removals to
+942    /// clients in `REMOVE_ADDRESS` frames. This allows clients to stop using outdated
+943    /// server address candidates that are no longer valid for NAT traversal.
+944    ///
+945    /// For clients, removed addresses will no longer be advertised in `REACH_OUT` frames.
+946    ///
+947    /// Addresses not present in the set will be silently ignored.
+948    pub fn remove_nat_traversal_address(
+949        &self,
+950        address: SocketAddr,
+951    ) -> Result<(), n0_nat_traversal::Error> {
+952        let mut conn = self.0.state.lock("remove_nat_traversal_addresses");
+953        conn.inner.remove_nat_traversal_address(address)
+954    }
+955
+956    /// Get the current local nat traversal addresses
+957    pub fn get_local_nat_traversal_addresses(
+958        &self,
+959    ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> {
+960        let conn = self.0.state.lock("get_local_nat_traversal_addresses");
+961        conn.inner.get_local_nat_traversal_addresses()
+962    }
+963
+964    /// Get the currently advertised nat traversal addresses by the server
+965    pub fn get_remote_nat_traversal_addresses(
+966        &self,
+967    ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> {
+968        let conn = self.0.state.lock("get_remote_nat_traversal_addresses");
+969        conn.inner.get_remote_nat_traversal_addresses()
+970    }
+971
+972    /// Initiates a new nat traversal round
+973    ///
+974    /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT`
+975    /// frames, and initiating probing of the known remote addresses. When a new round is
+976    /// initiated, the previous one is cancelled, and paths that have not been opened are closed.
+977    ///
+978    /// Returns the server addresses that are now being probed.
+979    pub fn initiate_nat_traversal_round(&self) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> {
+980        let mut conn = self.0.state.lock("initiate_nat_traversal_round");
+981        let now = conn.runtime.now();
+982        conn.inner.initiate_nat_traversal_round(now)
+983    }
+984}
+985
+986pin_project! {
+987    /// Future produced by [`Connection::open_uni`]
+988    pub struct OpenUni<'a> {
+989        conn: &'a ConnectionRef,
+990        #[pin]
+991        notify: Notified<'a>,
+992    }
+993}
+994
+995impl Future for OpenUni<'_> {
+996    type Output = Result<SendStream, ConnectionError>;
+997    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+998        let this = self.project();
+999        let (conn, id, is_0rtt) = ready!(poll_open(ctx, this.conn, this.notify, Dir::Uni))?;
+1000        Poll::Ready(Ok(SendStream::new(conn, id, is_0rtt)))
+1001    }
+1002}
+1003
+1004pin_project! {
+1005    /// Future produced by [`Connection::open_bi`]
+1006    pub struct OpenBi<'a> {
+1007        conn: &'a ConnectionRef,
+1008        #[pin]
+1009        notify: Notified<'a>,
+1010    }
+1011}
+1012
+1013impl Future for OpenBi<'_> {
+1014    type Output = Result<(SendStream, RecvStream), ConnectionError>;
+1015    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1016        let this = self.project();
+1017        let (conn, id, is_0rtt) = ready!(poll_open(ctx, this.conn, this.notify, Dir::Bi))?;
+1018
+1019        Poll::Ready(Ok((
+1020            SendStream::new(conn.clone(), id, is_0rtt),
+1021            RecvStream::new(conn, id, is_0rtt),
+1022        )))
+1023    }
+1024}
+1025
+1026fn poll_open<'a>(
+1027    ctx: &mut Context<'_>,
+1028    conn: &'a ConnectionRef,
+1029    mut notify: Pin<&mut Notified<'a>>,
+1030    dir: Dir,
+1031) -> Poll<Result<(ConnectionRef, StreamId, bool), ConnectionError>> {
+1032    let mut state = conn.state.lock("poll_open");
+1033    if let Some(ref e) = state.error {
+1034        return Poll::Ready(Err(e.clone()));
+1035    } else if let Some(id) = state.inner.streams().open(dir) {
+1036        let is_0rtt = state.inner.side().is_client() && state.inner.is_handshaking();
+1037        drop(state); // Release the lock so clone can take it
+1038        return Poll::Ready(Ok((conn.clone(), id, is_0rtt)));
+1039    }
+1040    loop {
+1041        match notify.as_mut().poll(ctx) {
+1042            // `state` lock ensures we didn't race with readiness
+1043            Poll::Pending => return Poll::Pending,
+1044            // Spurious wakeup, get a new future
+1045            Poll::Ready(()) => {
+1046                notify.set(conn.shared.stream_budget_available[dir as usize].notified())
+1047            }
+1048        }
+1049    }
+1050}
+1051
+1052pin_project! {
+1053    /// Future produced by [`Connection::accept_uni`]
+1054    pub struct AcceptUni<'a> {
+1055        conn: &'a ConnectionRef,
+1056        #[pin]
+1057        notify: Notified<'a>,
+1058    }
+1059}
+1060
+1061impl Future for AcceptUni<'_> {
+1062    type Output = Result<RecvStream, ConnectionError>;
+1063
+1064    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1065        let this = self.project();
+1066        let (conn, id, is_0rtt) = ready!(poll_accept(ctx, this.conn, this.notify, Dir::Uni))?;
+1067        Poll::Ready(Ok(RecvStream::new(conn, id, is_0rtt)))
+1068    }
+1069}
+1070
+1071pin_project! {
+1072    /// Future produced by [`Connection::accept_bi`]
+1073    pub struct AcceptBi<'a> {
+1074        conn: &'a ConnectionRef,
+1075        #[pin]
+1076        notify: Notified<'a>,
+1077    }
+1078}
+1079
+1080impl Future for AcceptBi<'_> {
+1081    type Output = Result<(SendStream, RecvStream), ConnectionError>;
+1082
+1083    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1084        let this = self.project();
+1085        let (conn, id, is_0rtt) = ready!(poll_accept(ctx, this.conn, this.notify, Dir::Bi))?;
+1086        Poll::Ready(Ok((
+1087            SendStream::new(conn.clone(), id, is_0rtt),
+1088            RecvStream::new(conn, id, is_0rtt),
+1089        )))
+1090    }
+1091}
+1092
+1093fn poll_accept<'a>(
+1094    ctx: &mut Context<'_>,
+1095    conn: &'a ConnectionRef,
+1096    mut notify: Pin<&mut Notified<'a>>,
+1097    dir: Dir,
+1098) -> Poll<Result<(ConnectionRef, StreamId, bool), ConnectionError>> {
+1099    let mut state = conn.state.lock("poll_accept");
+1100    // Check for incoming streams before checking `state.error` so that already-received streams,
+1101    // which are necessarily finite, can be drained from a closed connection.
+1102    if let Some(id) = state.inner.streams().accept(dir) {
+1103        let is_0rtt = state.inner.is_handshaking();
+1104        state.wake(); // To send additional stream ID credit
+1105        drop(state); // Release the lock so clone can take it
+1106        return Poll::Ready(Ok((conn.clone(), id, is_0rtt)));
+1107    } else if let Some(ref e) = state.error {
+1108        return Poll::Ready(Err(e.clone()));
+1109    }
+1110    loop {
+1111        match notify.as_mut().poll(ctx) {
+1112            // `state` lock ensures we didn't race with readiness
+1113            Poll::Pending => return Poll::Pending,
+1114            // Spurious wakeup, get a new future
+1115            Poll::Ready(()) => notify.set(conn.shared.stream_incoming[dir as usize].notified()),
+1116        }
+1117    }
+1118}
+1119
+1120pin_project! {
+1121    /// Future produced by [`Connection::read_datagram`]
+1122    pub struct ReadDatagram<'a> {
+1123        conn: &'a ConnectionRef,
+1124        #[pin]
+1125        notify: Notified<'a>,
+1126    }
+1127}
+1128
+1129impl Future for ReadDatagram<'_> {
+1130    type Output = Result<Bytes, ConnectionError>;
+1131    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1132        let mut this = self.project();
+1133        let mut state = this.conn.state.lock("ReadDatagram::poll");
+1134        // Check for buffered datagrams before checking `state.error` so that already-received
+1135        // datagrams, which are necessarily finite, can be drained from a closed connection.
+1136        if let Some(x) = state.inner.datagrams().recv() {
+1137            return Poll::Ready(Ok(x));
+1138        } else if let Some(ref e) = state.error {
+1139            return Poll::Ready(Err(e.clone()));
+1140        }
+1141        loop {
+1142            match this.notify.as_mut().poll(ctx) {
+1143                // `state` lock ensures we didn't race with readiness
+1144                Poll::Pending => return Poll::Pending,
+1145                // Spurious wakeup, get a new future
+1146                Poll::Ready(()) => this
+1147                    .notify
+1148                    .set(this.conn.shared.datagram_received.notified()),
+1149            }
+1150        }
+1151    }
+1152}
+1153
+1154pin_project! {
+1155    /// Future produced by [`Connection::send_datagram_wait`]
+1156    pub struct SendDatagram<'a> {
+1157        conn: &'a ConnectionRef,
+1158        data: Option<Bytes>,
+1159        #[pin]
+1160        notify: Notified<'a>,
+1161    }
+1162}
+1163
+1164impl Future for SendDatagram<'_> {
+1165    type Output = Result<(), SendDatagramError>;
+1166    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1167        let mut this = self.project();
+1168        let mut state = this.conn.state.lock("SendDatagram::poll");
+1169        if let Some(ref e) = state.error {
+1170            return Poll::Ready(Err(SendDatagramError::ConnectionLost(e.clone())));
+1171        }
+1172        use proto::SendDatagramError::*;
+1173        match state
+1174            .inner
+1175            .datagrams()
+1176            .send(this.data.take().unwrap(), false)
+1177        {
+1178            Ok(()) => {
+1179                state.wake();
+1180                Poll::Ready(Ok(()))
+1181            }
+1182            Err(e) => Poll::Ready(Err(match e {
+1183                Blocked(data) => {
+1184                    this.data.replace(data);
+1185                    loop {
+1186                        match this.notify.as_mut().poll(ctx) {
+1187                            Poll::Pending => return Poll::Pending,
+1188                            // Spurious wakeup, get a new future
+1189                            Poll::Ready(()) => this
+1190                                .notify
+1191                                .set(this.conn.shared.datagrams_unblocked.notified()),
+1192                        }
+1193                    }
+1194                }
+1195                UnsupportedByPeer => SendDatagramError::UnsupportedByPeer,
+1196                Disabled => SendDatagramError::Disabled,
+1197                TooLarge => SendDatagramError::TooLarge,
+1198            })),
+1199        }
+1200    }
+1201}
+1202
+1203/// Future returned by [`Connection::on_closed`]
+1204///
+1205/// Resolves to a tuple of ([`ConnectionError`], [`ConnectionStats`]).
+1206pub struct OnClosed {
+1207    rx: oneshot::Receiver<(ConnectionError, ConnectionStats)>,
+1208    conn: WeakConnectionHandle,
+1209}
+1210
+1211impl Drop for OnClosed {
+1212    fn drop(&mut self) {
+1213        if self.rx.is_terminated() {
+1214            return;
+1215        };
+1216        if let Some(conn) = self.conn.upgrade() {
+1217            self.rx.close();
+1218            conn.0
+1219                .state
+1220                .lock("OnClosed::drop")
+1221                .on_closed
+1222                .retain(|tx| !tx.is_closed());
+1223        }
+1224    }
+1225}
+1226
+1227impl Future for OnClosed {
+1228    type Output = (ConnectionError, ConnectionStats);
+1229
+1230    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+1231        let this = self.get_mut();
+1232        // The `expect` is safe because `State::drop` ensures that all senders are triggered
+1233        // before being dropped.
+1234        Pin::new(&mut this.rx)
+1235            .poll(cx)
+1236            .map(|x| x.expect("on_close sender is never dropped before sending"))
+1237    }
+1238}
+1239
+1240#[derive(Debug)]
+1241#[allow(clippy::redundant_allocation)]
+1242pub(crate) struct ConnectionRef(Arc<Arc<ConnectionInner>>);
+1243
+1244impl ConnectionRef {
+1245    #[allow(clippy::redundant_allocation)]
+1246    fn from_arc(inner: Arc<Arc<ConnectionInner>>) -> Self {
+1247        inner.state.lock("from_arc").ref_count += 1;
+1248        Self(inner)
+1249    }
+1250
+1251    pub(crate) fn stable_id(&self) -> usize {
+1252        &*self.0 as *const _ as usize
+1253    }
+1254
+1255    pub(crate) fn weak_handle(&self) -> WeakConnectionHandle {
+1256        WeakConnectionHandle(Arc::downgrade(&self.0))
+1257    }
+1258}
+1259
+1260impl Clone for ConnectionRef {
+1261    fn clone(&self) -> Self {
+1262        Self::from_arc(Arc::clone(&self.0))
+1263    }
+1264}
+1265
+1266impl Drop for ConnectionRef {
+1267    fn drop(&mut self) {
+1268        let conn = &mut *self.state.lock("drop");
+1269        if let Some(x) = conn.ref_count.checked_sub(1) {
+1270            conn.ref_count = x;
+1271            if x == 0 && !conn.inner.is_closed() {
+1272                // If the driver is alive, it's just it and us, so we'd better shut it down. If it's
+1273                // not, we can't do any harm. If there were any streams being opened, then either
+1274                // the connection will be closed for an unrelated reason or a fresh reference will
+1275                // be constructed for the newly opened stream.
+1276                conn.implicit_close(&self.shared);
+1277            }
+1278        }
+1279    }
+1280}
+1281
+1282impl std::ops::Deref for ConnectionRef {
+1283    type Target = ConnectionInner;
+1284    fn deref(&self) -> &Self::Target {
+1285        &self.0
+1286    }
+1287}
+1288
+1289#[derive(Debug)]
+1290pub(crate) struct ConnectionInner {
+1291    pub(crate) state: Mutex<State>,
+1292    pub(crate) shared: Shared,
+1293}
+1294
+1295/// A handle to some connection internals, use with care.
+1296///
+1297/// This contains a weak reference to the connection so will not itself keep the connection
+1298/// alive.
+1299#[derive(Debug, Clone)]
+1300pub struct WeakConnectionHandle(Weak<Arc<ConnectionInner>>);
+1301
+1302impl WeakConnectionHandle {
+1303    /// Returns `true` if the [`Connection`] associated with this handle is still alive.
+1304    pub fn is_alive(&self) -> bool {
+1305        self.0.upgrade().is_some()
+1306    }
+1307
+1308    /// Upgrade the handle to a full `Connection`
+1309    pub fn upgrade(&self) -> Option<Connection> {
+1310        self.upgrade_to_ref().map(Connection)
+1311    }
+1312
+1313    pub(crate) fn upgrade_to_ref(&self) -> Option<ConnectionRef> {
+1314        self.0.upgrade().map(ConnectionRef::from_arc)
+1315    }
+1316
+1317    /// Returns `true` if the two [`WeakConnectionHandle`] point at the same connection.
+1318    pub fn is_same_connection(&self, other: &Self) -> bool {
+1319        self.0.ptr_eq(&other.0)
+1320    }
+1321}
+1322
+1323#[derive(Debug, Default)]
+1324pub(crate) struct Shared {
+1325    handshake_confirmed: Notify,
+1326    /// Notified when new streams may be locally initiated due to an increase in stream ID flow
+1327    /// control budget
+1328    stream_budget_available: [Notify; 2],
+1329    /// Notified when the peer has initiated a new stream
+1330    stream_incoming: [Notify; 2],
+1331    datagram_received: Notify,
+1332    datagrams_unblocked: Notify,
+1333    closed: Notify,
+1334}
+1335
+1336pub(crate) struct State {
+1337    pub(crate) inner: proto::Connection,
+1338    driver: Option<Waker>,
+1339    handle: ConnectionHandle,
+1340    on_handshake_data: Option<oneshot::Sender<()>>,
+1341    on_connected: Option<oneshot::Sender<bool>>,
+1342    connected: bool,
+1343    handshake_confirmed: bool,
+1344    timer: Option<Pin<Box<dyn AsyncTimer>>>,
+1345    timer_deadline: Option<Instant>,
+1346    conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
+1347    endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+1348    pub(crate) blocked_writers: FxHashMap<StreamId, Waker>,
+1349    pub(crate) blocked_readers: FxHashMap<StreamId, Waker>,
+1350    pub(crate) stopped: FxHashMap<StreamId, Arc<Notify>>,
+1351    /// Always set to Some before the connection becomes drained
+1352    pub(crate) error: Option<ConnectionError>,
+1353    /// Tracks paths being opened
+1354    open_path: FxHashMap<PathId, watch::Sender<Result<(), PathError>>>,
+1355    /// Tracks reference counts for paths.
+1356    ///
+1357    /// I.e. how many [`Path`] and [`WeakPathHandle`] structs are alive for a path.
+1358    ///
+1359    /// [`WeakPathHandle`]: crate::path::WeakPathHandle
+1360    pub(crate) path_refs: FxHashMap<PathId, usize>,
+1361    /// Final path stats for discarded paths.
+1362    ///
+1363    /// We only insert entries if the discarded path has a non-zero reference count in [`Self::path_refs`].
+1364    /// When the last reference to a path is dropped via [`Self::decrement_path_refs`] its value is cleared.
+1365    pub(crate) final_path_stats: FxHashMap<PathId, PathStats>,
+1366    pub(crate) path_events: tokio::sync::broadcast::Sender<PathEvent>,
+1367    /// Number of live handles that can be used to initiate or handle I/O; excludes the driver
+1368    ref_count: usize,
+1369    sender: Pin<Box<dyn UdpSender>>,
+1370    pub(crate) runtime: Arc<dyn Runtime>,
+1371    send_buffer: Vec<u8>,
+1372    /// We buffer a transmit when the underlying I/O would block
+1373    buffered_transmit: Option<proto::Transmit>,
+1374    /// Our last external address reported by the peer. When multipath is enabled, this will be the
+1375    /// last report across all paths.
+1376    pub(crate) observed_external_addr: watch::Sender<Option<SocketAddr>>,
+1377    pub(crate) nat_traversal_updates: tokio::sync::broadcast::Sender<n0_nat_traversal::Event>,
+1378    on_closed: Vec<oneshot::Sender<(ConnectionError, ConnectionStats)>>,
+1379}
+1380
+1381impl State {
+1382    #[allow(clippy::too_many_arguments)]
+1383    fn new(
+1384        inner: proto::Connection,
+1385        handle: ConnectionHandle,
+1386        endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+1387        conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
+1388        on_handshake_data: oneshot::Sender<()>,
+1389        on_connected: oneshot::Sender<bool>,
+1390        sender: Pin<Box<dyn UdpSender>>,
+1391        runtime: Arc<dyn Runtime>,
+1392    ) -> Self {
+1393        Self {
+1394            inner,
+1395            driver: None,
+1396            handle,
+1397            on_handshake_data: Some(on_handshake_data),
+1398            on_connected: Some(on_connected),
+1399            connected: false,
+1400            handshake_confirmed: false,
+1401            timer: None,
+1402            timer_deadline: None,
+1403            conn_events,
+1404            endpoint_events,
+1405            blocked_writers: FxHashMap::default(),
+1406            blocked_readers: FxHashMap::default(),
+1407            stopped: FxHashMap::default(),
+1408            open_path: FxHashMap::default(),
+1409            error: None,
+1410            ref_count: 0,
+1411            sender,
+1412            runtime,
+1413            send_buffer: Vec::new(),
+1414            buffered_transmit: None,
+1415            path_events: tokio::sync::broadcast::channel(32).0,
+1416            observed_external_addr: watch::Sender::new(None),
+1417            nat_traversal_updates: tokio::sync::broadcast::channel(32).0,
+1418            on_closed: Vec::new(),
+1419            final_path_stats: Default::default(),
+1420            path_refs: Default::default(),
+1421        }
+1422    }
+1423
+1424    fn drive_transmit(&mut self, cx: &mut Context<'_>) -> io::Result<bool> {
+1425        let now = self.runtime.now();
+1426        let mut transmits = 0;
+1427
+1428        let max_datagrams = self
+1429            .sender
+1430            .max_transmit_segments()
+1431            .min(MAX_TRANSMIT_SEGMENTS);
+1432
+1433        loop {
+1434            // Retry the last transmit, or get a new one.
+1435            let t = match self.buffered_transmit.take() {
+1436                Some(t) => t,
+1437                None => {
+1438                    self.send_buffer.clear();
+1439                    match self
+1440                        .inner
+1441                        .poll_transmit(now, max_datagrams, &mut self.send_buffer)
+1442                    {
+1443                        Some(t) => {
+1444                            transmits += match t.segment_size {
+1445                                None => 1,
+1446                                Some(s) => t.size.div_ceil(s), // round up
+1447                            };
+1448                            t
+1449                        }
+1450                        None => break,
+1451                    }
+1452                }
+1453            };
+1454
+1455            let len = t.size;
+1456            match self
+1457                .sender
+1458                .as_mut()
+1459                .poll_send(&udp_transmit(&t, &self.send_buffer[..len]), cx)
+1460            {
+1461                Poll::Pending => {
+1462                    self.buffered_transmit = Some(t);
+1463                    return Ok(false);
+1464                }
+1465                Poll::Ready(Err(e)) => return Err(e),
+1466                Poll::Ready(Ok(())) => {}
+1467            }
+1468
+1469            if transmits >= MAX_TRANSMIT_DATAGRAMS {
+1470                // TODO: What isn't ideal here yet is that if we don't poll all
+1471                // datagrams that could be sent we don't go into the `app_limited`
+1472                // state and CWND continues to grow until we get here the next time.
+1473                // See https://github.com/quinn-rs/quinn/issues/1126
+1474                return Ok(true);
+1475            }
+1476        }
+1477
+1478        Ok(false)
+1479    }
+1480
+1481    fn forward_endpoint_events(&mut self) {
+1482        while let Some(event) = self.inner.poll_endpoint_events() {
+1483            // If the endpoint driver is gone, noop.
+1484            let _ = self.endpoint_events.send((self.handle, event));
+1485        }
+1486    }
+1487
+1488    /// If this returns `Err`, the endpoint is dead, so the driver should exit immediately.
+1489    fn process_conn_events(
+1490        &mut self,
+1491        shared: &Shared,
+1492        cx: &mut Context<'_>,
+1493    ) -> Result<(), ConnectionError> {
+1494        loop {
+1495            match self.conn_events.poll_recv(cx) {
+1496                Poll::Ready(Some(ConnectionEvent::Rebind(sender))) => {
+1497                    self.sender = sender;
+1498                    self.inner.handle_network_change(None, self.runtime.now());
+1499                }
+1500                Poll::Ready(Some(ConnectionEvent::LocalAddressChanged(hint))) => {
+1501                    self.inner
+1502                        .handle_network_change(hint.as_deref().map(|x| x as _), self.runtime.now());
+1503                }
+1504                Poll::Ready(Some(ConnectionEvent::Proto(event))) => {
+1505                    self.inner.handle_event(event);
+1506                }
+1507                Poll::Ready(Some(ConnectionEvent::Close { reason, error_code })) => {
+1508                    self.close(error_code, reason, shared);
+1509                }
+1510                Poll::Ready(None) => {
+1511                    return Err(ConnectionError::TransportError(TransportError::new(
+1512                        TransportErrorCode::INTERNAL_ERROR,
+1513                        "endpoint driver future was dropped".to_string(),
+1514                    )));
+1515                }
+1516                Poll::Pending => {
+1517                    return Ok(());
+1518                }
+1519            }
+1520        }
+1521    }
+1522
+1523    fn forward_app_events(&mut self, shared: &Shared) {
+1524        while let Some(event) = self.inner.poll() {
+1525            use proto::Event::*;
+1526            match event {
+1527                HandshakeDataReady => {
+1528                    if let Some(x) = self.on_handshake_data.take() {
+1529                        let _ = x.send(());
+1530                    }
+1531                }
+1532                Connected => {
+1533                    self.connected = true;
+1534                    if let Some(x) = self.on_connected.take() {
+1535                        // We don't care if the on-connected future was dropped
+1536                        let _ = x.send(self.inner.accepted_0rtt());
+1537                    }
+1538                    if self.inner.side().is_client() && !self.inner.accepted_0rtt() {
+1539                        // Wake up rejected 0-RTT streams so they can fail immediately with
+1540                        // `ZeroRttRejected` errors.
+1541                        wake_all(&mut self.blocked_writers);
+1542                        wake_all(&mut self.blocked_readers);
+1543                        wake_all_notify(&mut self.stopped);
+1544                    }
+1545                }
+1546                HandshakeConfirmed => {
+1547                    self.handshake_confirmed = true;
+1548                    shared.handshake_confirmed.notify_waiters();
+1549                }
+1550                ConnectionLost { reason } => {
+1551                    self.terminate(reason, shared);
+1552                }
+1553                Stream(StreamEvent::Writable { id }) => wake_stream(id, &mut self.blocked_writers),
+1554                Stream(StreamEvent::Opened { dir: Dir::Uni }) => {
+1555                    shared.stream_incoming[Dir::Uni as usize].notify_waiters();
+1556                }
+1557                Stream(StreamEvent::Opened { dir: Dir::Bi }) => {
+1558                    shared.stream_incoming[Dir::Bi as usize].notify_waiters();
+1559                }
+1560                DatagramReceived => {
+1561                    shared.datagram_received.notify_waiters();
+1562                }
+1563                DatagramsUnblocked => {
+1564                    shared.datagrams_unblocked.notify_waiters();
+1565                }
+1566                Stream(StreamEvent::Readable { id }) => wake_stream(id, &mut self.blocked_readers),
+1567                Stream(StreamEvent::Available { dir }) => {
+1568                    // Might mean any number of streams are ready, so we wake up everyone
+1569                    shared.stream_budget_available[dir as usize].notify_waiters();
+1570                }
+1571                Stream(StreamEvent::Finished { id }) => wake_stream_notify(id, &mut self.stopped),
+1572                Stream(StreamEvent::Stopped { id, .. }) => {
+1573                    wake_stream_notify(id, &mut self.stopped);
+1574                    wake_stream(id, &mut self.blocked_writers);
+1575                }
+1576                Path(ref evt @ PathEvent::ObservedAddr { addr: observed, .. }) => {
+1577                    self.path_events.send(evt.clone()).ok();
+1578                    self.observed_external_addr.send_if_modified(|addr| {
+1579                        let old = addr.replace(observed);
+1580                        old != *addr
+1581                    });
+1582                }
+1583                Path(ref evt @ PathEvent::Opened { id }) => {
+1584                    self.path_events.send(evt.clone()).ok();
+1585                    if let Some(sender) = self.open_path.remove(&id) {
+1586                        sender.send_modify(|value| *value = Ok(()));
+1587                    }
+1588                }
+1589                Path(evt @ PathEvent::Discarded { id, path_stats }) => {
+1590                    if self.path_refs.contains_key(&id) {
+1591                        self.final_path_stats.insert(id, path_stats);
+1592                    }
+1593                    self.path_events.send(evt).ok();
+1594                }
+1595                Path(ref evt @ PathEvent::Abandoned { id, .. }) => {
+1596                    if let Some(sender) = self.open_path.remove(&id) {
+1597                        // We don't care for the reason why this path was closed here, because semantically
+1598                        // all close reasons for a path that has not yet been opened equals to `ValidationFailed`.
+1599                        // With the noq API, there is no way to application-close a not-yet-opened path, so
+1600                        // `ApplicationClosed` cannot occur. And all other variants will only occur for paths
+1601                        // that have already been opened.
+1602                        // The previous iteration of this code had another event `PathEvent::LocallyClosed` which
+1603                        // contained a `PathError`, but that was only ever set to `ValidationFailed`.
+1604                        let error = PathError::ValidationFailed;
+1605                        sender.send_modify(|value| *value = Err(error));
+1606                    }
+1607                    // this will happen also for already opened paths
+1608                    self.path_events.send(evt.clone()).ok();
+1609                }
+1610                Path(evt @ PathEvent::RemoteStatus { .. }) => {
+1611                    self.path_events.send(evt).ok();
+1612                }
+1613                NatTraversal(update) => {
+1614                    self.nat_traversal_updates.send(update).ok();
+1615                }
+1616            }
+1617        }
+1618    }
+1619
+1620    fn drive_timer(&mut self, cx: &mut Context<'_>) -> bool {
+1621        // Check whether we need to (re)set the timer. If so, we must poll again to ensure the
+1622        // timer is registered with the runtime (and check whether it's already
+1623        // expired).
+1624        match self.inner.poll_timeout() {
+1625            Some(deadline) => {
+1626                if let Some(delay) = &mut self.timer {
+1627                    // There is no need to reset the tokio timer if the deadline
+1628                    // did not change
+1629                    if self
+1630                        .timer_deadline
+1631                        .map(|current_deadline| current_deadline != deadline)
+1632                        .unwrap_or(true)
+1633                    {
+1634                        delay.as_mut().reset(deadline);
+1635                    }
+1636                } else {
+1637                    self.timer = Some(self.runtime.new_timer(deadline));
+1638                }
+1639                // Store the actual expiration time of the timer
+1640                self.timer_deadline = Some(deadline);
+1641            }
+1642            None => {
+1643                self.timer_deadline = None;
+1644                return false;
+1645            }
+1646        }
+1647
+1648        if self.timer_deadline.is_none() {
+1649            return false;
+1650        }
+1651
+1652        let delay = self
+1653            .timer
+1654            .as_mut()
+1655            .expect("timer must exist in this state")
+1656            .as_mut();
+1657        if delay.poll(cx).is_pending() {
+1658            // Since there wasn't a timeout event, there is nothing new
+1659            // for the connection to do
+1660            return false;
+1661        }
+1662
+1663        // A timer expired, so the caller needs to check for
+1664        // new transmits, which might cause new timers to be set.
+1665        self.inner.handle_timeout(self.runtime.now());
+1666        self.timer_deadline = None;
+1667        true
+1668    }
+1669
+1670    /// Wake up a blocked `Driver` task to process I/O
+1671    pub(crate) fn wake(&mut self) {
+1672        if let Some(x) = self.driver.take() {
+1673            x.wake();
+1674        }
+1675    }
+1676
+1677    /// Used to wake up all blocked futures when the connection becomes closed for any reason
+1678    fn terminate(&mut self, reason: ConnectionError, shared: &Shared) {
+1679        self.error = Some(reason.clone());
+1680        if let Some(x) = self.on_handshake_data.take() {
+1681            let _ = x.send(());
+1682        }
+1683        wake_all(&mut self.blocked_writers);
+1684        wake_all(&mut self.blocked_readers);
+1685        shared.stream_budget_available[Dir::Uni as usize].notify_waiters();
+1686        shared.stream_budget_available[Dir::Bi as usize].notify_waiters();
+1687        shared.stream_incoming[Dir::Uni as usize].notify_waiters();
+1688        shared.stream_incoming[Dir::Bi as usize].notify_waiters();
+1689        shared.datagram_received.notify_waiters();
+1690        shared.datagrams_unblocked.notify_waiters();
+1691        if let Some(x) = self.on_connected.take() {
+1692            let _ = x.send(false);
+1693        }
+1694        shared.handshake_confirmed.notify_waiters();
+1695        wake_all_notify(&mut self.stopped);
+1696        shared.closed.notify_waiters();
+1697
+1698        // Send to the registered on_closed futures.
+1699        let stats = self.inner.stats();
+1700        for tx in self.on_closed.drain(..) {
+1701            tx.send((reason.clone(), stats.clone())).ok();
+1702        }
+1703    }
+1704
+1705    fn close(&mut self, error_code: VarInt, reason: Bytes, shared: &Shared) {
+1706        self.inner.close(self.runtime.now(), error_code, reason);
+1707        self.terminate(ConnectionError::LocallyClosed, shared);
+1708        self.wake();
+1709    }
+1710
+1711    /// Close for a reason other than the application's explicit request
+1712    pub(crate) fn implicit_close(&mut self, shared: &Shared) {
+1713        self.close(0u32.into(), Bytes::new(), shared);
+1714    }
+1715
+1716    pub(crate) fn check_0rtt(&self) -> Result<(), ()> {
+1717        if self.inner.is_handshaking()
+1718            || self.inner.accepted_0rtt()
+1719            || self.inner.side().is_server()
+1720        {
+1721            Ok(())
+1722        } else {
+1723            Err(())
+1724        }
+1725    }
+1726
+1727    /// Returns [`PathStats`] for a path, if available.
+1728    ///
+1729    /// This gets the stats from [`proto::Connection`]. If that returns `None`
+1730    /// it gets them from `Self::final_path_stats` instead.
+1731    pub(crate) fn path_stats(&mut self, path_id: PathId) -> Option<PathStats> {
+1732        self.inner
+1733            .path_stats(path_id)
+1734            .or_else(|| self.final_path_stats.get(&path_id).copied())
+1735    }
+1736
+1737    /// Increment the reference counter for a path in this connection.
+1738    ///
+1739    /// This counts how many [`Path`] or [`WeakPathHandle`] structs exist for a path.
+1740    /// Currently this is used to determine whether to store the final stats after a path is
+1741    /// abandoned.
+1742    ///
+1743    /// [`WeakPathHandle`]: crate::path::WeakPathHandle
+1744    pub(crate) fn increment_path_refs(&mut self, path_id: PathId) {
+1745        *self.path_refs.entry(path_id).or_default() += 1;
+1746    }
+1747
+1748    /// Decrement the reference counter for a path in this connection.
+1749    pub(crate) fn decrement_path_refs(&mut self, path_id: PathId) {
+1750        if let Some(refs) = self.path_refs.get_mut(&path_id) {
+1751            *refs = refs.saturating_sub(1);
+1752            if *refs == 0 {
+1753                self.path_refs.remove(&path_id);
+1754                self.final_path_stats.remove(&path_id);
+1755            }
+1756        }
+1757    }
+1758}
+1759
+1760impl Drop for State {
+1761    fn drop(&mut self) {
+1762        if !self.inner.is_drained() {
+1763            // Ensure the endpoint can tidy up
+1764            let _ = self
+1765                .endpoint_events
+1766                .send((self.handle, proto::EndpointEvent::drained()));
+1767        }
+1768
+1769        if !self.on_closed.is_empty() {
+1770            // Ensure that all on_closed oneshot senders are triggered before dropping.
+1771            let reason = self.error.as_ref().expect("closed without error reason");
+1772            let stats = self.inner.stats();
+1773            for tx in self.on_closed.drain(..) {
+1774                tx.send((reason.clone(), stats.clone())).ok();
+1775            }
+1776        }
+1777    }
+1778}
+1779
+1780impl fmt::Debug for State {
+1781    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+1782        f.debug_struct("State").field("inner", &self.inner).finish()
+1783    }
+1784}
+1785
+1786fn wake_stream(stream_id: StreamId, wakers: &mut FxHashMap<StreamId, Waker>) {
+1787    if let Some(waker) = wakers.remove(&stream_id) {
+1788        waker.wake();
+1789    }
+1790}
+1791
+1792fn wake_all(wakers: &mut FxHashMap<StreamId, Waker>) {
+1793    wakers.drain().for_each(|(_, waker)| waker.wake())
+1794}
+1795
+1796fn wake_stream_notify(stream_id: StreamId, wakers: &mut FxHashMap<StreamId, Arc<Notify>>) {
+1797    if let Some(notify) = wakers.remove(&stream_id) {
+1798        notify.notify_waiters()
+1799    }
+1800}
+1801
+1802fn wake_all_notify(wakers: &mut FxHashMap<StreamId, Arc<Notify>>) {
+1803    wakers
+1804        .drain()
+1805        .for_each(|(_, notify)| notify.notify_waiters())
+1806}
+1807
+1808/// Errors that can arise when sending a datagram
+1809#[derive(Debug, Error, Clone, Eq, PartialEq)]
+1810pub enum SendDatagramError {
+1811    /// The peer does not support receiving datagram frames
+1812    #[error("datagrams not supported by peer")]
+1813    UnsupportedByPeer,
+1814    /// Datagram support is disabled locally
+1815    #[error("datagram support disabled")]
+1816    Disabled,
+1817    /// The datagram is larger than the connection can currently accommodate
+1818    ///
+1819    /// Indicates that the path MTU minus overhead or the limit advertised by the peer has been
+1820    /// exceeded.
+1821    #[error("datagram too large")]
+1822    TooLarge,
+1823    /// The connection was lost
+1824    #[error("connection lost")]
+1825    ConnectionLost(#[from] ConnectionError),
+1826}
+1827
+1828/// The maximum amount of datagrams which will be produced in a single `drive_transmit` call
+1829///
+1830/// This limits the amount of CPU resources consumed by datagram generation,
+1831/// and allows other tasks (like receiving ACKs) to run in between.
+1832const MAX_TRANSMIT_DATAGRAMS: usize = 20;
+1833
+1834/// The maximum amount of datagrams that are sent in a single transmit
+1835///
+1836/// This can be lower than the maximum platform capabilities, to avoid excessive
+1837/// memory allocations when calling `poll_transmit()`. Benchmarks have shown
+1838/// that numbers around 10 are a good compromise.
+1839const MAX_TRANSMIT_SEGMENTS: NonZeroUsize = NonZeroUsize::new(10).expect("known");
\ No newline at end of file diff --git a/pr/509/docs/src/noq/endpoint.rs.html b/pr/509/docs/src/noq/endpoint.rs.html new file mode 100644 index 000000000..516703eda --- /dev/null +++ b/pr/509/docs/src/noq/endpoint.rs.html @@ -0,0 +1,968 @@ +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::{Arc, Mutex},
+12    task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
+13};
+14
+15#[cfg(all(
+16    not(wasm_browser),
+17    any(feature = "runtime-tokio", feature = "runtime-smol"),
+18    any(feature = "aws-lc-rs", feature = "ring"),
+19))]
+20use crate::runtime::default_runtime;
+21use crate::{
+22    Instant,
+23    runtime::{AsyncUdpSocket, Runtime, UdpSender},
+24    udp_transmit,
+25};
+26use bytes::{Bytes, BytesMut};
+27use pin_project_lite::pin_project;
+28use proto::{
+29    self as proto, ClientConfig, ConnectError, ConnectionError, ConnectionHandle, DatagramEvent,
+30    EndpointEvent, FourTuple, NetworkChangeHint, ServerConfig,
+31};
+32use rustc_hash::FxHashMap;
+33#[cfg(all(
+34    not(wasm_browser),
+35    any(feature = "runtime-tokio", feature = "runtime-smol"),
+36    any(feature = "aws-lc-rs", feature = "ring"),
+37))]
+38use socket2::{Domain, Protocol, Socket, Type};
+39use tokio::sync::{Notify, futures::Notified, mpsc};
+40use tracing::{Instrument, Span, trace};
+41use udp::{BATCH_SIZE, RecvMeta};
+42
+43use crate::{
+44    ConnectionEvent, EndpointConfig, IO_LOOP_BOUND, RECV_TIME_BOUND, VarInt,
+45    connection::Connecting, incoming::Incoming, work_limiter::WorkLimiter,
+46};
+47
+48/// A QUIC endpoint.
+49///
+50/// An endpoint corresponds to a single UDP socket, may host many connections, and may act as both
+51/// client and server for different connections.
+52///
+53/// May be cloned to obtain another handle to the same endpoint.
+54#[derive(Debug, Clone)]
+55pub struct Endpoint {
+56    pub(crate) inner: EndpointRef,
+57    runtime: Arc<dyn Runtime>,
+58}
+59
+60impl Endpoint {
+61    /// Helper to construct an endpoint for use with outgoing connections only
+62    ///
+63    /// Note that `addr` is the *local* address to bind to, which should usually be a wildcard
+64    /// address like `0.0.0.0:0` or `[::]:0`, which allow communication with any reachable IPv4 or
+65    /// IPv6 address respectively from an OS-assigned port.
+66    ///
+67    /// If an IPv6 address is provided, attempts to make the socket dual-stack so as to allow
+68    /// communication with both IPv4 and IPv6 addresses. As such, calling `Endpoint::client` with
+69    /// the address `[::]:0` is a reasonable default to maximize the ability to connect to other
+70    /// address. For example:
+71    ///
+72    /// ```
+73    /// noq::Endpoint::client((std::net::Ipv6Addr::UNSPECIFIED, 0).into());
+74    /// ```
+75    ///
+76    /// Some environments may not allow creation of dual-stack sockets, in which case an IPv6
+77    /// client will only be able to connect to IPv6 servers. An IPv4 client is never dual-stack.
+78    #[cfg(all(
+79        not(wasm_browser),
+80        any(feature = "runtime-tokio", feature = "runtime-smol"),
+81        any(feature = "aws-lc-rs", feature = "ring"), // `EndpointConfig::default()` is only available with these
+82    ))]
+83    pub fn client(addr: SocketAddr) -> io::Result<Self> {
+84        let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))?;
+85        if addr.is_ipv6()
+86            && let Err(e) = socket.set_only_v6(false)
+87        {
+88            tracing::debug!(%e, "unable to make socket dual-stack");
+89        }
+90        socket.bind(&addr.into())?;
+91        let runtime =
+92            default_runtime().ok_or_else(|| io::Error::other("no async runtime found"))?;
+93        Self::new_with_abstract_socket(
+94            EndpointConfig::default(),
+95            None,
+96            runtime.wrap_udp_socket(socket.into())?,
+97            runtime,
+98        )
+99    }
+100
+101    /// Returns relevant stats from this Endpoint
+102    pub fn stats(&self) -> EndpointStats {
+103        self.inner.state.lock().unwrap().stats
+104    }
+105
+106    /// Helper to construct an endpoint for use with both incoming and outgoing connections
+107    ///
+108    /// Platform defaults for dual-stack sockets vary. For example, any socket bound to a wildcard
+109    /// IPv6 address on Windows will not by default be able to communicate with IPv4
+110    /// addresses. Portable applications should bind an address that matches the family they wish to
+111    /// communicate within.
+112    #[cfg(all(
+113        not(wasm_browser),
+114        any(feature = "runtime-tokio", feature = "runtime-smol"),
+115        any(feature = "aws-lc-rs", feature = "ring"), // `EndpointConfig::default()` is only available with these
+116    ))]
+117    pub fn server(config: ServerConfig, addr: SocketAddr) -> io::Result<Self> {
+118        let socket = std::net::UdpSocket::bind(addr)?;
+119        let runtime =
+120            default_runtime().ok_or_else(|| io::Error::other("no async runtime found"))?;
+121        Self::new_with_abstract_socket(
+122            EndpointConfig::default(),
+123            Some(config),
+124            runtime.wrap_udp_socket(socket)?,
+125            runtime,
+126        )
+127    }
+128
+129    /// Construct an endpoint with arbitrary configuration and socket
+130    #[cfg(not(wasm_browser))]
+131    pub fn new(
+132        config: EndpointConfig,
+133        server_config: Option<ServerConfig>,
+134        socket: std::net::UdpSocket,
+135        runtime: Arc<dyn Runtime>,
+136    ) -> io::Result<Self> {
+137        let socket = runtime.wrap_udp_socket(socket)?;
+138        Self::new_with_abstract_socket(config, server_config, socket, runtime)
+139    }
+140
+141    /// Construct an endpoint with arbitrary configuration and pre-constructed abstract socket
+142    ///
+143    /// Useful when `socket` has additional state (e.g. sidechannels) attached for which shared
+144    /// ownership is needed.
+145    pub fn new_with_abstract_socket(
+146        config: EndpointConfig,
+147        server_config: Option<ServerConfig>,
+148        socket: Box<dyn AsyncUdpSocket>,
+149        runtime: Arc<dyn Runtime>,
+150    ) -> io::Result<Self> {
+151        let addr = socket.local_addr()?;
+152        let allow_mtud = !socket.may_fragment();
+153        let rc = EndpointRef::new(
+154            socket,
+155            proto::Endpoint::new(Arc::new(config), server_config.map(Arc::new), allow_mtud),
+156            addr.is_ipv6(),
+157            runtime.clone(),
+158        );
+159        let driver = EndpointDriver(rc.clone());
+160        runtime.spawn(Box::pin(
+161            async {
+162                if let Err(e) = driver.await {
+163                    tracing::error!("I/O error: {}", e);
+164                }
+165            }
+166            .instrument(Span::current()),
+167        ));
+168        Ok(Self { inner: rc, runtime })
+169    }
+170
+171    /// Get the next incoming connection attempt from a client
+172    ///
+173    /// Yields [`Incoming`]s, or `None` if the endpoint is [`close`](Self::close)d. [`Incoming`]
+174    /// can be `await`ed to obtain the final [`Connection`](crate::Connection), or used to e.g.
+175    /// filter connection attempts or force address validation, or converted into an intermediate
+176    /// `Connecting` future which can be used to e.g. send 0.5-RTT data.
+177    pub fn accept(&self) -> Accept<'_> {
+178        Accept {
+179            endpoint: self,
+180            notify: self.inner.shared.incoming.notified(),
+181        }
+182    }
+183
+184    /// Set the client configuration used by `connect`
+185    pub fn set_default_client_config(&self, config: ClientConfig) {
+186        self.inner.0.state.lock().unwrap().default_client_config = Some(config);
+187    }
+188
+189    /// Connect to a remote endpoint
+190    ///
+191    /// `server_name` must be covered by the certificate presented by the server. This prevents a
+192    /// connection from being intercepted by an attacker with a valid certificate for some other
+193    /// server.
+194    ///
+195    /// May fail immediately due to configuration errors, or in the future if the connection could
+196    /// not be established.
+197    pub fn connect(&self, addr: SocketAddr, server_name: &str) -> Result<Connecting, ConnectError> {
+198        let Some(config) = self
+199            .inner
+200            .0
+201            .state
+202            .lock()
+203            .unwrap()
+204            .default_client_config
+205            .clone()
+206        else {
+207            return Err(ConnectError::NoDefaultClientConfig);
+208        };
+209
+210        self.connect_with(config, addr, server_name)
+211    }
+212
+213    /// Connect to a remote endpoint using a custom configuration.
+214    ///
+215    /// See [`connect()`] for details.
+216    ///
+217    /// [`connect()`]: Endpoint::connect
+218    pub fn connect_with(
+219        &self,
+220        config: ClientConfig,
+221        addr: SocketAddr,
+222        server_name: &str,
+223    ) -> Result<Connecting, ConnectError> {
+224        let mut endpoint = self.inner.state.lock().unwrap();
+225        if endpoint.driver_lost || endpoint.recv_state.connections.close.is_some() {
+226            return Err(ConnectError::EndpointStopping);
+227        }
+228        if addr.is_ipv6() && !endpoint.ipv6 {
+229            return Err(ConnectError::InvalidRemoteAddress(addr));
+230        }
+231        let addr = if endpoint.ipv6 {
+232            SocketAddr::V6(ensure_ipv6(addr))
+233        } else {
+234            addr
+235        };
+236
+237        let (ch, conn) = endpoint
+238            .inner
+239            .connect(self.runtime.now(), config, addr, server_name)?;
+240
+241        let sender = endpoint.socket.create_sender();
+242        endpoint.stats.outgoing_handshakes += 1;
+243        Ok(endpoint
+244            .recv_state
+245            .connections
+246            .insert(ch, conn, sender, self.runtime.clone()))
+247    }
+248
+249    /// Switch to a new UDP socket
+250    ///
+251    /// See [`Endpoint::rebind_abstract()`] for details.
+252    #[cfg(not(wasm_browser))]
+253    pub fn rebind(&self, socket: std::net::UdpSocket) -> io::Result<()> {
+254        self.rebind_abstract(self.runtime.wrap_udp_socket(socket)?)
+255    }
+256
+257    /// Switch to a new UDP socket
+258    ///
+259    /// Allows the endpoint's address to be updated live, affecting all active connections. Incoming
+260    /// connections and connections to servers unreachable from the new address will be lost.
+261    ///
+262    /// On error, the old UDP socket is retained.
+263    pub fn rebind_abstract(&self, socket: Box<dyn AsyncUdpSocket>) -> io::Result<()> {
+264        let addr = socket.local_addr()?;
+265        let mut inner = self.inner.state.lock().unwrap();
+266        inner.prev_socket = Some(mem::replace(&mut inner.socket, socket));
+267        inner.ipv6 = addr.is_ipv6();
+268
+269        // Update connection socket references
+270        for sender in inner.recv_state.connections.senders.values() {
+271            // Ignoring errors from dropped connections
+272            let _ = sender.send(ConnectionEvent::Rebind(inner.socket.create_sender()));
+273        }
+274        if let Some(driver) = inner.driver.take() {
+275            // Ensure the driver can register for wake-ups from the new socket
+276            driver.wake();
+277        }
+278
+279        Ok(())
+280    }
+281
+282    /// Notify connections that the local network address has changed.
+283    ///
+284    /// This informs all active connections that the local address may have changed (e.g., due to a
+285    /// network interface change), triggering liveness checks and recovery procedures on each
+286    /// connection without requiring a socket rebind.
+287    ///
+288    /// Unlike [`Self::rebind`], this does not change the underlying socket. Use this when the
+289    /// network topology changes but the socket remains valid (e.g., if bound to the unspecified
+290    /// address and switching from WiFi to cellular, or when the local IP address changes).
+291    ///
+292    /// The optional `hint` allows callers to indicate which paths may still be recoverable after
+293    /// the network change. If `None`, all paths are assumed to be non-recoverable. For client-side
+294    /// multipath connections, unrecoverable paths will be closed and replaced with new paths to
+295    /// the same remote addresses.
+296    pub fn handle_network_change(&self, hint: Option<Arc<dyn NetworkChangeHint + Sync + Send>>) {
+297        let mut inner = self.inner.state.lock().unwrap();
+298        for sender in inner.recv_state.connections.senders.values() {
+299            // Ignoring errors from dropped connections
+300            let _ = sender.send(ConnectionEvent::LocalAddressChanged(hint.clone()));
+301        }
+302        if let Some(driver) = inner.driver.take() {
+303            driver.wake();
+304        }
+305    }
+306
+307    /// Replace the server configuration, affecting new incoming connections only
+308    ///
+309    /// Useful for e.g. refreshing TLS certificates without disrupting existing connections.
+310    pub fn set_server_config(&self, server_config: Option<ServerConfig>) {
+311        self.inner
+312            .state
+313            .lock()
+314            .unwrap()
+315            .inner
+316            .set_server_config(server_config.map(Arc::new))
+317    }
+318
+319    /// Get the local `SocketAddr` the underlying socket is bound to
+320    pub fn local_addr(&self) -> io::Result<SocketAddr> {
+321        self.inner.state.lock().unwrap().socket.local_addr()
+322    }
+323
+324    /// Get the number of connections that are currently open
+325    pub fn open_connections(&self) -> usize {
+326        self.inner.state.lock().unwrap().inner.open_connections()
+327    }
+328
+329    /// Close all of this endpoint's connections immediately and cease accepting new connections.
+330    ///
+331    /// See [`Connection::close()`] for details.
+332    ///
+333    /// [`Connection::close()`]: crate::Connection::close
+334    pub fn close(&self, error_code: VarInt, reason: &[u8]) {
+335        let reason = Bytes::copy_from_slice(reason);
+336        let mut endpoint = self.inner.state.lock().unwrap();
+337        endpoint.recv_state.connections.close = Some((error_code, reason.clone()));
+338        for sender in endpoint.recv_state.connections.senders.values() {
+339            // Ignoring errors from dropped connections
+340            let _ = sender.send(ConnectionEvent::Close {
+341                error_code,
+342                reason: reason.clone(),
+343            });
+344        }
+345        self.inner.shared.incoming.notify_waiters();
+346        // Wake the endpoint driver so that it can shutdown if the endpoint is already drained.
+347        if let Some(waker) = endpoint.driver.take() {
+348            waker.wake();
+349        }
+350    }
+351
+352    /// Wait for all connections on the endpoint to be cleanly shut down
+353    ///
+354    /// Waiting for this condition before exiting ensures that a good-faith effort is made to notify
+355    /// peers of recent connection closes, whereas exiting immediately could force them to wait out
+356    /// the idle timeout period.
+357    ///
+358    /// Does not proactively close existing connections or cause incoming connections to be
+359    /// rejected. Consider calling [`close()`] if that is desired.
+360    ///
+361    /// [`close()`]: Endpoint::close
+362    pub async fn wait_idle(&self) {
+363        loop {
+364            {
+365                let endpoint = &mut *self.inner.state.lock().unwrap();
+366                if endpoint.recv_state.connections.is_empty() {
+367                    break;
+368                }
+369                // Construct future while lock is held to avoid race
+370                self.inner.shared.idle.notified()
+371            }
+372            .await;
+373        }
+374    }
+375}
+376
+377/// Statistics on [Endpoint] activity
+378#[non_exhaustive]
+379#[derive(Debug, Default, Copy, Clone)]
+380pub struct EndpointStats {
+381    /// Cumulative number of Quic handshakes accepted by this [Endpoint]
+382    pub accepted_handshakes: u64,
+383    /// Cumulative number of Quic handshakes sent from this [Endpoint]
+384    pub outgoing_handshakes: u64,
+385    /// Cumulative number of Quic handshakes refused on this [Endpoint]
+386    pub refused_handshakes: u64,
+387    /// Cumulative number of Quic handshakes ignored on this [Endpoint]
+388    pub ignored_handshakes: u64,
+389}
+390
+391/// A future that drives IO on an endpoint
+392///
+393/// This task functions as the switch point between the UDP socket object and the
+394/// `Endpoint` responsible for routing datagrams to their owning `Connection`.
+395/// In order to do so, it also facilitates the exchange of different types of events
+396/// flowing between the `Endpoint` and the tasks managing `Connection`s. As such,
+397/// running this task is necessary to keep the endpoint's connections running.
+398///
+399/// `EndpointDriver` futures terminate when all clones of the `Endpoint` have been dropped, or when
+400/// an I/O error occurs.
+401#[must_use = "endpoint drivers must be spawned for I/O to occur"]
+402#[derive(Debug)]
+403pub(crate) struct EndpointDriver(pub(crate) EndpointRef);
+404
+405impl Future for EndpointDriver {
+406    type Output = Result<(), io::Error>;
+407
+408    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+409        let mut endpoint = self.0.state.lock().unwrap();
+410        if endpoint.driver.is_none() {
+411            endpoint.driver = Some(cx.waker().clone());
+412        }
+413
+414        let now = endpoint.runtime.now();
+415        let mut keep_going = false;
+416        keep_going |= endpoint.drive_recv(cx, now)?;
+417        keep_going |= endpoint.handle_events(cx, &self.0.shared);
+418
+419        if !endpoint.recv_state.incoming.is_empty() {
+420            self.0.shared.incoming.notify_waiters();
+421        }
+422
+423        // Stop the driver if either:
+424        // - all `Endpoint` structs are dropped and all connections are drained,
+425        // - or `Endpoint::close` has been called and all connections are drained.
+426        if endpoint.recv_state.connections.is_empty()
+427            && (endpoint.ref_count == 0 || endpoint.recv_state.connections.close.is_some())
+428        {
+429            trace!("endpoint driver stopping");
+430            Poll::Ready(Ok(()))
+431        } else {
+432            drop(endpoint);
+433            // If there is more work to do schedule the endpoint task again.
+434            // `wake_by_ref()` is called outside the lock to minimize
+435            // lock contention on a multithreaded runtime.
+436            if keep_going {
+437                cx.waker().wake_by_ref();
+438            }
+439            Poll::Pending
+440        }
+441    }
+442}
+443
+444impl Drop for EndpointDriver {
+445    fn drop(&mut self) {
+446        let mut endpoint = self.0.state.lock().unwrap();
+447        endpoint.driver_lost = true;
+448        self.0.shared.incoming.notify_waiters();
+449        // Drop all outgoing channels, signaling the termination of the endpoint to the associated
+450        // connections.
+451        endpoint.recv_state.connections.senders.clear();
+452    }
+453}
+454
+455#[derive(Debug)]
+456pub(crate) struct EndpointInner {
+457    pub(crate) state: Mutex<State>,
+458    pub(crate) shared: Shared,
+459}
+460
+461impl EndpointInner {
+462    pub(crate) fn accept(
+463        &self,
+464        incoming: proto::Incoming,
+465        server_config: Option<Arc<ServerConfig>>,
+466    ) -> Result<Connecting, ConnectionError> {
+467        let mut state = self.state.lock().unwrap();
+468        let mut response_buffer = Vec::new();
+469        let now = state.runtime.now();
+470        match state
+471            .inner
+472            .accept(incoming, now, &mut response_buffer, server_config)
+473        {
+474            Ok((handle, conn)) => {
+475                state.stats.accepted_handshakes += 1;
+476                let sender = state.socket.create_sender();
+477                let runtime = state.runtime.clone();
+478                Ok(state
+479                    .recv_state
+480                    .connections
+481                    .insert(handle, conn, sender, runtime))
+482            }
+483            Err(error) => {
+484                if let Some(transmit) = error.response {
+485                    respond(transmit, &response_buffer, &mut state.sender);
+486                }
+487                Err(error.cause)
+488            }
+489        }
+490    }
+491
+492    pub(crate) fn refuse(&self, incoming: proto::Incoming) {
+493        let mut state = self.state.lock().unwrap();
+494        state.stats.refused_handshakes += 1;
+495        let mut response_buffer = Vec::new();
+496        let transmit = state.inner.refuse(incoming, &mut response_buffer);
+497        respond(transmit, &response_buffer, &mut state.sender);
+498    }
+499
+500    pub(crate) fn retry(&self, incoming: proto::Incoming) -> Result<(), proto::RetryError> {
+501        let mut state = self.state.lock().unwrap();
+502        let mut response_buffer = Vec::new();
+503        let transmit = state.inner.retry(incoming, &mut response_buffer)?;
+504        respond(transmit, &response_buffer, &mut state.sender);
+505        Ok(())
+506    }
+507
+508    pub(crate) fn ignore(&self, incoming: proto::Incoming) {
+509        let mut state = self.state.lock().unwrap();
+510        state.stats.ignored_handshakes += 1;
+511        state.inner.ignore(incoming);
+512    }
+513}
+514
+515#[derive(Debug)]
+516pub(crate) struct State {
+517    socket: Box<dyn AsyncUdpSocket>,
+518    sender: Pin<Box<dyn UdpSender>>,
+519    /// During an active migration, abandoned_socket receives traffic
+520    /// until the first packet arrives on the new socket.
+521    prev_socket: Option<Box<dyn AsyncUdpSocket>>,
+522    inner: proto::Endpoint,
+523    recv_state: RecvState,
+524    driver: Option<Waker>,
+525    ipv6: bool,
+526    events: mpsc::UnboundedReceiver<(ConnectionHandle, EndpointEvent)>,
+527    /// Number of live handles that can be used to initiate or handle I/O; excludes the driver
+528    ref_count: usize,
+529    driver_lost: bool,
+530    runtime: Arc<dyn Runtime>,
+531    stats: EndpointStats,
+532    default_client_config: Option<ClientConfig>,
+533}
+534
+535#[derive(Debug)]
+536pub(crate) struct Shared {
+537    incoming: Notify,
+538    idle: Notify,
+539}
+540
+541impl State {
+542    fn drive_recv(&mut self, cx: &mut Context<'_>, now: Instant) -> Result<bool, io::Error> {
+543        let get_time = || self.runtime.now();
+544        self.recv_state.recv_limiter.start_cycle(get_time);
+545        if let Some(socket) = &mut self.prev_socket {
+546            // We don't care about the `PollProgress` from old sockets.
+547            let poll_res = self.recv_state.poll_socket(
+548                cx,
+549                &mut self.inner,
+550                &mut **socket,
+551                &mut self.sender,
+552                &*self.runtime,
+553                now,
+554            );
+555            if poll_res.is_err() {
+556                self.prev_socket = None;
+557            }
+558        };
+559        let poll_res = self.recv_state.poll_socket(
+560            cx,
+561            &mut self.inner,
+562            &mut *self.socket,
+563            &mut self.sender,
+564            &*self.runtime,
+565            now,
+566        );
+567        self.recv_state.recv_limiter.finish_cycle(get_time);
+568        let poll_res = poll_res?;
+569        if poll_res.received_connection_packet {
+570            // Traffic has arrived on self.socket, therefore there is no need for the abandoned
+571            // one anymore. TODO: Account for multiple outgoing connections.
+572            self.prev_socket = None;
+573        }
+574        Ok(poll_res.keep_going)
+575    }
+576
+577    fn handle_events(&mut self, cx: &mut Context<'_>, shared: &Shared) -> bool {
+578        for _ in 0..IO_LOOP_BOUND {
+579            let (ch, event) = match self.events.poll_recv(cx) {
+580                Poll::Ready(Some(x)) => x,
+581                Poll::Ready(None) => unreachable!("EndpointInner owns one sender"),
+582                Poll::Pending => {
+583                    return false;
+584                }
+585            };
+586
+587            if event.is_drained() {
+588                self.recv_state.connections.senders.remove(&ch);
+589                if self.recv_state.connections.is_empty() {
+590                    shared.idle.notify_waiters();
+591                }
+592            }
+593            let Some(event) = self.inner.handle_event(ch, event) else {
+594                continue;
+595            };
+596            // Ignoring errors from dropped connections that haven't yet been cleaned up
+597            let _ = self
+598                .recv_state
+599                .connections
+600                .senders
+601                .get_mut(&ch)
+602                .unwrap()
+603                .send(ConnectionEvent::Proto(event));
+604        }
+605
+606        true
+607    }
+608}
+609
+610impl Drop for State {
+611    fn drop(&mut self) {
+612        for incoming in self.recv_state.incoming.drain(..) {
+613            self.inner.ignore(incoming);
+614        }
+615    }
+616}
+617
+618fn respond(
+619    transmit: proto::Transmit,
+620    response_buffer: &[u8],
+621    sender: &mut Pin<Box<dyn UdpSender>>,
+622) {
+623    // Send if there's kernel buffer space; otherwise, drop it
+624    //
+625    // As an endpoint-generated packet, we know this is an
+626    // immediate, stateless response to an unconnected peer,
+627    // one of:
+628    //
+629    // - A version negotiation response due to an unknown version
+630    // - A `CLOSE` due to a malformed or unwanted connection attempt
+631    // - A stateless reset due to an unrecognized connection
+632    // - A `Retry` packet due to a connection attempt when
+633    //   `use_retry` is set
+634    //
+635    // In each case, a well-behaved peer can be trusted to retry a
+636    // few times, which is guaranteed to produce the same response
+637    // from us. Repeated failures might at worst cause a peer's new
+638    // connection attempt to time out, which is acceptable if we're
+639    // under such heavy load that there's never room for this code
+640    // to transmit. This is morally equivalent to the packet getting
+641    // lost due to congestion further along the link, which
+642    // similarly relies on peer retries for recovery.
+643
+644    // Copied from rust 1.85's std::task::Waker::noop() implementation for backwards compatibility
+645    const NOOP: RawWaker = {
+646        const VTABLE: RawWakerVTable = RawWakerVTable::new(
+647            // Cloning just returns a new no-op raw waker
+648            |_| NOOP,
+649            // `wake` does nothing
+650            |_| {},
+651            // `wake_by_ref` does nothing
+652            |_| {},
+653            // Dropping does nothing as we don't allocate anything
+654            |_| {},
+655        );
+656        RawWaker::new(std::ptr::null(), &VTABLE)
+657    };
+658    // SAFETY: Copied from rust stdlib, the NOOP waker is thread-safe and doesn't violate the RawWakerVTable contract,
+659    // it doesn't access the data pointer at all.
+660    let waker = unsafe { Waker::from_raw(NOOP) };
+661    let mut cx = Context::from_waker(&waker);
+662    _ = sender.as_mut().poll_send(
+663        &udp_transmit(&transmit, &response_buffer[..transmit.size]),
+664        &mut cx,
+665    );
+666}
+667
+668#[inline]
+669fn proto_ecn(ecn: udp::EcnCodepoint) -> proto::EcnCodepoint {
+670    match ecn {
+671        udp::EcnCodepoint::Ect0 => proto::EcnCodepoint::Ect0,
+672        udp::EcnCodepoint::Ect1 => proto::EcnCodepoint::Ect1,
+673        udp::EcnCodepoint::Ce => proto::EcnCodepoint::Ce,
+674    }
+675}
+676
+677#[derive(Debug)]
+678struct ConnectionSet {
+679    /// Senders for communicating with the endpoint's connections
+680    senders: FxHashMap<ConnectionHandle, mpsc::UnboundedSender<ConnectionEvent>>,
+681    /// Stored to give out clones to new ConnectionInners
+682    sender: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+683    /// Set if the endpoint has been manually closed
+684    close: Option<(VarInt, Bytes)>,
+685}
+686
+687impl ConnectionSet {
+688    fn insert(
+689        &mut self,
+690        handle: ConnectionHandle,
+691        conn: proto::Connection,
+692        sender: Pin<Box<dyn UdpSender>>,
+693        runtime: Arc<dyn Runtime>,
+694    ) -> Connecting {
+695        let (send, recv) = mpsc::unbounded_channel();
+696        if let Some((error_code, ref reason)) = self.close {
+697            send.send(ConnectionEvent::Close {
+698                error_code,
+699                reason: reason.clone(),
+700            })
+701            .unwrap();
+702        }
+703        self.senders.insert(handle, send);
+704        Connecting::new(handle, conn, self.sender.clone(), recv, sender, runtime)
+705    }
+706
+707    fn is_empty(&self) -> bool {
+708        self.senders.is_empty()
+709    }
+710}
+711
+712pub(crate) fn ensure_ipv6(x: SocketAddr) -> SocketAddrV6 {
+713    match x {
+714        SocketAddr::V6(x) => x,
+715        SocketAddr::V4(x) => SocketAddrV6::new(x.ip().to_ipv6_mapped(), x.port(), 0, 0),
+716    }
+717}
+718
+719pin_project! {
+720    /// Future produced by [`Endpoint::accept`]
+721    pub struct Accept<'a> {
+722        endpoint: &'a Endpoint,
+723        #[pin]
+724        notify: Notified<'a>,
+725    }
+726}
+727
+728impl Future for Accept<'_> {
+729    type Output = Option<Incoming>;
+730    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+731        let mut this = self.project();
+732        let mut endpoint = this.endpoint.inner.state.lock().unwrap();
+733        if endpoint.driver_lost {
+734            return Poll::Ready(None);
+735        }
+736        if let Some(incoming) = endpoint.recv_state.incoming.pop_front() {
+737            // Release the mutex lock on endpoint so cloning it doesn't deadlock
+738            drop(endpoint);
+739            let incoming = Incoming::new(incoming, this.endpoint.inner.clone());
+740            return Poll::Ready(Some(incoming));
+741        }
+742        if endpoint.recv_state.connections.close.is_some() {
+743            return Poll::Ready(None);
+744        }
+745        loop {
+746            match this.notify.as_mut().poll(ctx) {
+747                // `state` lock ensures we didn't race with readiness
+748                Poll::Pending => return Poll::Pending,
+749                // Spurious wakeup, get a new future
+750                Poll::Ready(()) => this
+751                    .notify
+752                    .set(this.endpoint.inner.shared.incoming.notified()),
+753            }
+754        }
+755    }
+756}
+757
+758#[derive(Debug)]
+759pub(crate) struct EndpointRef(Arc<EndpointInner>);
+760
+761impl EndpointRef {
+762    pub(crate) fn new(
+763        socket: Box<dyn AsyncUdpSocket>,
+764        inner: proto::Endpoint,
+765        ipv6: bool,
+766        runtime: Arc<dyn Runtime>,
+767    ) -> Self {
+768        let (sender, events) = mpsc::unbounded_channel();
+769        let recv_state = RecvState::new(sender, socket.max_receive_segments(), &inner);
+770        let sender = socket.create_sender();
+771        Self(Arc::new(EndpointInner {
+772            shared: Shared {
+773                incoming: Notify::new(),
+774                idle: Notify::new(),
+775            },
+776            state: Mutex::new(State {
+777                socket,
+778                sender,
+779                prev_socket: None,
+780                inner,
+781                ipv6,
+782                events,
+783                driver: None,
+784                ref_count: 0,
+785                driver_lost: false,
+786                recv_state,
+787                runtime,
+788                stats: EndpointStats::default(),
+789                default_client_config: None,
+790            }),
+791        }))
+792    }
+793}
+794
+795impl Clone for EndpointRef {
+796    fn clone(&self) -> Self {
+797        self.0.state.lock().unwrap().ref_count += 1;
+798        Self(self.0.clone())
+799    }
+800}
+801
+802impl Drop for EndpointRef {
+803    fn drop(&mut self) {
+804        let endpoint = &mut *self.0.state.lock().unwrap();
+805        if let Some(x) = endpoint.ref_count.checked_sub(1) {
+806            endpoint.ref_count = x;
+807            if x == 0 {
+808                // If the driver is about to be on its own, ensure it can shut down if the last
+809                // connection is gone.
+810                if let Some(task) = endpoint.driver.take() {
+811                    task.wake();
+812                }
+813            }
+814        }
+815    }
+816}
+817
+818impl std::ops::Deref for EndpointRef {
+819    type Target = EndpointInner;
+820    fn deref(&self) -> &Self::Target {
+821        &self.0
+822    }
+823}
+824
+825/// State directly involved in handling incoming packets
+826struct RecvState {
+827    incoming: VecDeque<proto::Incoming>,
+828    connections: ConnectionSet,
+829    recv_buf: Box<[u8]>,
+830    recv_limiter: WorkLimiter,
+831}
+832
+833impl RecvState {
+834    fn new(
+835        sender: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+836        max_receive_segments: NonZeroUsize,
+837        endpoint: &proto::Endpoint,
+838    ) -> Self {
+839        let recv_buf = vec![
+840            0;
+841            endpoint.config().get_max_udp_payload_size().min(64 * 1024) as usize
+842                * max_receive_segments.get()
+843                * BATCH_SIZE
+844        ];
+845        Self {
+846            connections: ConnectionSet {
+847                senders: FxHashMap::default(),
+848                sender,
+849                close: None,
+850            },
+851            incoming: VecDeque::new(),
+852            recv_buf: recv_buf.into(),
+853            recv_limiter: WorkLimiter::new(RECV_TIME_BOUND),
+854        }
+855    }
+856
+857    fn poll_socket(
+858        &mut self,
+859        cx: &mut Context<'_>,
+860        endpoint: &mut proto::Endpoint,
+861        socket: &mut dyn AsyncUdpSocket,
+862        sender: &mut Pin<Box<dyn UdpSender>>,
+863        runtime: &dyn Runtime,
+864        now: Instant,
+865    ) -> Result<PollProgress, io::Error> {
+866        let mut received_connection_packet = false;
+867        let mut metas = [RecvMeta::default(); BATCH_SIZE];
+868        let mut iovs: [IoSliceMut<'_>; BATCH_SIZE] = {
+869            let mut bufs = self
+870                .recv_buf
+871                .chunks_mut(self.recv_buf.len() / BATCH_SIZE)
+872                .map(IoSliceMut::new);
+873
+874            // expect() safe as self.recv_buf is chunked into BATCH_SIZE items
+875            // and iovs will be of size BATCH_SIZE, thus from_fn is called
+876            // exactly BATCH_SIZE times.
+877            std::array::from_fn(|_| bufs.next().expect("BATCH_SIZE elements"))
+878        };
+879        loop {
+880            match socket.poll_recv(cx, &mut iovs, &mut metas) {
+881                Poll::Ready(Ok(msgs)) => {
+882                    self.recv_limiter.record_work(msgs);
+883                    for (meta, buf) in metas.iter().zip(iovs.iter()).take(msgs) {
+884                        let mut data: BytesMut = buf[0..meta.len].into();
+885                        while !data.is_empty() {
+886                            let buf = data.split_to(meta.stride.min(data.len()));
+887                            let mut response_buffer = Vec::new();
+888                            let addresses = FourTuple {
+889                                remote: meta.addr,
+890                                local_ip: meta.dst_ip,
+891                            };
+892                            match endpoint.handle(
+893                                now,
+894                                addresses,
+895                                meta.ecn.map(proto_ecn),
+896                                buf,
+897                                &mut response_buffer,
+898                            ) {
+899                                Some(DatagramEvent::NewConnection(incoming)) => {
+900                                    if self.connections.close.is_none() {
+901                                        self.incoming.push_back(incoming);
+902                                    } else {
+903                                        let transmit =
+904                                            endpoint.refuse(incoming, &mut response_buffer);
+905                                        respond(transmit, &response_buffer, sender);
+906                                    }
+907                                }
+908                                Some(DatagramEvent::ConnectionEvent(handle, event)) => {
+909                                    // Ignoring errors from dropped connections that haven't yet been cleaned up
+910                                    received_connection_packet = true;
+911                                    let _ = self
+912                                        .connections
+913                                        .senders
+914                                        .get_mut(&handle)
+915                                        .unwrap()
+916                                        .send(ConnectionEvent::Proto(event));
+917                                }
+918                                Some(DatagramEvent::Response(transmit)) => {
+919                                    respond(transmit, &response_buffer, sender);
+920                                }
+921                                None => {}
+922                            }
+923                        }
+924                    }
+925                }
+926                Poll::Pending => {
+927                    return Ok(PollProgress {
+928                        received_connection_packet,
+929                        keep_going: false,
+930                    });
+931                }
+932                // Ignore ECONNRESET as it's undefined in QUIC and may be injected by an
+933                // attacker
+934                Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::ConnectionReset => {
+935                    continue;
+936                }
+937                Poll::Ready(Err(e)) => {
+938                    return Err(e);
+939                }
+940            }
+941            if !self.recv_limiter.allow_work(|| runtime.now()) {
+942                return Ok(PollProgress {
+943                    received_connection_packet,
+944                    keep_going: true,
+945                });
+946            }
+947        }
+948    }
+949}
+950
+951impl fmt::Debug for RecvState {
+952    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+953        f.debug_struct("RecvState")
+954            .field("incoming", &self.incoming)
+955            .field("connections", &self.connections)
+956            // recv_buf too large
+957            .field("recv_limiter", &self.recv_limiter)
+958            .finish_non_exhaustive()
+959    }
+960}
+961
+962#[derive(Default)]
+963struct PollProgress {
+964    /// Whether a datagram was routed to an existing connection
+965    received_connection_packet: bool,
+966    /// Whether datagram handling was interrupted early by the work limiter for fairness
+967    keep_going: bool,
+968}
\ No newline at end of file diff --git a/pr/509/docs/src/noq/incoming.rs.html b/pr/509/docs/src/noq/incoming.rs.html new file mode 100644 index 000000000..e632f6314 --- /dev/null +++ b/pr/509/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, 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<proto::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/509/docs/src/noq/lib.rs.html b/pr/509/docs/src/noq/lib.rs.html new file mode 100644 index 000000000..226cbf91a --- /dev/null +++ b/pr/509/docs/src/noq/lib.rs.html @@ -0,0 +1,140 @@ +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 incoming;
+48mod mutex;
+49mod path;
+50mod recv_stream;
+51mod runtime;
+52mod send_stream;
+53mod work_limiter;
+54
+55#[cfg(not(wasm_browser))]
+56pub(crate) use std::time::{Duration, Instant};
+57#[cfg(wasm_browser)]
+58pub(crate) use web_time::{Duration, Instant};
+59
+60#[cfg(feature = "bloom")]
+61pub use proto::BloomTokenLog;
+62pub use proto::{
+63    AckFrequencyConfig, ApplicationClose, Chunk, ClientConfig, ClosedStream, ConfigError,
+64    ConnectError, ConnectionClose, ConnectionError, ConnectionId, ConnectionIdGenerator,
+65    ConnectionStats, Dir, EcnCodepoint, EndpointConfig, FrameStats, FrameType, IdleTimeout,
+66    InvalidCid, MtuDiscoveryConfig, NetworkChangeHint, NoneTokenLog, NoneTokenStore, PathId,
+67    PathStats, ServerConfig, Side, StdSystemTime, StreamId, TimeSource, TokenLog, TokenMemoryCache,
+68    TokenReuseError, TokenStore, Transmit, TransportConfig, TransportErrorCode, UdpStats,
+69    ValidationTokenConfig, VarInt, VarIntBoundsExceeded, Written, congestion, crypto,
+70};
+71#[cfg(feature = "qlog")]
+72pub use proto::{QlogConfig, QlogFactory, QlogFileFactory};
+73#[cfg(feature = "rustls")]
+74pub use rustls;
+75pub use udp;
+76
+77pub use crate::connection::{
+78    AcceptBi, AcceptUni, Connecting, Connection, OnClosed, OpenBi, OpenUni, ReadDatagram,
+79    SendDatagram, SendDatagramError, WeakConnectionHandle, ZeroRttAccepted,
+80};
+81pub use crate::endpoint::{Accept, Endpoint, EndpointStats};
+82pub use crate::incoming::{Incoming, IncomingFuture, RetryError};
+83pub use crate::path::{AddressDiscovery, OpenPath, Path, WeakPathHandle};
+84pub use crate::recv_stream::{
+85    ReadError, ReadExactError, ReadToEndError, RecvStream, ResetError, UnorderedRecvStream,
+86};
+87#[cfg(feature = "runtime-smol")]
+88pub use crate::runtime::SmolRuntime;
+89#[cfg(feature = "runtime-tokio")]
+90pub use crate::runtime::TokioRuntime;
+91#[cfg(any(feature = "runtime-tokio", feature = "runtime-smol"))]
+92pub use crate::runtime::default_runtime;
+93pub use crate::runtime::{AsyncTimer, AsyncUdpSocket, Runtime, UdpSender};
+94pub use crate::send_stream::{SendStream, Stopped, StoppedError, WriteError};
+95
+96#[cfg(test)]
+97mod tests;
+98
+99#[derive(Debug)]
+100enum ConnectionEvent {
+101    Close {
+102        error_code: VarInt,
+103        reason: bytes::Bytes,
+104    },
+105    Proto(proto::ConnectionEvent),
+106    Rebind(Pin<Box<dyn UdpSender>>),
+107    LocalAddressChanged(Option<Arc<dyn NetworkChangeHint + Sync + Send>>),
+108}
+109
+110fn udp_transmit<'a>(t: &proto::Transmit, buffer: &'a [u8]) -> udp::Transmit<'a> {
+111    udp::Transmit {
+112        destination: t.destination,
+113        ecn: t.ecn.map(udp_ecn),
+114        contents: buffer,
+115        segment_size: t.segment_size,
+116        src_ip: t.src_ip,
+117    }
+118}
+119
+120fn udp_ecn(ecn: proto::EcnCodepoint) -> udp::EcnCodepoint {
+121    match ecn {
+122        proto::EcnCodepoint::Ect0 => udp::EcnCodepoint::Ect0,
+123        proto::EcnCodepoint::Ect1 => udp::EcnCodepoint::Ect1,
+124        proto::EcnCodepoint::Ce => udp::EcnCodepoint::Ce,
+125    }
+126}
+127
+128/// Maximum number of datagrams processed in send/recv calls to make before moving on to other processing
+129///
+130/// This helps ensure we don't starve anything when the CPU is slower than the link.
+131/// Value is selected by picking a low number which didn't degrade throughput in benchmarks.
+132const IO_LOOP_BOUND: usize = 160;
+133
+134/// The maximum amount of time that should be spent in `recvmsg()` calls per endpoint iteration
+135///
+136/// 50us are chosen so that an endpoint iteration with a 50us sendmsg limit blocks
+137/// the runtime for a maximum of about 100us.
+138/// Going much lower does not yield any noticeable difference, since a single `recvmmsg`
+139/// batch of size 32 was observed to take 30us on some systems.
+140const RECV_TIME_BOUND: Duration = Duration::from_micros(50);
\ No newline at end of file diff --git a/pr/509/docs/src/noq/mutex.rs.html b/pr/509/docs/src/noq/mutex.rs.html new file mode 100644 index 000000000..f04b9bef5 --- /dev/null +++ b/pr/509/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;
+114
+115#[cfg(not(feature = "lock_tracking"))]
+116mod non_tracking {
+117    use super::*;
+118
+119    /// A Mutex which optionally allows to track the time a lock was held and
+120    /// emit warnings in case of excessive lock times
+121    #[derive(Debug)]
+122    pub(crate) struct Mutex<T> {
+123        inner: std::sync::Mutex<T>,
+124    }
+125
+126    impl<T> Mutex<T> {
+127        pub(crate) fn new(value: T) -> Self {
+128            Self {
+129                inner: std::sync::Mutex::new(value),
+130            }
+131        }
+132
+133        /// Acquires the lock for a certain purpose
+134        ///
+135        /// The purpose will be recorded in the list of last lock owners
+136        pub(crate) fn lock(&self, _purpose: &'static str) -> MutexGuard<'_, T> {
+137            MutexGuard {
+138                guard: self.inner.lock().unwrap(),
+139            }
+140        }
+141    }
+142
+143    pub(crate) struct MutexGuard<'a, T> {
+144        guard: std::sync::MutexGuard<'a, T>,
+145    }
+146
+147    impl<T> Deref for MutexGuard<'_, T> {
+148        type Target = T;
+149
+150        fn deref(&self) -> &Self::Target {
+151            self.guard.deref()
+152        }
+153    }
+154
+155    impl<T> DerefMut for MutexGuard<'_, T> {
+156        fn deref_mut(&mut self) -> &mut Self::Target {
+157            self.guard.deref_mut()
+158        }
+159    }
+160}
+161
+162#[cfg(not(feature = "lock_tracking"))]
+163pub(crate) use non_tracking::Mutex;
\ No newline at end of file diff --git a/pr/509/docs/src/noq/path.rs.html b/pr/509/docs/src/noq/path.rs.html new file mode 100644 index 000000000..1dc90c9c1 --- /dev/null +++ b/pr/509/docs/src/noq/path.rs.html @@ -0,0 +1,421 @@ +path.rs - source

noq/
path.rs

1use std::future::Future;
+2use std::net::SocketAddr;
+3use std::pin::Pin;
+4use std::sync::Arc;
+5use std::task::{Context, Poll, ready};
+6use std::time::Duration;
+7
+8use proto::{
+9    ClosePathError, ClosedPath, PathError, PathEvent, PathId, PathStats, PathStatus,
+10    SetPathStatusError, TransportErrorCode,
+11};
+12use tokio::sync::watch;
+13use tokio_stream::{Stream, wrappers::WatchStream};
+14
+15use crate::connection::ConnectionRef;
+16use crate::{Runtime, WeakConnectionHandle};
+17
+18/// Future produced by [`crate::Connection::open_path`]
+19pub struct OpenPath(OpenPathInner);
+20
+21enum OpenPathInner {
+22    /// Opening a path in underway
+23    ///
+24    /// This might fail later on.
+25    Ongoing {
+26        opened: WatchStream<Result<(), PathError>>,
+27        path_id: PathId,
+28        conn: ConnectionRef,
+29    },
+30    /// Opening a path failed immediately
+31    Rejected {
+32        /// The error that occurred
+33        err: PathError,
+34    },
+35    /// The path is already open
+36    Ready {
+37        path_id: PathId,
+38        conn: ConnectionRef,
+39    },
+40}
+41
+42impl OpenPath {
+43    pub(crate) fn new(
+44        path_id: PathId,
+45        opened: watch::Receiver<Result<(), PathError>>,
+46        conn: ConnectionRef,
+47    ) -> Self {
+48        Self(OpenPathInner::Ongoing {
+49            opened: WatchStream::from_changes(opened),
+50            path_id,
+51            conn,
+52        })
+53    }
+54
+55    pub(crate) fn ready(path_id: PathId, conn: ConnectionRef) -> Self {
+56        Self(OpenPathInner::Ready { path_id, conn })
+57    }
+58
+59    pub(crate) fn rejected(err: PathError) -> Self {
+60        Self(OpenPathInner::Rejected { err })
+61    }
+62
+63    /// Returns the path ID of the new path being opened.
+64    ///
+65    /// If an error occurred before a path ID was allocated, `None` is returned.  In this
+66    /// case the future is ready and polling it will immediately yield the error.
+67    ///
+68    /// The returned value remains the same for the entire lifetime of this future.
+69    pub fn path_id(&self) -> Option<PathId> {
+70        match self.0 {
+71            OpenPathInner::Ongoing { path_id, .. } => Some(path_id),
+72            OpenPathInner::Rejected { .. } => None,
+73            OpenPathInner::Ready { path_id, .. } => Some(path_id),
+74        }
+75    }
+76}
+77
+78impl Future for OpenPath {
+79    type Output = Result<Path, PathError>;
+80    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+81        match self.get_mut().0 {
+82            OpenPathInner::Ongoing {
+83                ref mut opened,
+84                path_id,
+85                ref mut conn,
+86            } => match ready!(Pin::new(opened).poll_next(ctx)) {
+87                Some(value) => {
+88                    Poll::Ready(value.map(|_| Path::new_unchecked(conn.clone(), path_id)))
+89                }
+90                None => {
+91                    // This only happens if receiving a notification change failed, this means the
+92                    // sender was dropped. This generally should not happen so we use a transient
+93                    // error
+94                    Poll::Ready(Err(PathError::ValidationFailed))
+95                }
+96            },
+97            OpenPathInner::Ready {
+98                path_id,
+99                ref mut conn,
+100            } => Poll::Ready(Ok(Path::new_unchecked(conn.clone(), path_id))),
+101            OpenPathInner::Rejected { err } => Poll::Ready(Err(err)),
+102        }
+103    }
+104}
+105
+106/// An open network transmission within a multipath-enabled connection.
+107///
+108/// As long as a [`Path`] or [`WeakPathHandle`] is alive, it is ensured that the
+109/// [`PathStats`] for this path are not dropped even after the path is abandoned.
+110///
+111/// [`WeakPathHandle`]: crate::path::WeakPathHandle
+112#[derive(Debug)]
+113pub struct Path {
+114    id: PathId,
+115    conn: ConnectionRef,
+116}
+117
+118impl Clone for Path {
+119    fn clone(&self) -> Self {
+120        self.conn
+121            .state
+122            .lock("Path::clone")
+123            .increment_path_refs(self.id);
+124        Self {
+125            id: self.id,
+126            conn: self.conn.clone(),
+127        }
+128    }
+129}
+130
+131impl Drop for Path {
+132    fn drop(&mut self) {
+133        let mut state = self.conn.state.lock("Path::drop");
+134        state.decrement_path_refs(self.id);
+135    }
+136}
+137
+138impl Path {
+139    /// Returns a [`Path`] for a path id, after checking that the path is not closed.
+140    pub(crate) fn new(conn: &ConnectionRef, id: PathId) -> Option<Self> {
+141        {
+142            let mut state = conn.state.lock("Path::new");
+143            // TODO(flub): Using this to know if the path still exists is... hacky.
+144            state.inner.path_status(id).ok()?;
+145            state.increment_path_refs(id);
+146        }
+147        Some(Self {
+148            id,
+149            conn: conn.clone(),
+150        })
+151    }
+152
+153    /// Returns a [`Path`] for a path id without checking if the path exists or is closed.
+154    fn new_unchecked(conn: ConnectionRef, id: PathId) -> Self {
+155        conn.state
+156            .lock("Path::new_unchecked")
+157            .increment_path_refs(id);
+158        Self { id, conn }
+159    }
+160
+161    /// Returns a [`WeakPathHandle`] for this path.
+162    ///
+163    /// Holding a [`WeakPathHandle`] does not keep a connection alive, but ensures that the
+164    /// path's stats are not dropped until the underlying connection is dropped, even if the
+165    /// path is abandoned.
+166    pub fn weak_handle(&self) -> WeakPathHandle {
+167        self.conn
+168            .state
+169            .lock("Path::weak_handle")
+170            .increment_path_refs(self.id);
+171        WeakPathHandle {
+172            id: self.id,
+173            conn: self.conn.weak_handle(),
+174        }
+175    }
+176
+177    /// The [`PathId`] of this path.
+178    pub fn id(&self) -> PathId {
+179        self.id
+180    }
+181
+182    /// The current local [`PathStatus`] of this path.
+183    pub fn status(&self) -> Result<PathStatus, ClosedPath> {
+184        self.conn
+185            .state
+186            .lock("path status")
+187            .inner
+188            .path_status(self.id)
+189    }
+190
+191    /// Sets the [`PathStatus`] of this path.
+192    pub fn set_status(&self, status: PathStatus) -> Result<(), SetPathStatusError> {
+193        self.conn
+194            .state
+195            .lock("set path status")
+196            .inner
+197            .set_path_status(self.id, status)?;
+198        Ok(())
+199    }
+200
+201    /// Returns the [`PathStats`] for this path.
+202    pub fn stats(&self) -> PathStats {
+203        // The `expect` is safe:
+204        // - `Path` can only be created for non-closed paths.
+205        // - `Path` and its clones or `WeakPathHandle`s all increment the connection state's `path_ref`
+206        //   reference counter
+207        // - As long as a path is not abandoned, its stats are available from `proto::Connection`
+208        // - If a path is abandoned, the `crate::Connection` stores the final stats as long as
+209        //   the path's refcount is not 0
+210        // - Therefore, we always get stats here.
+211        self.conn
+212            .state
+213            .lock("Path::stats")
+214            .path_stats(self.id)
+215            .expect("either path stats or discarded path stats are always set as long as Path is not dropped")
+216    }
+217
+218    /// Closes this path.
+219    ///
+220    /// The path is immediately considered closed by the local endpoint. Once the state is removed,
+221    /// after a short period of time for any in-flight packets, a [`PathEvent::Abandoned`] is
+222    /// returned.
+223    pub fn close(&self) -> Result<(), ClosePathError> {
+224        let mut state = self.conn.state.lock("close_path");
+225        state.inner.close_path(
+226            crate::Instant::now(),
+227            self.id,
+228            TransportErrorCode::APPLICATION_ABANDON_PATH.into(),
+229        )
+230    }
+231
+232    /// Sets the keep_alive_interval for a specific path
+233    ///
+234    /// See [`TransportConfig::default_path_keep_alive_interval`] for details.
+235    ///
+236    /// Returns the previous value of the setting.
+237    ///
+238    /// [`TransportConfig::default_path_keep_alive_interval`]: crate::TransportConfig::default_path_keep_alive_interval
+239    pub fn set_max_idle_timeout(
+240        &self,
+241        timeout: Option<Duration>,
+242    ) -> Result<Option<Duration>, ClosedPath> {
+243        let mut state = self.conn.state.lock("path_set_max_idle_timeout");
+244        state.inner.set_path_max_idle_timeout(self.id, timeout)
+245    }
+246
+247    /// Sets the keep_alive_interval for a specific path
+248    ///
+249    /// See [`TransportConfig::default_path_keep_alive_interval`] for details.
+250    ///
+251    /// Returns the previous value of the setting.
+252    ///
+253    /// [`TransportConfig::default_path_keep_alive_interval`]: crate::TransportConfig::default_path_keep_alive_interval
+254    pub fn set_keep_alive_interval(
+255        &self,
+256        interval: Option<Duration>,
+257    ) -> Result<Option<Duration>, ClosedPath> {
+258        let mut state = self.conn.state.lock("path_set_keep_alive_interval");
+259        state.inner.set_path_keep_alive_interval(self.id, interval)
+260    }
+261
+262    /// Track changes on our external address as reported by the peer.
+263    ///
+264    /// If the address-discovery extension is not negotiated, the stream will never return.
+265    pub fn observed_external_addr(&self) -> Result<AddressDiscovery, ClosedPath> {
+266        let state = self.conn.state.lock("per_path_observed_address");
+267        let path_events = state.path_events.subscribe();
+268        let initial_value = state.inner.path_observed_address(self.id)?;
+269        Ok(AddressDiscovery::new(
+270            self.id,
+271            path_events,
+272            initial_value,
+273            state.runtime.clone(),
+274        ))
+275    }
+276
+277    /// The peer's UDP address for this path.
+278    pub fn remote_address(&self) -> Result<SocketAddr, ClosedPath> {
+279        let state = self.conn.state.lock("per_path_remote_address");
+280        Ok(state.inner.network_path(self.id)?.remote)
+281    }
+282
+283    /// Ping the remote endpoint over this path.
+284    pub fn ping(&self) -> Result<(), ClosedPath> {
+285        let mut state = self.conn.state.lock("ping");
+286        state.inner.ping_path(self.id)
+287    }
+288}
+289
+290impl PartialEq for Path {
+291    fn eq(&self, other: &Self) -> bool {
+292        self.id == other.id && self.conn.stable_id() == other.conn.stable_id()
+293    }
+294}
+295
+296/// Weak handle for a [`Path`] that does not keep the connection alive.
+297///
+298/// As long as a [`WeakPathHandle`] for a path exists, that path's final stats will not be dropped even if
+299/// the path was abandoned.
+300///
+301/// The [`WeakPathHandle`] can be upgraded to a [`Path`] as long as its [`Connection`] has not been dropped.
+302///
+303/// [`Connection`]: crate::Connection
+304#[derive(Debug)]
+305pub struct WeakPathHandle {
+306    id: PathId,
+307    conn: WeakConnectionHandle,
+308}
+309
+310impl Clone for WeakPathHandle {
+311    fn clone(&self) -> Self {
+312        if let Some(conn) = self.conn.upgrade_to_ref() {
+313            conn.state
+314                .lock("WeakPathHandle::clone")
+315                .increment_path_refs(self.id);
+316        }
+317        Self {
+318            id: self.id,
+319            conn: self.conn.clone(),
+320        }
+321    }
+322}
+323
+324impl PartialEq for WeakPathHandle {
+325    fn eq(&self, other: &Self) -> bool {
+326        self.id == other.id && self.conn.is_same_connection(&other.conn)
+327    }
+328}
+329
+330impl Eq for WeakPathHandle {}
+331
+332impl Drop for WeakPathHandle {
+333    fn drop(&mut self) {
+334        if let Some(conn) = self.conn.upgrade_to_ref() {
+335            conn.state
+336                .lock("WeakPathHandle::drop")
+337                .decrement_path_refs(self.id);
+338        }
+339    }
+340}
+341
+342impl WeakPathHandle {
+343    /// Returns the [`PathId`] of this path.
+344    pub fn id(&self) -> PathId {
+345        self.id
+346    }
+347
+348    /// Upgrades to a [`Path`].
+349    ///
+350    /// Returns `None` if the connection was dropped.
+351    pub fn upgrade(&self) -> Option<Path> {
+352        let conn = self.conn.upgrade_to_ref()?;
+353        Some(Path::new_unchecked(conn, self.id))
+354    }
+355}
+356
+357/// Stream produced by [`Path::observed_external_addr`]
+358///
+359/// This will always return the external address most recently reported by the remote over this
+360/// path. If the extension is not negotiated, this stream will never return.
+361// TODO(@divma): provide a way to check if the extension is negotiated.
+362pub struct AddressDiscovery {
+363    watcher: WatchStream<SocketAddr>,
+364}
+365
+366impl AddressDiscovery {
+367    pub(super) fn new(
+368        path_id: PathId,
+369        mut path_events: tokio::sync::broadcast::Receiver<PathEvent>,
+370        initial_value: Option<SocketAddr>,
+371        runtime: Arc<dyn Runtime>,
+372    ) -> Self {
+373        let (tx, rx) = watch::channel(initial_value.unwrap_or_else(||
+374                // if the dummy value is used, it will be ignored
+375                SocketAddr::new([0, 0, 0, 0].into(), 0)));
+376        let filter = async move {
+377            loop {
+378                match path_events.recv().await {
+379                    Ok(PathEvent::ObservedAddr { id, addr: observed }) if id == path_id => {
+380                        tx.send_if_modified(|addr| {
+381                            let old = std::mem::replace(addr, observed);
+382                            old != *addr
+383                        });
+384                    }
+385                    Ok(PathEvent::Discarded { id, .. }) if id == path_id => {
+386                        // If the path is closed, terminate the stream
+387                        break;
+388                    }
+389                    Ok(_) => {
+390                        // ignore any other event
+391                    }
+392                    Err(_) => {
+393                        // A lagged error should never happen since this (detached) task is
+394                        // constantly reading from the channel. Therefore, if an error does happen,
+395                        // the stream can terminate
+396                        break;
+397                    }
+398                }
+399            }
+400        };
+401
+402        let watcher = if initial_value.is_some() {
+403            WatchStream::new(rx)
+404        } else {
+405            WatchStream::from_changes(rx)
+406        };
+407
+408        runtime.spawn(Box::pin(filter));
+409        // TODO(@divma): check if there's a way to ensure the future ends. AbortHandle is not an
+410        // option
+411        Self { watcher }
+412    }
+413}
+414
+415impl Stream for AddressDiscovery {
+416    type Item = SocketAddr;
+417
+418    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+419        Pin::new(&mut self.watcher).poll_next(cx)
+420    }
+421}
\ No newline at end of file diff --git a/pr/509/docs/src/noq/recv_stream.rs.html b/pr/509/docs/src/noq/recv_stream.rs.html new file mode 100644 index 000000000..2f99b5538 --- /dev/null +++ b/pr/509/docs/src/noq/recv_stream.rs.html @@ -0,0 +1,759 @@ +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 fn poll_read_buf(
+131        &mut self,
+132        cx: &mut Context<'_>,
+133        buf: &mut ReadBuf<'_>,
+134    ) -> Poll<Result<(), ReadError>> {
+135        if buf.remaining() == 0 {
+136            return Poll::Ready(Ok(()));
+137        }
+138
+139        self.poll_read_generic(cx, true, |chunks| {
+140            let mut read = false;
+141            loop {
+142                if buf.remaining() == 0 {
+143                    // We know `read` is `true` because `buf.remaining()` was not 0 before
+144                    return ReadStatus::Readable(());
+145                }
+146
+147                match chunks.next(buf.remaining()) {
+148                    Ok(Some(chunk)) => {
+149                        buf.put_slice(&chunk.bytes);
+150                        read = true;
+151                    }
+152                    res => return (if read { Some(()) } else { None }, res.err()).into(),
+153                }
+154            }
+155        })
+156        .map(|res| res.map(|_| ()))
+157    }
+158
+159    /// Read the next segment of data
+160    ///
+161    /// Yields `None` if the stream was finished. Otherwise, yields a segment of data and its
+162    /// offset in the stream. The chunk's offset will be immediately after
+163    /// the last data yielded by [`RecvStream::read`] or [`RecvStream::read_chunk`].
+164    ///
+165    /// For unordered reads, convert the stream into an unordered stream using [`Self::into_unordered`].
+166    ///
+167    /// Slightly more efficient than [`RecvStream::read`] due to not copying. Chunk boundaries do not correspond
+168    /// to peer writes, and hence cannot be used as framing.
+169    ///
+170    /// This operation is cancel-safe.
+171    pub async fn read_chunk(&mut self, max_length: usize) -> Result<Option<Chunk>, ReadError> {
+172        ReadChunk {
+173            stream: self,
+174            max_length,
+175            ordered: true,
+176        }
+177        .await
+178    }
+179
+180    /// Attempts to read a chunk from the stream.
+181    ///
+182    /// On success, returns `Poll::Ready(Ok(Some(chunk)))`. If `Poll::Ready(Ok(None))`
+183    /// is returned, it implies that EOF has been reached.
+184    ///
+185    /// If no data is available for reading, the method returns `Poll::Pending`
+186    /// and arranges for the current task (via cx.waker()) to receive a notification
+187    /// when the stream becomes readable or is closed.
+188    fn poll_read_chunk(
+189        &mut self,
+190        cx: &mut Context<'_>,
+191        max_length: usize,
+192        ordered: bool,
+193    ) -> Poll<Result<Option<Chunk>, ReadError>> {
+194        self.poll_read_generic(cx, ordered, |chunks| match chunks.next(max_length) {
+195            Ok(Some(chunk)) => ReadStatus::Readable(chunk),
+196            res => (None, res.err()).into(),
+197        })
+198    }
+199
+200    /// Read the next segments of data
+201    ///
+202    /// Fills `bufs` with the segments of data beginning immediately after the
+203    /// last data yielded by `read` or `read_chunk`, or `None` if the stream was
+204    /// finished.
+205    ///
+206    /// Slightly more efficient than `read` due to not copying. Chunk boundaries
+207    /// do not correspond to peer writes, and hence cannot be used as framing.
+208    ///
+209    /// This operation is cancel-safe.
+210    pub async fn read_chunks(&mut self, bufs: &mut [Bytes]) -> Result<Option<usize>, ReadError> {
+211        ReadChunks { stream: self, bufs }.await
+212    }
+213
+214    /// Foundation of [`Self::read_chunks`]
+215    fn poll_read_chunks(
+216        &mut self,
+217        cx: &mut Context<'_>,
+218        bufs: &mut [Bytes],
+219    ) -> Poll<Result<Option<usize>, ReadError>> {
+220        if bufs.is_empty() {
+221            return Poll::Ready(Ok(Some(0)));
+222        }
+223
+224        self.poll_read_generic(cx, true, |chunks| {
+225            let mut read = 0;
+226            loop {
+227                if read >= bufs.len() {
+228                    // We know `read > 0` because `bufs` cannot be empty here
+229                    return ReadStatus::Readable(read);
+230                }
+231
+232                match chunks.next(usize::MAX) {
+233                    Ok(Some(chunk)) => {
+234                        bufs[read] = chunk.bytes;
+235                        read += 1;
+236                    }
+237                    res => return (if read == 0 { None } else { Some(read) }, res.err()).into(),
+238                }
+239            }
+240        })
+241    }
+242
+243    /// Convenience method to read all remaining data into a buffer
+244    ///
+245    /// Fails with [`ReadToEndError::TooLong`] on reading more than `size_limit` bytes, discarding
+246    /// all data read. Uses unordered reads to be more efficient than using `AsyncRead` would
+247    /// allow. `size_limit` should be set to limit worst-case memory use.
+248    ///
+249    /// If unordered reads have already been made, the resulting buffer may have gaps containing
+250    /// arbitrary data.
+251    ///
+252    /// This operation is *not* cancel-safe.
+253    ///
+254    /// [`ReadToEndError::TooLong`]: crate::ReadToEndError::TooLong
+255    pub async fn read_to_end(&mut self, size_limit: usize) -> Result<Vec<u8>, ReadToEndError> {
+256        ReadToEnd {
+257            stream: self,
+258            size_limit,
+259            read: Vec::new(),
+260            start: u64::MAX,
+261            end: 0,
+262        }
+263        .await
+264    }
+265
+266    /// Stop accepting data
+267    ///
+268    /// Discards unread data and notifies the peer to stop transmitting. Once stopped, further
+269    /// attempts to operate on a stream will yield `ClosedStream` errors.
+270    pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+271        let mut conn = self.conn.state.lock("RecvStream::stop");
+272        if self.is_0rtt && conn.check_0rtt().is_err() {
+273            return Ok(());
+274        }
+275        conn.inner.recv_stream(self.stream).stop(error_code)?;
+276        conn.wake();
+277        self.all_data_read = true;
+278        Ok(())
+279    }
+280
+281    /// Check if this stream has been opened during 0-RTT.
+282    ///
+283    /// In which case any non-idempotent request should be considered dangerous at the application
+284    /// level. Because read data is subject to replay attacks.
+285    pub fn is_0rtt(&self) -> bool {
+286        self.is_0rtt
+287    }
+288
+289    /// Get the identity of this stream
+290    pub fn id(&self) -> StreamId {
+291        self.stream
+292    }
+293
+294    /// Completes when the stream has been reset by the peer or otherwise closed
+295    ///
+296    /// Yields `Some` with the reset error code when the stream is reset by the peer. Yields `None`
+297    /// when the stream was previously [`stop()`](Self::stop)ed, or when the stream was
+298    /// [`finish()`](crate::SendStream::finish)ed by the peer and all data has been received, after
+299    /// which it is no longer meaningful for the stream to be reset.
+300    ///
+301    /// This operation is cancel-safe.
+302    pub async fn received_reset(&mut self) -> Result<Option<VarInt>, ResetError> {
+303        poll_fn(|cx| {
+304            let mut conn = self.conn.state.lock("RecvStream::reset");
+305            if self.is_0rtt && conn.check_0rtt().is_err() {
+306                return Poll::Ready(Err(ResetError::ZeroRttRejected));
+307            }
+308
+309            if let Some(code) = self.reset {
+310                return Poll::Ready(Ok(Some(code)));
+311            }
+312
+313            match conn.inner.recv_stream(self.stream).received_reset() {
+314                Err(_) => Poll::Ready(Ok(None)),
+315                Ok(Some(error_code)) => {
+316                    // Stream state has just now been freed, so the connection may need to issue new
+317                    // stream ID flow control credit
+318                    conn.wake();
+319                    Poll::Ready(Ok(Some(error_code)))
+320                }
+321                Ok(None) => {
+322                    if let Some(e) = &conn.error {
+323                        return Poll::Ready(Err(e.clone().into()));
+324                    }
+325                    // Resets always notify readers, since a reset is an immediate read error. We
+326                    // could introduce a dedicated channel to reduce the risk of spurious wakeups,
+327                    // but that increased complexity is probably not justified, as an application
+328                    // that is expecting a reset is not likely to receive large amounts of data.
+329                    conn.blocked_readers.insert(self.stream, cx.waker().clone());
+330                    Poll::Pending
+331                }
+332            }
+333        })
+334        .await
+335    }
+336
+337    /// Handle common logic related to reading out of a receive stream
+338    ///
+339    /// This takes an `FnMut` closure that takes care of the actual reading process, matching
+340    /// the detailed read semantics for the calling function with a particular return type.
+341    /// The closure can read from the passed `&mut Chunks` and has to return the status after
+342    /// reading: the amount of data read, and the status after the final read call.
+343    fn poll_read_generic<T, U>(
+344        &mut self,
+345        cx: &mut Context<'_>,
+346        ordered: bool,
+347        mut read_fn: T,
+348    ) -> Poll<Result<Option<U>, ReadError>>
+349    where
+350        T: FnMut(&mut Chunks<'_>) -> ReadStatus<U>,
+351    {
+352        use proto::ReadError::*;
+353        if self.all_data_read {
+354            return Poll::Ready(Ok(None));
+355        }
+356
+357        let mut conn = self.conn.state.lock("RecvStream::poll_read");
+358        if self.is_0rtt {
+359            conn.check_0rtt().map_err(|()| ReadError::ZeroRttRejected)?;
+360        }
+361
+362        // If we stored an error during a previous call, return it now. This can happen if a
+363        // `read_fn` both wants to return data and also returns an error in its final stream status.
+364        let status = match self.reset {
+365            Some(code) => ReadStatus::Failed(None, Reset(code)),
+366            None => {
+367                let mut recv = conn.inner.recv_stream(self.stream);
+368                let mut chunks = recv.read(ordered).map_err(|e| match e {
+369                    ReadableError::ClosedStream => ReadError::ClosedStream,
+370                    ReadableError::IllegalOrderedRead => {
+371                        // We should never get here because the only way to do unordered reads is
+372                        // via UnorderedRecvStream, which allows only unordered reads. It is not
+373                        // possible to get a RecvStream from an UnorderedRecvStream.
+374                        unreachable!("ordered read after unordered read")
+375                    }
+376                })?;
+377                let status = read_fn(&mut chunks);
+378                if chunks.finalize().should_transmit() {
+379                    conn.wake();
+380                }
+381                status
+382            }
+383        };
+384
+385        match status {
+386            ReadStatus::Readable(read) => Poll::Ready(Ok(Some(read))),
+387            ReadStatus::Finished(read) => {
+388                self.all_data_read = true;
+389                Poll::Ready(Ok(read))
+390            }
+391            ReadStatus::Failed(read, Blocked) => match read {
+392                Some(val) => Poll::Ready(Ok(Some(val))),
+393                None => {
+394                    if let Some(ref x) = conn.error {
+395                        return Poll::Ready(Err(ReadError::ConnectionLost(x.clone())));
+396                    }
+397                    conn.blocked_readers.insert(self.stream, cx.waker().clone());
+398                    Poll::Pending
+399                }
+400            },
+401            ReadStatus::Failed(read, Reset(error_code)) => match read {
+402                None => {
+403                    self.all_data_read = true;
+404                    self.reset = Some(error_code);
+405                    Poll::Ready(Err(ReadError::Reset(error_code)))
+406                }
+407                done => {
+408                    self.reset = Some(error_code);
+409                    Poll::Ready(Ok(done))
+410                }
+411            },
+412        }
+413    }
+414
+415    /// Converts this stream into an unordered stream.
+416    pub fn into_unordered(self) -> UnorderedRecvStream {
+417        UnorderedRecvStream { inner: self }
+418    }
+419}
+420
+421/// A stream that can be used to receive data out-of-order.
+422///
+423/// Obtained by converting a [`RecvStream`] via [`RecvStream::into_unordered`].
+424///
+425/// This variant of `RecvStream` allows reading chunks of data *exclusively*
+426/// out of order. Once you have done an unordered read, ordered reads are no
+427/// longer possible since data may have been consumed out of order.
+428///
+429/// The stream state related fns like [`Self::id`], [`Self::is_0rtt`], [`Self::stop`], and
+430/// [`Self::received_reset`] behave exactly as on [`RecvStream`].
+431#[derive(Debug)]
+432pub struct UnorderedRecvStream {
+433    inner: RecvStream,
+434}
+435
+436impl UnorderedRecvStream {
+437    /// Reads the next segment of data.
+438    ///
+439    /// Yields `None` if the stream was finished. Otherwise, yields a segment of data and its
+440    /// offset in the stream. Segments may be received in any order, and the `Chunk`'s `offset`
+441    /// field can be used to determine ordering in the caller. Unordered reads are less prone
+442    /// to head-of-line blocking within a stream, but require the application to manage
+443    /// reassembling the original data.
+444    ///
+445    /// This operation is cancel-safe.
+446    pub async fn read_chunk(&mut self, max_length: usize) -> Result<Option<Chunk>, ReadError> {
+447        ReadChunk {
+448            stream: &mut self.inner,
+449            max_length,
+450            ordered: false,
+451        }
+452        .await
+453    }
+454
+455    /// Get the identity of this stream
+456    pub fn id(&self) -> StreamId {
+457        self.inner.id()
+458    }
+459
+460    /// Check if this stream has been opened during 0-RTT.
+461    ///
+462    /// In which case any non-idempotent request should be considered dangerous at the application
+463    /// level. Because read data is subject to replay attacks.
+464    pub fn is_0rtt(&self) -> bool {
+465        self.inner.is_0rtt()
+466    }
+467
+468    /// Stop accepting data
+469    ///
+470    /// Discards unread data and notifies the peer to stop transmitting. Once stopped, further
+471    /// attempts to operate on a stream will yield `ClosedStream` errors.
+472    pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+473        self.inner.stop(error_code)
+474    }
+475
+476    /// Completes when the stream has been reset by the peer or otherwise closed
+477    ///
+478    /// Yields `Some` with the reset error code when the stream is reset by the peer. Yields `None`
+479    /// when the stream was previously [`stop()`](Self::stop)ed, or when the stream was
+480    /// [`finish()`](crate::SendStream::finish)ed by the peer and all data has been received, after
+481    /// which it is no longer meaningful for the stream to be reset.
+482    ///
+483    /// This operation is cancel-safe.
+484    pub async fn received_reset(&mut self) -> Result<Option<VarInt>, ResetError> {
+485        self.inner.received_reset().await
+486    }
+487}
+488
+489enum ReadStatus<T> {
+490    Readable(T),
+491    Finished(Option<T>),
+492    Failed(Option<T>, proto::ReadError),
+493}
+494
+495impl<T> From<(Option<T>, Option<proto::ReadError>)> for ReadStatus<T> {
+496    fn from(status: (Option<T>, Option<proto::ReadError>)) -> Self {
+497        match status {
+498            (read, None) => Self::Finished(read),
+499            (read, Some(e)) => Self::Failed(read, e),
+500        }
+501    }
+502}
+503
+504/// Future produced by [`RecvStream::read_to_end()`].
+505///
+506/// [`RecvStream::read_to_end()`]: crate::RecvStream::read_to_end
+507struct ReadToEnd<'a> {
+508    stream: &'a mut RecvStream,
+509    read: Vec<(Bytes, u64)>,
+510    start: u64,
+511    end: u64,
+512    size_limit: usize,
+513}
+514
+515impl Future for ReadToEnd<'_> {
+516    type Output = Result<Vec<u8>, ReadToEndError>;
+517    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+518        loop {
+519            match ready!(self.stream.poll_read_chunk(cx, usize::MAX, false))? {
+520                Some(chunk) => {
+521                    self.start = self.start.min(chunk.offset);
+522                    let end = chunk.bytes.len() as u64 + chunk.offset;
+523                    if (end - self.start) > self.size_limit as u64 {
+524                        return Poll::Ready(Err(ReadToEndError::TooLong));
+525                    }
+526                    self.end = self.end.max(end);
+527                    self.read.push((chunk.bytes, chunk.offset));
+528                }
+529                None => {
+530                    if self.end == 0 {
+531                        // Never received anything
+532                        return Poll::Ready(Ok(Vec::new()));
+533                    }
+534                    let start = self.start;
+535                    let mut buffer = vec![0; (self.end - start) as usize];
+536                    for (data, offset) in self.read.drain(..) {
+537                        let offset = (offset - start) as usize;
+538                        buffer[offset..offset + data.len()].copy_from_slice(&data);
+539                    }
+540                    return Poll::Ready(Ok(buffer));
+541                }
+542            }
+543        }
+544    }
+545}
+546
+547/// Errors from [`RecvStream::read_to_end`]
+548#[derive(Debug, Error, Clone, PartialEq, Eq)]
+549pub enum ReadToEndError {
+550    /// An error occurred during reading
+551    #[error("read error: {0}")]
+552    Read(#[from] ReadError),
+553    /// The stream is larger than the user-supplied limit
+554    #[error("stream too long")]
+555    TooLong,
+556}
+557
+558#[cfg(feature = "futures-io")]
+559impl futures_io::AsyncRead for RecvStream {
+560    fn poll_read(
+561        self: Pin<&mut Self>,
+562        cx: &mut Context<'_>,
+563        buf: &mut [u8],
+564    ) -> Poll<io::Result<usize>> {
+565        let mut buf = ReadBuf::new(buf);
+566        ready!(Self::poll_read_buf(self.get_mut(), cx, &mut buf))?;
+567        Poll::Ready(Ok(buf.filled().len()))
+568    }
+569}
+570
+571impl tokio::io::AsyncRead for RecvStream {
+572    fn poll_read(
+573        self: Pin<&mut Self>,
+574        cx: &mut Context<'_>,
+575        buf: &mut ReadBuf<'_>,
+576    ) -> Poll<io::Result<()>> {
+577        ready!(Self::poll_read_buf(self.get_mut(), cx, buf))?;
+578        Poll::Ready(Ok(()))
+579    }
+580}
+581
+582impl Drop for RecvStream {
+583    fn drop(&mut self) {
+584        let mut conn = self.conn.state.lock("RecvStream::drop");
+585
+586        // clean up any previously registered wakers
+587        conn.blocked_readers.remove(&self.stream);
+588
+589        if conn.error.is_some() || (self.is_0rtt && conn.check_0rtt().is_err()) {
+590            return;
+591        }
+592        if !self.all_data_read {
+593            // Ignore ClosedStream errors
+594            let _ = conn.inner.recv_stream(self.stream).stop(0u32.into());
+595            conn.wake();
+596        }
+597    }
+598}
+599
+600/// Errors that arise from reading from a stream.
+601#[derive(Debug, Error, Clone, PartialEq, Eq)]
+602pub enum ReadError {
+603    /// The peer abandoned transmitting data on this stream
+604    ///
+605    /// Carries an application-defined error code.
+606    #[error("stream reset by peer: error {0}")]
+607    Reset(VarInt),
+608    /// The connection was lost
+609    #[error("connection lost")]
+610    ConnectionLost(#[from] ConnectionError),
+611    /// The stream has already been stopped, finished, or reset
+612    #[error("closed stream")]
+613    ClosedStream,
+614    /// This was a 0-RTT stream and the server rejected it
+615    ///
+616    /// Can only occur on clients for 0-RTT streams, which can be opened using
+617    /// [`Connecting::into_0rtt()`].
+618    ///
+619    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+620    #[error("0-RTT rejected")]
+621    ZeroRttRejected,
+622}
+623
+624impl From<ResetError> for ReadError {
+625    fn from(e: ResetError) -> Self {
+626        match e {
+627            ResetError::ConnectionLost(e) => Self::ConnectionLost(e),
+628            ResetError::ZeroRttRejected => Self::ZeroRttRejected,
+629        }
+630    }
+631}
+632
+633impl From<ReadError> for io::Error {
+634    fn from(x: ReadError) -> Self {
+635        use ReadError::*;
+636        let kind = match x {
+637            Reset { .. } | ZeroRttRejected => io::ErrorKind::ConnectionReset,
+638            ConnectionLost(_) | ClosedStream => io::ErrorKind::NotConnected,
+639        };
+640        Self::new(kind, x)
+641    }
+642}
+643
+644/// Errors that arise while waiting for a stream to be reset
+645#[derive(Debug, Error, Clone, PartialEq, Eq)]
+646pub enum ResetError {
+647    /// The connection was lost
+648    #[error("connection lost")]
+649    ConnectionLost(#[from] ConnectionError),
+650    /// This was a 0-RTT stream and the server rejected it
+651    ///
+652    /// Can only occur on clients for 0-RTT streams, which can be opened using
+653    /// [`Connecting::into_0rtt()`].
+654    ///
+655    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+656    #[error("0-RTT rejected")]
+657    ZeroRttRejected,
+658}
+659
+660impl From<ResetError> for io::Error {
+661    fn from(x: ResetError) -> Self {
+662        use ResetError::*;
+663        let kind = match x {
+664            ZeroRttRejected => io::ErrorKind::ConnectionReset,
+665            ConnectionLost(_) => io::ErrorKind::NotConnected,
+666        };
+667        Self::new(kind, x)
+668    }
+669}
+670
+671/// Future produced by [`RecvStream::read()`].
+672///
+673/// [`RecvStream::read()`]: crate::RecvStream::read
+674struct Read<'a> {
+675    stream: &'a mut RecvStream,
+676    buf: ReadBuf<'a>,
+677}
+678
+679impl Future for Read<'_> {
+680    type Output = Result<Option<usize>, ReadError>;
+681
+682    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+683        let this = self.get_mut();
+684        ready!(this.stream.poll_read_buf(cx, &mut this.buf))?;
+685        match this.buf.filled().len() {
+686            0 if this.buf.capacity() != 0 => Poll::Ready(Ok(None)),
+687            n => Poll::Ready(Ok(Some(n))),
+688        }
+689    }
+690}
+691
+692/// Future produced by [`RecvStream::read_exact()`].
+693///
+694/// [`RecvStream::read_exact()`]: crate::RecvStream::read_exact
+695struct ReadExact<'a> {
+696    stream: &'a mut RecvStream,
+697    buf: ReadBuf<'a>,
+698}
+699
+700impl Future for ReadExact<'_> {
+701    type Output = Result<(), ReadExactError>;
+702    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+703        let this = self.get_mut();
+704        let mut remaining = this.buf.remaining();
+705        while remaining > 0 {
+706            ready!(this.stream.poll_read_buf(cx, &mut this.buf))?;
+707            let new = this.buf.remaining();
+708            if new == remaining {
+709                return Poll::Ready(Err(ReadExactError::FinishedEarly(this.buf.filled().len())));
+710            }
+711            remaining = new;
+712        }
+713        Poll::Ready(Ok(()))
+714    }
+715}
+716
+717/// Errors that arise from reading from a stream.
+718#[derive(Debug, Error, Clone, PartialEq, Eq)]
+719pub enum ReadExactError {
+720    /// The stream finished before all bytes were read
+721    #[error("stream finished early ({0} bytes read)")]
+722    FinishedEarly(usize),
+723    /// A read error occurred
+724    #[error(transparent)]
+725    ReadError(#[from] ReadError),
+726}
+727
+728/// Future produced by [`RecvStream::read_chunk()`].
+729///
+730/// [`RecvStream::read_chunk()`]: crate::RecvStream::read_chunk
+731struct ReadChunk<'a> {
+732    stream: &'a mut RecvStream,
+733    max_length: usize,
+734    ordered: bool,
+735}
+736
+737impl Future for ReadChunk<'_> {
+738    type Output = Result<Option<Chunk>, ReadError>;
+739    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+740        let (max_length, ordered) = (self.max_length, self.ordered);
+741        self.stream.poll_read_chunk(cx, max_length, ordered)
+742    }
+743}
+744
+745/// Future produced by [`RecvStream::read_chunks()`].
+746///
+747/// [`RecvStream::read_chunks()`]: crate::RecvStream::read_chunks
+748struct ReadChunks<'a> {
+749    stream: &'a mut RecvStream,
+750    bufs: &'a mut [Bytes],
+751}
+752
+753impl Future for ReadChunks<'_> {
+754    type Output = Result<Option<usize>, ReadError>;
+755    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+756        let this = self.get_mut();
+757        this.stream.poll_read_chunks(cx, this.bufs)
+758    }
+759}
\ No newline at end of file diff --git a/pr/509/docs/src/noq/runtime/mod.rs.html b/pr/509/docs/src/noq/runtime/mod.rs.html new file mode 100644 index 000000000..7556ac37d --- /dev/null +++ b/pr/509/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/509/docs/src/noq/runtime/smol.rs.html b/pr/509/docs/src/noq/runtime/smol.rs.html new file mode 100644 index 000000000..b4c82ed2b --- /dev/null +++ b/pr/509/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/509/docs/src/noq/runtime/tokio.rs.html b/pr/509/docs/src/noq/runtime/tokio.rs.html new file mode 100644 index 000000000..c7dd8b409 --- /dev/null +++ b/pr/509/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/509/docs/src/noq/send_stream.rs.html b/pr/509/docs/src/noq/send_stream.rs.html new file mode 100644 index 000000000..e27304db2 --- /dev/null +++ b/pr/509/docs/src/noq/send_stream.rs.html @@ -0,0 +1,482 @@ +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, Written};
+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    /// Write a slice of [`Bytes`] into this stream, returning how much was written
+83    ///
+84    /// Bytes to try to write are provided to this method as an array of cheaply cloneable chunks.
+85    /// Unless this method errors, it waits until some amount of those bytes can be written into
+86    /// this stream, and then writes as much as it can without waiting again. Due to congestion and
+87    /// flow control, this may be less than the total number of bytes.
+88    ///
+89    /// On success, this method both mutates `bufs` and yields an informative [`Written`] struct
+90    /// indicating how much was written:
+91    ///
+92    /// - [`Bytes`] chunks that were fully written are mutated to be [empty](Bytes::is_empty).
+93    /// - If a [`Bytes`] chunk was partially written, it is [split to](Bytes::split_to) contain
+94    ///   only the suffix of bytes that were not written.
+95    /// - The yielded [`Written`] struct indicates how many chunks were fully written as well as
+96    ///   how many bytes were written.
+97    ///
+98    /// # Cancel safety
+99    ///
+100    /// This method is cancellation safe. If this does not resolve, no bytes were written.
+101    pub async fn write_chunks(&mut self, bufs: &mut [Bytes]) -> Result<Written, WriteError> {
+102        poll_fn(|cx| self.execute_poll(cx, |s| s.write_chunks(bufs))).await
+103    }
+104
+105    /// Write a single [`Bytes`] into this stream in its entirety
+106    ///
+107    /// Bytes to write are provided to this method as an single cheaply cloneable chunk. This
+108    /// method repeatedly calls [`write_chunks`](Self::write_chunks) until all bytes are written,
+109    /// or an error occurs.
+110    ///
+111    /// # Cancel safety
+112    ///
+113    /// This method is *not* cancellation safe. Even if this does not resolve, some bytes may have
+114    /// been written when previously polled.
+115    pub async fn write_chunk(&mut self, buf: Bytes) -> Result<(), WriteError> {
+116        self.write_all_chunks(&mut [buf]).await?;
+117        Ok(())
+118    }
+119
+120    /// Write 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_chunks`](Self::write_chunks) until all bytes are written,
+124    /// or an error occurs. This method mutates `bufs` by mutating all chunks to be
+125    /// [empty](Bytes::is_empty).
+126    ///
+127    /// # Cancel safety
+128    ///
+129    /// This method is *not* cancellation safe. Even if this does not resolve, some bytes may have
+130    /// been written when previously polled.
+131    pub async fn write_all_chunks(&mut self, mut bufs: &mut [Bytes]) -> Result<(), WriteError> {
+132        while !bufs.is_empty() {
+133            let written = self.write_chunks(bufs).await?;
+134            bufs = &mut bufs[written.chunks..];
+135        }
+136        Ok(())
+137    }
+138
+139    fn execute_poll<F, R>(
+140        &mut self,
+141        cx: &mut Context<'_>,
+142        write_fn: F,
+143    ) -> Poll<Result<R, WriteError>>
+144    where
+145        F: FnOnce(&mut proto::SendStream<'_>) -> Result<R, proto::WriteError>,
+146    {
+147        use proto::WriteError::*;
+148        let mut conn = self.conn.state.lock("SendStream::poll_write");
+149        if self.is_0rtt {
+150            conn.check_0rtt()
+151                .map_err(|()| WriteError::ZeroRttRejected)?;
+152        }
+153        if let Some(ref x) = conn.error {
+154            return Poll::Ready(Err(WriteError::ConnectionLost(x.clone())));
+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                return Poll::Pending;
+162            }
+163            Err(Stopped(error_code)) => {
+164                return Poll::Ready(Err(WriteError::Stopped(error_code)));
+165            }
+166            Err(ClosedStream) => {
+167                return Poll::Ready(Err(WriteError::ClosedStream));
+168            }
+169        };
+170
+171        conn.wake();
+172        Poll::Ready(Ok(result))
+173    }
+174
+175    /// Notify the peer that no more data will ever be written to this stream
+176    ///
+177    /// It is an error to write to a [`SendStream`] after `finish()`ing it. [`reset()`](Self::reset)
+178    /// may still be called after `finish` to abandon transmission of any stream data that might
+179    /// still be buffered.
+180    ///
+181    /// To wait for the peer to receive all buffered stream data, see [`stopped()`](Self::stopped).
+182    ///
+183    /// May fail if [`finish()`](Self::finish) or [`reset()`](Self::reset) was previously
+184    /// called. This error is harmless and serves only to indicate that the caller may have
+185    /// incorrect assumptions about the stream's state.
+186    pub fn finish(&mut self) -> Result<(), ClosedStream> {
+187        let mut conn = self.conn.state.lock("finish");
+188        match conn.inner.send_stream(self.stream).finish() {
+189            Ok(()) => {
+190                conn.wake();
+191                Ok(())
+192            }
+193            Err(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            Err(FinishError::Stopped(_)) => Ok(()),
+197        }
+198    }
+199
+200    /// Close the send stream immediately.
+201    ///
+202    /// No new data can be written after calling this method. Locally buffered data is dropped, and
+203    /// previously transmitted data will no longer be retransmitted if lost. If an attempt has
+204    /// already been made to finish the stream, the peer may still receive all written data.
+205    ///
+206    /// May fail if [`finish()`](Self::finish) or [`reset()`](Self::reset) was previously
+207    /// called. This error is harmless and serves only to indicate that the caller may have
+208    /// incorrect assumptions about the stream's state.
+209    pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+210        let mut conn = self.conn.state.lock("SendStream::reset");
+211        if self.is_0rtt && conn.check_0rtt().is_err() {
+212            return Ok(());
+213        }
+214        conn.inner.send_stream(self.stream).reset(error_code)?;
+215        conn.wake();
+216        Ok(())
+217    }
+218
+219    /// Set the priority of the send stream
+220    ///
+221    /// Every send stream has an initial priority of 0. Locally buffered data from streams with
+222    /// higher priority will be transmitted before data from streams with lower priority. Changing
+223    /// the priority of a stream with pending data may only take effect after that data has been
+224    /// transmitted. Using many different priority levels per connection may have a negative
+225    /// impact on performance.
+226    pub fn set_priority(&self, priority: i32) -> Result<(), ClosedStream> {
+227        let mut conn = self.conn.state.lock("SendStream::set_priority");
+228        conn.inner.send_stream(self.stream).set_priority(priority)?;
+229        Ok(())
+230    }
+231
+232    /// Get the priority of the send stream
+233    pub fn priority(&self) -> Result<i32, ClosedStream> {
+234        let mut conn = self.conn.state.lock("SendStream::priority");
+235        conn.inner.send_stream(self.stream).priority()
+236    }
+237
+238    /// Completes when the peer stops the stream or reads the stream to completion
+239    ///
+240    /// Yields `Some` with the stop error code if the peer stops the stream. Yields `None` if the
+241    /// local side [`finish()`](Self::finish)es the stream and then the peer acknowledges receipt
+242    /// of all stream data (although not necessarily the processing of it), after which the peer
+243    /// closing the stream is no longer meaningful.
+244    ///
+245    /// For a variety of reasons, the peer may not send acknowledgements immediately upon receiving
+246    /// data. As such, relying on `stopped` to know when the peer has read a stream to completion
+247    /// may introduce more latency than using an application-level response of some sort.
+248    pub fn stopped(&self) -> Stopped {
+249        let notified = {
+250            // Create an `OwnedNotified` to move into the future. By creating it before the first poll,
+251            // we make sure that we don't miss any notifications.
+252            let mut conn = self.conn.state.lock("SendStream::stopped");
+253            conn.stopped
+254                .entry(self.stream)
+255                .or_default()
+256                .clone()
+257                .notified_owned()
+258        };
+259        Stopped {
+260            conn: self.conn.clone(),
+261            stream: self.stream,
+262            is_0rtt: self.is_0rtt,
+263            notified,
+264        }
+265    }
+266
+267    /// Get the identity of this stream
+268    pub fn id(&self) -> StreamId {
+269        self.stream
+270    }
+271
+272    /// Attempt to write bytes from buf into the stream.
+273    ///
+274    /// On success, returns Poll::Ready(Ok(num_bytes_written)).
+275    ///
+276    /// If the stream is not ready for writing, the method returns Poll::Pending and arranges
+277    /// for the current task (via cx.waker().wake_by_ref()) to receive a notification when the
+278    /// stream becomes writable or is closed.
+279    pub fn poll_write(
+280        self: Pin<&mut Self>,
+281        cx: &mut Context<'_>,
+282        buf: &[u8],
+283    ) -> Poll<Result<usize, WriteError>> {
+284        pin!(self.get_mut().write(buf)).as_mut().poll(cx)
+285    }
+286}
+287
+288/// Check if a send stream is stopped.
+289///
+290/// Returns `Some` if the stream is stopped or the connection is closed.
+291/// Returns `None` if the stream is not stopped.
+292fn send_stream_stopped(
+293    conn: &mut State,
+294    stream: StreamId,
+295    is_0rtt: bool,
+296) -> Option<Result<Option<VarInt>, StoppedError>> {
+297    if is_0rtt && conn.check_0rtt().is_err() {
+298        return Some(Err(StoppedError::ZeroRttRejected));
+299    }
+300    match conn.inner.send_stream(stream).stopped() {
+301        Err(ClosedStream { .. }) => Some(Ok(None)),
+302        Ok(Some(error_code)) => Some(Ok(Some(error_code))),
+303        Ok(None) => conn.error.clone().map(|error| Err(error.into())),
+304    }
+305}
+306
+307#[cfg(feature = "futures-io")]
+308impl futures_io::AsyncWrite for SendStream {
+309    fn poll_write(
+310        self: Pin<&mut Self>,
+311        cx: &mut Context<'_>,
+312        buf: &[u8],
+313    ) -> Poll<io::Result<usize>> {
+314        self.poll_write(cx, buf).map_err(Into::into)
+315    }
+316
+317    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+318        Poll::Ready(Ok(()))
+319    }
+320
+321    fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+322        Poll::Ready(self.get_mut().finish().map_err(Into::into))
+323    }
+324}
+325
+326impl tokio::io::AsyncWrite for SendStream {
+327    fn poll_write(
+328        self: Pin<&mut Self>,
+329        cx: &mut Context<'_>,
+330        buf: &[u8],
+331    ) -> Poll<io::Result<usize>> {
+332        self.poll_write(cx, buf).map_err(Into::into)
+333    }
+334
+335    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+336        Poll::Ready(Ok(()))
+337    }
+338
+339    fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+340        Poll::Ready(self.get_mut().finish().map_err(Into::into))
+341    }
+342}
+343
+344impl Drop for SendStream {
+345    fn drop(&mut self) {
+346        let mut conn = self.conn.state.lock("SendStream::drop");
+347
+348        // clean up any previously registered wakers
+349        conn.blocked_writers.remove(&self.stream);
+350
+351        if conn.error.is_some() || (self.is_0rtt && conn.check_0rtt().is_err()) {
+352            return;
+353        }
+354        match conn.inner.send_stream(self.stream).finish() {
+355            Ok(()) => conn.wake(),
+356            Err(FinishError::Stopped(reason)) => {
+357                if conn.inner.send_stream(self.stream).reset(reason).is_ok() {
+358                    conn.wake();
+359                }
+360            }
+361            // Already finished or reset, which is fine.
+362            Err(FinishError::ClosedStream) => {}
+363        }
+364    }
+365}
+366
+367/// Errors that arise from writing to a stream
+368#[derive(Debug, Error, Clone, PartialEq, Eq)]
+369pub enum WriteError {
+370    /// The peer is no longer accepting data on this stream
+371    ///
+372    /// Carries an application-defined error code.
+373    #[error("sending stopped by peer: error {0}")]
+374    Stopped(VarInt),
+375    /// The connection was lost
+376    #[error("connection lost")]
+377    ConnectionLost(#[from] ConnectionError),
+378    /// The stream has already been finished or reset
+379    #[error("closed stream")]
+380    ClosedStream,
+381    /// This was a 0-RTT stream and the server rejected it
+382    ///
+383    /// Can only occur on clients for 0-RTT streams, which can be opened using
+384    /// [`Connecting::into_0rtt()`].
+385    ///
+386    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+387    #[error("0-RTT rejected")]
+388    ZeroRttRejected,
+389}
+390
+391impl From<ClosedStream> for WriteError {
+392    #[inline]
+393    fn from(_: ClosedStream) -> Self {
+394        Self::ClosedStream
+395    }
+396}
+397
+398impl From<StoppedError> for WriteError {
+399    fn from(x: StoppedError) -> Self {
+400        match x {
+401            StoppedError::ConnectionLost(e) => Self::ConnectionLost(e),
+402            StoppedError::ZeroRttRejected => Self::ZeroRttRejected,
+403        }
+404    }
+405}
+406
+407impl From<WriteError> for io::Error {
+408    fn from(x: WriteError) -> Self {
+409        use WriteError::*;
+410        let kind = match x {
+411            Stopped(_) | ZeroRttRejected => io::ErrorKind::ConnectionReset,
+412            ConnectionLost(_) | ClosedStream => io::ErrorKind::NotConnected,
+413        };
+414        Self::new(kind, x)
+415    }
+416}
+417
+418/// Errors that arise while monitoring for a send stream stop from the peer
+419#[derive(Debug, Error, Clone, PartialEq, Eq)]
+420pub enum StoppedError {
+421    /// The connection was lost
+422    #[error("connection lost")]
+423    ConnectionLost(#[from] ConnectionError),
+424    /// This was a 0-RTT stream and the server rejected it
+425    ///
+426    /// Can only occur on clients for 0-RTT streams, which can be opened using
+427    /// [`Connecting::into_0rtt()`].
+428    ///
+429    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+430    #[error("0-RTT rejected")]
+431    ZeroRttRejected,
+432}
+433
+434impl From<StoppedError> for io::Error {
+435    fn from(x: StoppedError) -> Self {
+436        use StoppedError::*;
+437        let kind = match x {
+438            ZeroRttRejected => io::ErrorKind::ConnectionReset,
+439            ConnectionLost(_) => io::ErrorKind::NotConnected,
+440        };
+441        Self::new(kind, x)
+442    }
+443}
+444
+445pin_project! {
+446    /// Future returned from [`SendStream::stopped`].
+447    #[derive(Debug)]
+448    pub struct Stopped {
+449        conn: ConnectionRef,
+450        stream: StreamId,
+451        is_0rtt: bool,
+452        #[pin]
+453        notified: OwnedNotified,
+454    }
+455}
+456
+457impl Future for Stopped {
+458    type Output = Result<Option<VarInt>, StoppedError>;
+459
+460    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+461        let mut this = self.project();
+462        loop {
+463            let mut conn = this.conn.state.lock("SendStream::stopped");
+464            // Check if the stream is stopped before polling the notify. This makes sure that
+465            // no wakeups are missed.
+466            if let Some(output) = send_stream_stopped(&mut conn, *this.stream, *this.is_0rtt) {
+467                return Poll::Ready(output);
+468            }
+469            std::task::ready!(this.notified.as_mut().poll(cx));
+470        }
+471    }
+472}
+473
+474#[cfg(test)]
+475mod tests {
+476    fn check_is_send_sync<A: Send + Sync>() {}
+477
+478    #[allow(dead_code)]
+479    fn test_bounds() {
+480        check_is_send_sync::<super::Stopped>();
+481    }
+482}
\ No newline at end of file diff --git a/pr/509/docs/src/noq/work_limiter.rs.html b/pr/509/docs/src/noq/work_limiter.rs.html new file mode 100644 index 000000000..a94d3824d --- /dev/null +++ b/pr/509/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/509/docs/src/noq_perf/perf.rs.html b/pr/509/docs/src/noq_perf/perf.rs.html new file mode 100644 index 000000000..4f377e286 --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/address_discovery.rs.html b/pr/509/docs/src/noq_proto/address_discovery.rs.html new file mode 100644 index 000000000..ecc530e19 --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/bloom_token_log.rs.html b/pr/509/docs/src/noq_proto/bloom_token_log.rs.html new file mode 100644 index 000000000..537e210e2 --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/cid_generator.rs.html b/pr/509/docs/src/noq_proto/cid_generator.rs.html new file mode 100644 index 000000000..619d45199 --- /dev/null +++ b/pr/509/docs/src/noq_proto/cid_generator.rs.html @@ -0,0 +1,180 @@ +cid_generator.rs - source

noq_proto/
cid_generator.rs

1use std::hash::Hasher;
+2
+3use rand::{Rng, RngCore};
+4
+5use crate::Duration;
+6use crate::MAX_CID_SIZE;
+7use crate::shared::ConnectionId;
+8
+9/// Generates connection IDs for incoming connections
+10pub trait ConnectionIdGenerator: Send + Sync {
+11    /// Generates a new CID
+12    ///
+13    /// Connection IDs MUST NOT contain any information that can be used by
+14    /// an external observer (that is, one that does not cooperate with the
+15    /// issuer) to correlate them with other connection IDs for the same
+16    /// connection. They MUST have high entropy, e.g. due to encrypted data
+17    /// or cryptographic-grade random data.
+18    fn generate_cid(&mut self) -> ConnectionId;
+19
+20    /// Quickly determine whether `cid` could have been generated by this generator
+21    ///
+22    /// False positives are permitted, but increase the cost of handling invalid packets.
+23    fn validate(&self, _cid: ConnectionId) -> Result<(), InvalidCid> {
+24        Ok(())
+25    }
+26
+27    /// Returns the length of a CID for connections created by this generator
+28    fn cid_len(&self) -> usize;
+29    /// Returns the lifetime of generated Connection IDs
+30    ///
+31    /// Connection IDs will be retired after the returned `Duration`, if any. Assumed to be constant.
+32    fn cid_lifetime(&self) -> Option<Duration>;
+33}
+34
+35/// The connection ID was not recognized by the [`ConnectionIdGenerator`]
+36#[derive(Debug, Copy, Clone)]
+37pub struct InvalidCid;
+38
+39/// Generates purely random connection IDs of a specified length
+40///
+41/// Random CIDs can be smaller than those produced by [`HashedConnectionIdGenerator`], but cannot be
+42/// usefully [`validate`](ConnectionIdGenerator::validate)d.
+43#[derive(Debug, Clone, Copy)]
+44pub struct RandomConnectionIdGenerator {
+45    cid_len: usize,
+46    lifetime: Option<Duration>,
+47}
+48
+49impl Default for RandomConnectionIdGenerator {
+50    fn default() -> Self {
+51        Self {
+52            cid_len: 8,
+53            lifetime: None,
+54        }
+55    }
+56}
+57
+58impl RandomConnectionIdGenerator {
+59    /// Initialize Random CID generator with a fixed CID length
+60    ///
+61    /// The given length must be less than or equal to MAX_CID_SIZE.
+62    pub fn new(cid_len: usize) -> Self {
+63        debug_assert!(cid_len <= MAX_CID_SIZE);
+64        Self {
+65            cid_len,
+66            ..Self::default()
+67        }
+68    }
+69
+70    /// Set the lifetime of CIDs created by this generator
+71    pub fn set_lifetime(&mut self, d: Duration) -> &mut Self {
+72        self.lifetime = Some(d);
+73        self
+74    }
+75}
+76
+77impl ConnectionIdGenerator for RandomConnectionIdGenerator {
+78    fn generate_cid(&mut self) -> ConnectionId {
+79        let mut bytes_arr = [0; MAX_CID_SIZE];
+80        rand::rng().fill_bytes(&mut bytes_arr[..self.cid_len]);
+81
+82        ConnectionId::new(&bytes_arr[..self.cid_len])
+83    }
+84
+85    /// Provide the length of dst_cid in short header packet
+86    fn cid_len(&self) -> usize {
+87        self.cid_len
+88    }
+89
+90    fn cid_lifetime(&self) -> Option<Duration> {
+91        self.lifetime
+92    }
+93}
+94
+95/// Generates 8-byte connection IDs that can be efficiently
+96/// [`validate`](ConnectionIdGenerator::validate)d
+97///
+98/// This generator uses a non-cryptographic hash and can therefore still be spoofed, but nonetheless
+99/// helps prevents noq from responding to non-QUIC packets at very low cost.
+100pub struct HashedConnectionIdGenerator {
+101    key: u64,
+102    lifetime: Option<Duration>,
+103}
+104
+105impl HashedConnectionIdGenerator {
+106    /// Create a generator with a random key
+107    pub fn new() -> Self {
+108        Self::from_key(rand::rng().random())
+109    }
+110
+111    /// Create a generator with a specific key
+112    ///
+113    /// Allows [`validate`](ConnectionIdGenerator::validate) to recognize a consistent set of
+114    /// connection IDs across restarts
+115    pub fn from_key(key: u64) -> Self {
+116        Self {
+117            key,
+118            lifetime: None,
+119        }
+120    }
+121
+122    /// Set the lifetime of CIDs created by this generator
+123    pub fn set_lifetime(&mut self, d: Duration) -> &mut Self {
+124        self.lifetime = Some(d);
+125        self
+126    }
+127}
+128
+129impl Default for HashedConnectionIdGenerator {
+130    fn default() -> Self {
+131        Self::new()
+132    }
+133}
+134
+135impl ConnectionIdGenerator for HashedConnectionIdGenerator {
+136    fn generate_cid(&mut self) -> ConnectionId {
+137        let mut bytes_arr = [0; NONCE_LEN + SIGNATURE_LEN];
+138        rand::rng().fill_bytes(&mut bytes_arr[..NONCE_LEN]);
+139        let mut hasher = rustc_hash::FxHasher::default();
+140        hasher.write_u64(self.key);
+141        hasher.write(&bytes_arr[..NONCE_LEN]);
+142        bytes_arr[NONCE_LEN..].copy_from_slice(&hasher.finish().to_le_bytes()[..SIGNATURE_LEN]);
+143        ConnectionId::new(&bytes_arr)
+144    }
+145
+146    fn validate(&self, cid: ConnectionId) -> Result<(), InvalidCid> {
+147        let (nonce, signature) = cid.split_at(NONCE_LEN);
+148        let mut hasher = rustc_hash::FxHasher::default();
+149        hasher.write_u64(self.key);
+150        hasher.write(nonce);
+151        let expected = hasher.finish().to_le_bytes();
+152        match expected[..SIGNATURE_LEN] == signature[..] {
+153            true => Ok(()),
+154            false => Err(InvalidCid),
+155        }
+156    }
+157
+158    fn cid_len(&self) -> usize {
+159        NONCE_LEN + SIGNATURE_LEN
+160    }
+161
+162    fn cid_lifetime(&self) -> Option<Duration> {
+163        self.lifetime
+164    }
+165}
+166
+167const NONCE_LEN: usize = 3; // Good for more than 16 million connections
+168const SIGNATURE_LEN: usize = 8 - NONCE_LEN; // 8-byte total CID length
+169
+170#[cfg(test)]
+171mod tests {
+172    use super::*;
+173
+174    #[test]
+175    fn validate_keyed_cid() {
+176        let mut generator = HashedConnectionIdGenerator::new();
+177        let cid = generator.generate_cid();
+178        generator.validate(cid).unwrap();
+179    }
+180}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/cid_queue.rs.html b/pr/509/docs/src/noq_proto/cid_queue.rs.html new file mode 100644 index 000000000..45ad9d177 --- /dev/null +++ b/pr/509/docs/src/noq_proto/cid_queue.rs.html @@ -0,0 +1,517 @@ +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    pub(crate) fn next_reserved(&mut self) -> Option<ConnectionId> {
+132        let (i, cid_data) = self.iter_from_reserved().nth(1)?;
+133
+134        self.cursor_reserved = (self.cursor_reserved + i) % Self::LEN;
+135        Some(cid_data.0)
+136    }
+137
+138    /// Returns the number of unused CIDs (neither active nor reserved).
+139    pub(crate) fn remaining(&self) -> usize {
+140        self.iter_from_reserved()
+141            .count()
+142            .checked_sub(1)
+143            .expect("iterator is non empty")
+144    }
+145
+146    /// Iterate CIDs in CidQueue that are not `None`, including the active CID
+147    fn iter_from_active(&self) -> impl Iterator<Item = (usize, CidData)> + '_ {
+148        (0..Self::LEN).filter_map(move |step| {
+149            let index = (self.cursor + step) % Self::LEN;
+150            self.buffer[index].map(|cid_data| (step, cid_data))
+151        })
+152    }
+153
+154    /// Iterate CIDs in CidQueue that are not `None`, from [`Self::cursor_reserved`].
+155    ///
+156    /// The iterator will always have at least one item, as it will include the active CID when no
+157    /// CID has been reserved, or the last reserved CID otherwise.
+158    ///
+159    /// Along with the CID, it returns the offset counted from [`Self::cursor_reserved`] where the
+160    /// CID is stored.
+161    fn iter_from_reserved(&self) -> impl Iterator<Item = (usize, CidData)> + '_ {
+162        (0..(Self::LEN - self.reserved_len())).filter_map(move |step| {
+163            let index = (self.cursor_reserved + step) % Self::LEN;
+164            self.buffer[index].map(|cid_data| (step, cid_data))
+165        })
+166    }
+167
+168    /// The length of the internal buffer's section of CIDs that are marked as reserved.
+169    fn reserved_len(&self) -> usize {
+170        if self.cursor_reserved >= self.cursor {
+171            self.cursor_reserved - self.cursor
+172        } else {
+173            self.cursor_reserved + Self::LEN - self.cursor
+174        }
+175    }
+176
+177    /// Replace the initial CID
+178    pub(crate) fn update_initial_cid(&mut self, cid: ConnectionId) {
+179        debug_assert_eq!(self.offset, 0);
+180        self.buffer[self.cursor] = Some(CidData(cid, None));
+181    }
+182
+183    /// Return active remote CID itself
+184    pub(crate) fn active(&self) -> ConnectionId {
+185        self.buffer[self.cursor].unwrap().0
+186    }
+187
+188    /// Return the sequence number of active remote CID
+189    pub(crate) fn active_seq(&self) -> u64 {
+190        self.offset
+191    }
+192
+193    pub(crate) const LEN: usize = 5;
+194}
+195
+196#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+197pub(crate) enum InsertError {
+198    /// CID was already retired
+199    Retired,
+200    /// Sequence number violates the leading edge of the window
+201    ExceedsLimit,
+202}
+203
+204#[cfg(test)]
+205mod tests {
+206    use super::*;
+207
+208    fn cid(sequence: u64, retire_prior_to: u64) -> NewConnectionId {
+209        NewConnectionId {
+210            path_id: None,
+211            sequence,
+212            id: ConnectionId::new(&sequence.to_be_bytes()),
+213            reset_token: ResetToken::from([0xCD; crate::RESET_TOKEN_SIZE]),
+214            retire_prior_to,
+215        }
+216    }
+217
+218    fn initial_cid() -> ConnectionId {
+219        ConnectionId::new(&[0xFF; 8])
+220    }
+221
+222    #[test]
+223    fn next_dense() {
+224        let mut q = CidQueue::new(initial_cid());
+225        assert!(q.next().is_none());
+226        assert!(q.next().is_none());
+227
+228        for i in 1..CidQueue::LEN as u64 {
+229            q.insert(cid(i, 0)).unwrap();
+230        }
+231        for i in 1..CidQueue::LEN as u64 {
+232            let (_, retire) = q.next().unwrap();
+233            assert_eq!(q.active_seq(), i);
+234            assert_eq!(retire.end - retire.start, 1);
+235        }
+236        assert!(q.next().is_none());
+237    }
+238
+239    #[test]
+240    fn next_sparse() {
+241        let mut q = CidQueue::new(initial_cid());
+242        let seqs = (1..CidQueue::LEN as u64).filter(|x| x % 2 == 0);
+243        for i in seqs.clone() {
+244            q.insert(cid(i, 0)).unwrap();
+245        }
+246        for i in seqs {
+247            let (_, retire) = q.next().unwrap();
+248            dbg!(&retire);
+249            assert_eq!(q.active_seq(), i);
+250            assert_eq!(retire, (q.active_seq().saturating_sub(2))..q.active_seq());
+251        }
+252        assert!(q.next().is_none());
+253    }
+254
+255    #[test]
+256    fn wrap() {
+257        let mut q = CidQueue::new(initial_cid());
+258
+259        for i in 1..CidQueue::LEN as u64 {
+260            q.insert(cid(i, 0)).unwrap();
+261        }
+262        for _ in 1..(CidQueue::LEN as u64 - 1) {
+263            q.next().unwrap();
+264        }
+265        for i in CidQueue::LEN as u64..(CidQueue::LEN as u64 + 3) {
+266            q.insert(cid(i, 0)).unwrap();
+267        }
+268        for i in (CidQueue::LEN as u64 - 1)..(CidQueue::LEN as u64 + 3) {
+269            q.next().unwrap();
+270            assert_eq!(q.active_seq(), i);
+271        }
+272        assert!(q.next().is_none());
+273    }
+274
+275    #[test]
+276    fn retire_dense() {
+277        let mut q = CidQueue::new(initial_cid());
+278
+279        for i in 1..CidQueue::LEN as u64 {
+280            q.insert(cid(i, 0)).unwrap();
+281        }
+282        assert_eq!(q.active_seq(), 0);
+283
+284        assert_eq!(q.insert(cid(4, 2)).unwrap().unwrap().0, 0..2);
+285        assert_eq!(q.active_seq(), 2);
+286        assert_eq!(q.insert(cid(4, 2)), Ok(None));
+287
+288        for i in 2..(CidQueue::LEN as u64 - 1) {
+289            let _ = q.next().unwrap();
+290            assert_eq!(q.active_seq(), i + 1);
+291            assert_eq!(q.insert(cid(i + 1, i + 1)), Ok(None));
+292        }
+293
+294        assert!(q.next().is_none());
+295    }
+296
+297    #[test]
+298    fn retire_sparse() {
+299        // Retiring CID 0 when CID 1 is not known should retire CID 1 as we move to CID 2
+300        let mut q = CidQueue::new(initial_cid());
+301        q.insert(cid(2, 0)).unwrap();
+302        assert_eq!(q.insert(cid(3, 1)).unwrap().unwrap().0, 0..2,);
+303        assert_eq!(q.active_seq(), 2);
+304    }
+305
+306    #[test]
+307    fn retire_many() {
+308        let mut q = CidQueue::new(initial_cid());
+309        q.insert(cid(2, 0)).unwrap();
+310        assert_eq!(
+311            q.insert(cid(1_000_000, 1_000_000)).unwrap().unwrap().0,
+312            0..CidQueue::LEN as u64,
+313        );
+314        assert_eq!(q.active_seq(), 1_000_000);
+315    }
+316
+317    #[test]
+318    fn insert_limit() {
+319        let mut q = CidQueue::new(initial_cid());
+320        assert_eq!(q.insert(cid(CidQueue::LEN as u64 - 1, 0)), Ok(None));
+321        assert_eq!(
+322            q.insert(cid(CidQueue::LEN as u64, 0)),
+323            Err(InsertError::ExceedsLimit)
+324        );
+325    }
+326
+327    #[test]
+328    fn insert_duplicate() {
+329        let mut q = CidQueue::new(initial_cid());
+330        q.insert(cid(0, 0)).unwrap();
+331        q.insert(cid(0, 0)).unwrap();
+332    }
+333
+334    #[test]
+335    fn insert_retired() {
+336        let mut q = CidQueue::new(initial_cid());
+337        assert_eq!(
+338            q.insert(cid(0, 0)),
+339            Ok(None),
+340            "reinserting active CID succeeds"
+341        );
+342        assert!(q.next().is_none(), "active CID isn't requeued");
+343        q.insert(cid(1, 0)).unwrap();
+344        q.next().unwrap();
+345        assert_eq!(
+346            q.insert(cid(0, 0)),
+347            Err(InsertError::Retired),
+348            "previous active CID is already retired"
+349        );
+350    }
+351
+352    #[test]
+353    fn retire_then_insert_next() {
+354        let mut q = CidQueue::new(initial_cid());
+355        for i in 1..CidQueue::LEN as u64 {
+356            q.insert(cid(i, 0)).unwrap();
+357        }
+358        q.next().unwrap();
+359        q.insert(cid(CidQueue::LEN as u64, 0)).unwrap();
+360        assert_eq!(
+361            q.insert(cid(CidQueue::LEN as u64 + 1, 0)),
+362            Err(InsertError::ExceedsLimit)
+363        );
+364    }
+365
+366    #[test]
+367    fn always_valid() {
+368        let mut q = CidQueue::new(initial_cid());
+369        assert!(q.next().is_none());
+370        assert_eq!(q.active(), initial_cid());
+371        assert_eq!(q.active_seq(), 0);
+372    }
+373
+374    #[test]
+375    fn reserved_smoke() {
+376        let mut q = CidQueue::new(initial_cid());
+377        assert_eq!(q.next_reserved(), None);
+378
+379        let one = cid(1, 0);
+380        q.insert(one).unwrap();
+381        assert_eq!(q.next_reserved(), Some(one.id));
+382
+383        let two = cid(2, 2);
+384        let (retired_range, reset_token) = q.insert(two).unwrap().unwrap();
+385        assert_eq!(reset_token, two.reset_token);
+386        assert_eq!(retired_range, 0..2);
+387
+388        assert_eq!(q.next_reserved(), None);
+389
+390        let four = cid(4, 2);
+391        q.insert(four).unwrap();
+392        println!("{q:?}");
+393        assert_eq!(q.next_reserved(), Some(four.id));
+394        assert_eq!(q.active(), two.id);
+395
+396        assert_eq!(q.next(), None);
+397    }
+398
+399    #[test]
+400    fn reserve_multiple() {
+401        let mut q = CidQueue::new(initial_cid());
+402        let one = cid(1, 0);
+403        let two = cid(2, 0);
+404        q.insert(one).unwrap();
+405        q.insert(two).unwrap();
+406        assert_eq!(q.next_reserved(), Some(one.id));
+407        assert_eq!(q.next_reserved(), Some(two.id));
+408        assert_eq!(q.next_reserved(), None);
+409    }
+410
+411    #[test]
+412    fn reserve_multiple_sparse() {
+413        let mut q = CidQueue::new(initial_cid());
+414        let two = cid(2, 0);
+415        let four = cid(4, 0);
+416        q.insert(two).unwrap();
+417        q.insert(four).unwrap();
+418        assert_eq!(q.next_reserved(), Some(two.id));
+419        assert_eq!(q.next_reserved(), Some(four.id));
+420        assert_eq!(q.next_reserved(), None);
+421    }
+422
+423    #[test]
+424    fn reserve_many_next_clears() {
+425        let mut q = CidQueue::new(initial_cid());
+426        for i in 1..CidQueue::LEN {
+427            q.insert(cid(i as u64, 0)).unwrap();
+428        }
+429
+430        for _ in 0..CidQueue::LEN - 2 {
+431            assert!(q.next_reserved().is_some());
+432        }
+433
+434        assert!(q.next().is_some());
+435        assert_eq!(q.next(), None);
+436    }
+437
+438    #[test]
+439    fn reserve_many_next_reserved_none() {
+440        let mut q = CidQueue::new(initial_cid());
+441        for i in 1..CidQueue::LEN {
+442            q.insert(cid(i as u64, 0)).unwrap();
+443        }
+444
+445        for _ in 0..CidQueue::LEN - 1 {
+446            assert!(q.next_reserved().is_some());
+447        }
+448
+449        assert_eq!(q.next_reserved(), None);
+450    }
+451
+452    #[test]
+453    fn one_active_all_else_reserved_next_none() {
+454        let mut q = CidQueue::new(initial_cid());
+455        for i in 1..CidQueue::LEN {
+456            q.insert(cid(i as u64, 0)).unwrap();
+457        }
+458
+459        for _ in 0..CidQueue::LEN - 1 {
+460            assert!(q.next_reserved().is_some());
+461        }
+462
+463        assert_eq!(q.next(), None);
+464    }
+465
+466    #[test]
+467    fn insert_reserve_advance() {
+468        let mut q = CidQueue::new(initial_cid());
+469
+470        let first = cid(1, 0);
+471        let second = cid(2, 0);
+472        let third = cid(3, 0);
+473
+474        q.insert(first).unwrap();
+475        q.insert(second).unwrap();
+476
+477        assert_eq!(q.next_reserved(), Some(first.id));
+478        q.insert(third).unwrap();
+479        q.next();
+480        assert_eq!(q.active(), second.id);
+481    }
+482
+483    #[test]
+484    fn sparse_insert_reserve_insert_advance() {
+485        let mut q = CidQueue::new(initial_cid());
+486
+487        let one = cid(1, 0);
+488        let two = cid(2, 0);
+489        let three = cid(3, 0);
+490
+491        q.insert(two).unwrap();
+492        q.insert(three).unwrap();
+493        assert_eq!(q.next_reserved(), Some(two.id));
+494        q.insert(one).unwrap();
+495        q.next();
+496        assert_eq!(q.active(), three.id);
+497        assert_eq!(q.next_reserved(), None);
+498    }
+499
+500    #[test]
+501    fn reserve_many_next_clears_across_wraparound() {
+502        let mut q = CidQueue::new(initial_cid());
+503        for i in 1..CidQueue::LEN {
+504            q.insert(cid(i as u64, 0)).unwrap();
+505        }
+506
+507        for _ in 0..CidQueue::LEN - 2 {
+508            assert!(q.next_reserved().is_some());
+509        }
+510
+511        assert!(q.next().is_some());
+512        q.insert(cid(CidQueue::LEN as u64, 0)).unwrap();
+513        assert!(q.next_reserved().is_some());
+514        q.insert(cid(CidQueue::LEN as u64 + 1, 0)).unwrap();
+515        assert!(q.next().is_some());
+516    }
+517}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/coding.rs.html b/pr/509/docs/src/noq_proto/coding.rs.html new file mode 100644 index 000000000..28149b1d5 --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/config/mod.rs.html b/pr/509/docs/src/noq_proto/config/mod.rs.html new file mode 100644 index 000000000..79322a614 --- /dev/null +++ b/pr/509/docs/src/noq_proto/config/mod.rs.html @@ -0,0 +1,688 @@ +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#[cfg(doc)]
+37pub use transport::DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED;
+38
+39/// Global configuration for the endpoint, affecting all connections
+40///
+41/// Default values should be suitable for most internet applications.
+42#[derive(Clone)]
+43pub struct EndpointConfig {
+44    pub(crate) reset_key: Arc<dyn HmacKey>,
+45    pub(crate) max_udp_payload_size: VarInt,
+46    /// CID generator factory
+47    ///
+48    /// Create a cid generator for local cid in Endpoint struct
+49    pub(crate) connection_id_generator_factory:
+50        Arc<dyn Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync>,
+51    pub(crate) supported_versions: Vec<u32>,
+52    pub(crate) grease_quic_bit: bool,
+53    /// Minimum interval between outgoing stateless reset packets
+54    pub(crate) min_reset_interval: Duration,
+55    /// Optional seed to be used internally for random number generation
+56    pub(crate) rng_seed: Option<[u8; 32]>,
+57}
+58
+59impl EndpointConfig {
+60    /// Create a default config with a particular `reset_key`
+61    pub fn new(reset_key: Arc<dyn HmacKey>) -> Self {
+62        let cid_factory =
+63            || -> Box<dyn ConnectionIdGenerator> { Box::<HashedConnectionIdGenerator>::default() };
+64        Self {
+65            reset_key,
+66            max_udp_payload_size: (1500u32 - 28).into(), // Ethernet MTU minus IP + UDP headers
+67            connection_id_generator_factory: Arc::new(cid_factory),
+68            supported_versions: DEFAULT_SUPPORTED_VERSIONS.to_vec(),
+69            grease_quic_bit: true,
+70            min_reset_interval: Duration::from_millis(20),
+71            rng_seed: None,
+72        }
+73    }
+74
+75    /// Supply a custom connection ID generator factory
+76    ///
+77    /// Called once by each `Endpoint` constructed from this configuration to obtain the CID
+78    /// generator which will be used to generate the CIDs used for incoming packets on all
+79    /// connections involving that  `Endpoint`. A custom CID generator allows applications to embed
+80    /// information in local connection IDs, e.g. to support stateless packet-level load balancers.
+81    ///
+82    /// Defaults to [`HashedConnectionIdGenerator`].
+83    pub fn cid_generator<F: Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync + 'static>(
+84        &mut self,
+85        factory: F,
+86    ) -> &mut Self {
+87        self.connection_id_generator_factory = Arc::new(factory);
+88        self
+89    }
+90
+91    /// Private key used to send authenticated connection resets to peers who were
+92    /// communicating with a previous instance of this endpoint.
+93    pub fn reset_key(&mut self, key: Arc<dyn HmacKey>) -> &mut Self {
+94        self.reset_key = key;
+95        self
+96    }
+97
+98    /// Maximum UDP payload size accepted from peers (excluding UDP and IP overhead).
+99    ///
+100    /// Must be greater or equal than 1200.
+101    ///
+102    /// Defaults to 1472, which is the largest UDP payload that can be transmitted in the typical
+103    /// 1500 byte Ethernet MTU. Deployments on links with larger MTUs (e.g. loopback or Ethernet
+104    /// with jumbo frames) can raise this to improve performance at the cost of a linear increase in
+105    /// datagram receive buffer size.
+106    pub fn max_udp_payload_size(&mut self, value: u16) -> Result<&mut Self, ConfigError> {
+107        if !(1200..=65_527).contains(&value) {
+108            return Err(ConfigError::OutOfBounds);
+109        }
+110
+111        self.max_udp_payload_size = value.into();
+112        Ok(self)
+113    }
+114
+115    /// Get the current value of [`max_udp_payload_size`](Self::max_udp_payload_size)
+116    //
+117    // While most parameters don't need to be readable, this must be exposed to allow higher-level
+118    // layers, e.g. the `noq` crate, to determine how large a receive buffer to allocate to
+119    // support an externally-defined `EndpointConfig`.
+120    //
+121    // While `get_` accessors are typically unidiomatic in Rust, we favor concision for setters,
+122    // which will be used far more heavily.
+123    pub fn get_max_udp_payload_size(&self) -> u64 {
+124        self.max_udp_payload_size.into()
+125    }
+126
+127    /// Override supported QUIC versions
+128    pub fn supported_versions(&mut self, supported_versions: Vec<u32>) -> &mut Self {
+129        self.supported_versions = supported_versions;
+130        self
+131    }
+132
+133    /// Whether to accept QUIC packets containing any value for the fixed bit
+134    ///
+135    /// Enabled by default. Helps protect against protocol ossification and makes traffic less
+136    /// identifiable to observers. Disable if helping observers identify this traffic as QUIC is
+137    /// desired.
+138    pub fn grease_quic_bit(&mut self, value: bool) -> &mut Self {
+139        self.grease_quic_bit = value;
+140        self
+141    }
+142
+143    /// Minimum interval between outgoing stateless reset packets
+144    ///
+145    /// Defaults to 20ms. Limits the impact of attacks which flood an endpoint with garbage packets,
+146    /// e.g. [ISAKMP/IKE amplification]. Larger values provide a stronger defense, but may delay
+147    /// detection of some error conditions by clients. Using a [`ConnectionIdGenerator`] with a low
+148    /// rate of false positives in [`validate`](ConnectionIdGenerator::validate) reduces the risk
+149    /// incurred by a small minimum reset interval.
+150    ///
+151    /// [ISAKMP/IKE
+152    /// amplification]: https://bughunters.google.com/blog/5960150648750080/preventing-cross-service-udp-loops-in-quic#isakmp-ike-amplification-vs-quic
+153    pub fn min_reset_interval(&mut self, value: Duration) -> &mut Self {
+154        self.min_reset_interval = value;
+155        self
+156    }
+157
+158    /// Optional seed to be used internally for random number generation
+159    ///
+160    /// By default, noq will initialize an endpoint's rng using a platform entropy source.
+161    /// However, you can seed the rng yourself through this method (e.g. if you need to run noq
+162    /// deterministically or if you are using noq in an environment that doesn't have a source of
+163    /// entropy available).
+164    pub fn rng_seed(&mut self, seed: Option<[u8; 32]>) -> &mut Self {
+165        self.rng_seed = seed;
+166        self
+167    }
+168}
+169
+170impl fmt::Debug for EndpointConfig {
+171    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+172        fmt.debug_struct("EndpointConfig")
+173            // reset_key not debug
+174            .field("max_udp_payload_size", &self.max_udp_payload_size)
+175            // cid_generator_factory not debug
+176            .field("supported_versions", &self.supported_versions)
+177            .field("grease_quic_bit", &self.grease_quic_bit)
+178            .field("rng_seed", &self.rng_seed)
+179            .finish_non_exhaustive()
+180    }
+181}
+182
+183#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
+184impl Default for EndpointConfig {
+185    fn default() -> Self {
+186        #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+187        use aws_lc_rs::hmac;
+188        use rand::RngCore;
+189        #[cfg(feature = "ring")]
+190        use ring::hmac;
+191
+192        let mut reset_key = [0; 64];
+193        rand::rng().fill_bytes(&mut reset_key);
+194
+195        Self::new(Arc::new(hmac::Key::new(hmac::HMAC_SHA256, &reset_key)))
+196    }
+197}
+198
+199/// Parameters governing incoming connections
+200///
+201/// Default values should be suitable for most internet applications.
+202#[derive(Clone)]
+203pub struct ServerConfig {
+204    /// Transport configuration to use for incoming connections
+205    pub transport: Arc<TransportConfig>,
+206
+207    /// TLS configuration used for incoming connections
+208    ///
+209    /// Must be set to use TLS 1.3 only.
+210    pub crypto: Arc<dyn crypto::ServerConfig>,
+211
+212    /// Configuration for sending and handling validation tokens
+213    pub validation_token: ValidationTokenConfig,
+214
+215    /// Used to generate one-time AEAD keys to protect handshake tokens
+216    pub(crate) token_key: Arc<dyn HandshakeTokenKey>,
+217
+218    /// Duration after a retry token was issued for which it's considered valid
+219    pub(crate) retry_token_lifetime: Duration,
+220
+221    /// Whether to allow clients to migrate to new addresses
+222    ///
+223    /// Improves behavior for clients that move between different internet connections or suffer NAT
+224    /// rebinding. Enabled by default.
+225    pub(crate) migration: bool,
+226
+227    pub(crate) preferred_address_v4: Option<SocketAddrV4>,
+228    pub(crate) preferred_address_v6: Option<SocketAddrV6>,
+229
+230    pub(crate) max_incoming: usize,
+231    pub(crate) incoming_buffer_size: u64,
+232    pub(crate) incoming_buffer_size_total: u64,
+233
+234    pub(crate) time_source: Arc<dyn TimeSource>,
+235}
+236
+237impl ServerConfig {
+238    /// Create a default config with a particular handshake token key
+239    pub fn new(
+240        crypto: Arc<dyn crypto::ServerConfig>,
+241        token_key: Arc<dyn HandshakeTokenKey>,
+242    ) -> Self {
+243        Self {
+244            transport: Arc::new(TransportConfig::default()),
+245            crypto,
+246
+247            token_key,
+248            retry_token_lifetime: Duration::from_secs(15),
+249
+250            migration: true,
+251
+252            validation_token: ValidationTokenConfig::default(),
+253
+254            preferred_address_v4: None,
+255            preferred_address_v6: None,
+256
+257            max_incoming: 1 << 16,
+258            incoming_buffer_size: 10 << 20,
+259            incoming_buffer_size_total: 100 << 20,
+260
+261            time_source: Arc::new(StdSystemTime),
+262        }
+263    }
+264
+265    /// Set a custom [`TransportConfig`]
+266    pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self {
+267        self.transport = transport;
+268        self
+269    }
+270
+271    /// Set a custom [`ValidationTokenConfig`]
+272    pub fn validation_token_config(
+273        &mut self,
+274        validation_token: ValidationTokenConfig,
+275    ) -> &mut Self {
+276        self.validation_token = validation_token;
+277        self
+278    }
+279
+280    /// Private key used to authenticate data included in handshake tokens
+281    pub fn token_key(&mut self, value: Arc<dyn HandshakeTokenKey>) -> &mut Self {
+282        self.token_key = value;
+283        self
+284    }
+285
+286    /// Duration after a retry token was issued for which it's considered valid
+287    ///
+288    /// Defaults to 15 seconds.
+289    pub fn retry_token_lifetime(&mut self, value: Duration) -> &mut Self {
+290        self.retry_token_lifetime = value;
+291        self
+292    }
+293
+294    /// Whether to allow clients to migrate to new addresses
+295    ///
+296    /// Improves behavior for clients that move between different internet connections or suffer NAT
+297    /// rebinding. Enabled by default.
+298    pub fn migration(&mut self, value: bool) -> &mut Self {
+299        self.migration = value;
+300        self
+301    }
+302
+303    /// The preferred IPv4 address that will be communicated to clients during handshaking
+304    ///
+305    /// If the client is able to reach this address, it will switch to it.
+306    pub fn preferred_address_v4(&mut self, address: Option<SocketAddrV4>) -> &mut Self {
+307        self.preferred_address_v4 = address;
+308        self
+309    }
+310
+311    /// The preferred IPv6 address that will be communicated to clients during handshaking
+312    ///
+313    /// If the client is able to reach this address, it will switch to it.
+314    pub fn preferred_address_v6(&mut self, address: Option<SocketAddrV6>) -> &mut Self {
+315        self.preferred_address_v6 = address;
+316        self
+317    }
+318
+319    /// Maximum number of [`Incoming`][crate::Incoming] to allow to exist at a time
+320    ///
+321    /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
+322    /// is received and stops existing when the application either accepts it or otherwise disposes
+323    /// of it. While this limit is reached, new incoming connection attempts are immediately
+324    /// refused. Larger values have greater worst-case memory consumption, but accommodate greater
+325    /// application latency in handling incoming connection attempts.
+326    ///
+327    /// The default value is set to 65536. With a typical Ethernet MTU of 1500 bytes, this limits
+328    /// memory consumption from this to under 100 MiB--a generous amount that still prevents memory
+329    /// exhaustion in most contexts.
+330    pub fn max_incoming(&mut self, max_incoming: usize) -> &mut Self {
+331        self.max_incoming = max_incoming;
+332        self
+333    }
+334
+335    /// Maximum number of received bytes to buffer for each [`Incoming`][crate::Incoming]
+336    ///
+337    /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
+338    /// is received and stops existing when the application either accepts it or otherwise disposes
+339    /// of it. This limit governs only packets received within that period, and does not include
+340    /// the first packet. Packets received in excess of this limit are dropped, which may cause
+341    /// 0-RTT or handshake data to have to be retransmitted.
+342    ///
+343    /// The default value is set to 10 MiB--an amount such that in most situations a client would
+344    /// not transmit that much 0-RTT data faster than the server handles the corresponding
+345    /// [`Incoming`][crate::Incoming].
+346    pub fn incoming_buffer_size(&mut self, incoming_buffer_size: u64) -> &mut Self {
+347        self.incoming_buffer_size = incoming_buffer_size;
+348        self
+349    }
+350
+351    /// Maximum number of received bytes to buffer for all [`Incoming`][crate::Incoming]
+352    /// collectively
+353    ///
+354    /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
+355    /// is received and stops existing when the application either accepts it or otherwise disposes
+356    /// of it. This limit governs only packets received within that period, and does not include
+357    /// the first packet. Packets received in excess of this limit are dropped, which may cause
+358    /// 0-RTT or handshake data to have to be retransmitted.
+359    ///
+360    /// The default value is set to 100 MiB--a generous amount that still prevents memory
+361    /// exhaustion in most contexts.
+362    pub fn incoming_buffer_size_total(&mut self, incoming_buffer_size_total: u64) -> &mut Self {
+363        self.incoming_buffer_size_total = incoming_buffer_size_total;
+364        self
+365    }
+366
+367    /// Object to get current [`SystemTime`]
+368    ///
+369    /// This exists to allow system time to be mocked in tests, or wherever else desired.
+370    ///
+371    /// Defaults to [`StdSystemTime`], which simply calls [`SystemTime::now()`](SystemTime::now).
+372    pub fn time_source(&mut self, time_source: Arc<dyn TimeSource>) -> &mut Self {
+373        self.time_source = time_source;
+374        self
+375    }
+376
+377    pub(crate) fn has_preferred_address(&self) -> bool {
+378        self.preferred_address_v4.is_some() || self.preferred_address_v6.is_some()
+379    }
+380}
+381
+382#[cfg(all(feature = "rustls", any(feature = "aws-lc-rs", feature = "ring")))]
+383impl ServerConfig {
+384    /// Create a server config with the given certificate chain to be presented to clients
+385    ///
+386    /// Uses a randomized handshake token key.
+387    pub fn with_single_cert(
+388        cert_chain: Vec<CertificateDer<'static>>,
+389        key: PrivateKeyDer<'static>,
+390    ) -> Result<Self, rustls::Error> {
+391        Ok(Self::with_crypto(Arc::new(QuicServerConfig::new(
+392            cert_chain, key,
+393        )?)))
+394    }
+395}
+396
+397#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
+398impl ServerConfig {
+399    /// Create a server config with the given [`crypto::ServerConfig`]
+400    ///
+401    /// Uses a randomized handshake token key.
+402    pub fn with_crypto(crypto: Arc<dyn crypto::ServerConfig>) -> Self {
+403        use crate::crypto::ring_like::RetryTokenKey;
+404
+405        let retry_token_key = RetryTokenKey::new(&mut rand::rng());
+406        Self::new(crypto, Arc::new(retry_token_key))
+407    }
+408}
+409
+410impl fmt::Debug for ServerConfig {
+411    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+412        fmt.debug_struct("ServerConfig")
+413            .field("transport", &self.transport)
+414            // crypto not debug
+415            // token not debug
+416            .field("retry_token_lifetime", &self.retry_token_lifetime)
+417            .field("validation_token", &self.validation_token)
+418            .field("migration", &self.migration)
+419            .field("preferred_address_v4", &self.preferred_address_v4)
+420            .field("preferred_address_v6", &self.preferred_address_v6)
+421            .field("max_incoming", &self.max_incoming)
+422            .field("incoming_buffer_size", &self.incoming_buffer_size)
+423            .field(
+424                "incoming_buffer_size_total",
+425                &self.incoming_buffer_size_total,
+426            )
+427            // system_time_clock not debug
+428            .finish_non_exhaustive()
+429    }
+430}
+431
+432/// Configuration for sending and handling validation tokens in incoming connections
+433///
+434/// Default values should be suitable for most internet applications.
+435///
+436/// ## QUIC Tokens
+437///
+438/// The QUIC protocol defines a concept of "[address validation][1]". Essentially, one side of a
+439/// QUIC connection may appear to be receiving QUIC packets from a particular remote UDP address,
+440/// but it will only consider that remote address "validated" once it has convincing evidence that
+441/// the address is not being [spoofed][2].
+442///
+443/// Validation is important primarily because of QUIC's "anti-amplification limit." This limit
+444/// prevents a QUIC server from sending a client more than three times the number of bytes it has
+445/// received from the client on a given address until that address is validated. This is designed
+446/// to mitigate the ability of attackers to use QUIC-based servers as reflectors in [amplification
+447/// attacks][3].
+448///
+449/// A path may become validated in several ways. The server is always considered validated by the
+450/// client. The client usually begins in an unvalidated state upon first connecting or migrating,
+451/// but then becomes validated through various mechanisms that usually take one network round trip.
+452/// However, in some cases, a client which has previously attempted to connect to a server may have
+453/// been given a one-time use cryptographically secured "token" that it can send in a subsequent
+454/// connection attempt to be validated immediately.
+455///
+456/// There are two ways these tokens can originate:
+457///
+458/// - If the server responds to an incoming connection with `retry`, a "retry token" is minted and
+459///   sent to the client, which the client immediately uses to attempt to connect again. Retry
+460///   tokens operate on short timescales, such as 15 seconds.
+461/// - If a client's path within an active connection is validated, the server may send the client
+462///   one or more "validation tokens," which the client may store for use in later connections to
+463///   the same server. Validation tokens may be valid for much longer lifetimes than retry token.
+464///
+465/// The usage of validation tokens is most impactful in situations where 0-RTT data is also being
+466/// used--in particular, in situations where the server sends the client more than three times more
+467/// 0.5-RTT data than it has received 0-RTT data. Since the successful completion of a connection
+468/// handshake implicitly causes the client's address to be validated, transmission of 0.5-RTT data
+469/// is the main situation where a server might be sending application data to an address that could
+470/// be validated by token usage earlier than it would become validated without token usage.
+471///
+472/// [1]: https://www.rfc-editor.org/rfc/rfc9000.html#section-8
+473/// [2]: https://en.wikipedia.org/wiki/IP_address_spoofing
+474/// [3]: https://en.wikipedia.org/wiki/Denial-of-service_attack#Amplification
+475///
+476/// These tokens should not be confused with "stateless reset tokens," which are similarly named
+477/// but entirely unrelated.
+478#[derive(Clone)]
+479pub struct ValidationTokenConfig {
+480    pub(crate) lifetime: Duration,
+481    pub(crate) log: Arc<dyn TokenLog>,
+482    pub(crate) sent: u32,
+483}
+484
+485impl ValidationTokenConfig {
+486    /// Duration after an address validation token was issued for which it's considered valid
+487    ///
+488    /// This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.
+489    ///
+490    /// Defaults to 2 weeks.
+491    pub fn lifetime(&mut self, value: Duration) -> &mut Self {
+492        self.lifetime = value;
+493        self
+494    }
+495
+496    #[allow(rustdoc::redundant_explicit_links)] // which links are redundant depends on features
+497    /// Set a custom [`TokenLog`]
+498    ///
+499    /// If the `bloom` feature is enabled (which it is by default), defaults to a default
+500    /// [`BloomTokenLog`][crate::BloomTokenLog], which is suitable for most internet applications.
+501    ///
+502    /// If the `bloom` feature is disabled, defaults to [`NoneTokenLog`][crate::NoneTokenLog],
+503    /// which makes the server ignore all address validation tokens (that is, tokens originating
+504    /// from NEW_TOKEN frames--retry tokens are not affected).
+505    pub fn log(&mut self, log: Arc<dyn TokenLog>) -> &mut Self {
+506        self.log = log;
+507        self
+508    }
+509
+510    /// Number of address validation tokens sent to a client when its path is validated
+511    ///
+512    /// This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.
+513    ///
+514    /// If the `bloom` feature is enabled (which it is by default), defaults to 2. Otherwise,
+515    /// defaults to 0.
+516    pub fn sent(&mut self, value: u32) -> &mut Self {
+517        self.sent = value;
+518        self
+519    }
+520}
+521
+522impl Default for ValidationTokenConfig {
+523    fn default() -> Self {
+524        #[cfg(feature = "bloom")]
+525        let log = Arc::new(BloomTokenLog::default());
+526        #[cfg(not(feature = "bloom"))]
+527        let log = Arc::new(NoneTokenLog);
+528        Self {
+529            lifetime: Duration::from_secs(2 * 7 * 24 * 60 * 60),
+530            log,
+531            sent: if cfg!(feature = "bloom") { 2 } else { 0 },
+532        }
+533    }
+534}
+535
+536impl fmt::Debug for ValidationTokenConfig {
+537    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+538        fmt.debug_struct("ServerValidationTokenConfig")
+539            .field("lifetime", &self.lifetime)
+540            // log not debug
+541            .field("sent", &self.sent)
+542            .finish_non_exhaustive()
+543    }
+544}
+545
+546/// Configuration for outgoing connections
+547///
+548/// Default values should be suitable for most internet applications.
+549#[derive(Clone)]
+550#[non_exhaustive]
+551pub struct ClientConfig {
+552    /// Transport configuration to use
+553    pub(crate) transport: Arc<TransportConfig>,
+554
+555    /// Cryptographic configuration to use
+556    pub(crate) crypto: Arc<dyn crypto::ClientConfig>,
+557
+558    /// Validation token store to use
+559    pub(crate) token_store: Arc<dyn TokenStore>,
+560
+561    /// Provider that populates the destination connection ID of Initial Packets
+562    pub(crate) initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>,
+563
+564    /// QUIC protocol version to use
+565    pub(crate) version: u32,
+566}
+567
+568impl ClientConfig {
+569    /// Create a default config with a particular cryptographic config
+570    pub fn new(crypto: Arc<dyn crypto::ClientConfig>) -> Self {
+571        Self {
+572            transport: Default::default(),
+573            crypto,
+574            token_store: Arc::new(TokenMemoryCache::default()),
+575            initial_dst_cid_provider: Arc::new(|| {
+576                RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid()
+577            }),
+578            version: 1,
+579        }
+580    }
+581
+582    /// Configure how to populate the destination CID of the initial packet when attempting to
+583    /// establish a new connection
+584    ///
+585    /// By default, it's populated with random bytes with reasonable length, so unless you have
+586    /// a good reason, you do not need to change it.
+587    ///
+588    /// When prefer to override the default, please note that the generated connection ID MUST be
+589    /// at least 8 bytes long and unpredictable, as per section 7.2 of RFC 9000.
+590    pub fn initial_dst_cid_provider(
+591        &mut self,
+592        initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>,
+593    ) -> &mut Self {
+594        self.initial_dst_cid_provider = initial_dst_cid_provider;
+595        self
+596    }
+597
+598    /// Set a custom [`TransportConfig`]
+599    pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self {
+600        self.transport = transport;
+601        self
+602    }
+603
+604    /// Set a custom [`TokenStore`]
+605    ///
+606    /// Defaults to [`TokenMemoryCache`], which is suitable for most internet applications.
+607    pub fn token_store(&mut self, store: Arc<dyn TokenStore>) -> &mut Self {
+608        self.token_store = store;
+609        self
+610    }
+611
+612    /// Set the QUIC version to use
+613    pub fn version(&mut self, version: u32) -> &mut Self {
+614        self.version = version;
+615        self
+616    }
+617}
+618
+619#[cfg(all(feature = "rustls", any(feature = "aws-lc-rs", feature = "ring")))]
+620impl ClientConfig {
+621    /// Create a client configuration that trusts the platform's native roots
+622    #[cfg(feature = "platform-verifier")]
+623    pub fn try_with_platform_verifier() -> Result<Self, rustls::Error> {
+624        Ok(Self::new(Arc::new(
+625            crypto::rustls::QuicClientConfig::with_platform_verifier()?,
+626        )))
+627    }
+628
+629    /// Create a client configuration that trusts specified trust anchors
+630    pub fn with_root_certificates(
+631        roots: Arc<rustls::RootCertStore>,
+632    ) -> Result<Self, rustls::client::VerifierBuilderError> {
+633        Ok(Self::new(Arc::new(crypto::rustls::QuicClientConfig::new(
+634            WebPkiServerVerifier::builder_with_provider(roots, configured_provider()).build()?,
+635        ))))
+636    }
+637}
+638
+639impl fmt::Debug for ClientConfig {
+640    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+641        fmt.debug_struct("ClientConfig")
+642            .field("transport", &self.transport)
+643            // crypto not debug
+644            // token_store not debug
+645            .field("version", &self.version)
+646            .finish_non_exhaustive()
+647    }
+648}
+649
+650/// Errors in the configuration of an endpoint
+651#[derive(Debug, Error, Clone, PartialEq, Eq)]
+652#[non_exhaustive]
+653pub enum ConfigError {
+654    /// Value exceeds supported bounds
+655    #[error("value exceeds supported bounds")]
+656    OutOfBounds,
+657}
+658
+659impl From<TryFromIntError> for ConfigError {
+660    fn from(_: TryFromIntError) -> Self {
+661        Self::OutOfBounds
+662    }
+663}
+664
+665impl From<VarIntBoundsExceeded> for ConfigError {
+666    fn from(_: VarIntBoundsExceeded) -> Self {
+667        Self::OutOfBounds
+668    }
+669}
+670
+671/// Object to get current [`SystemTime`]
+672///
+673/// This exists to allow system time to be mocked in tests, or wherever else desired.
+674pub trait TimeSource: Send + Sync {
+675    /// Get [`SystemTime::now()`](SystemTime::now) or the mocked equivalent
+676    fn now(&self) -> SystemTime;
+677}
+678
+679/// Default implementation of [`TimeSource`]
+680///
+681/// Implements `now` by calling [`SystemTime::now()`](SystemTime::now).
+682pub struct StdSystemTime;
+683
+684impl TimeSource for StdSystemTime {
+685    fn now(&self) -> SystemTime {
+686        SystemTime::now()
+687    }
+688}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/config/qlog.rs.html b/pr/509/docs/src/noq_proto/config/qlog.rs.html new file mode 100644 index 000000000..fbfb7fa77 --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/config/transport.rs.html b/pr/509/docs/src/noq_proto/config/transport.rs.html new file mode 100644 index 000000000..aa22739e6 --- /dev/null +++ b/pr/509/docs/src/noq_proto/config/transport.rs.html @@ -0,0 +1,912 @@ +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/// When multipath is required and has not been explicitly enabled, this value will be used for
+18/// [`TransportConfig::max_concurrent_multipath_paths`].
+19const DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED_: NonZeroU32 = {
+20    match NonZeroU32::new(12) {
+21        Some(v) => v,
+22        None => panic!("to enable multipath this must be positive, which clearly it is"),
+23    }
+24};
+25
+26/// When multipath is required and has not been explicitly enabled, this value will be used for
+27///
+28/// [`TransportConfig::max_concurrent_multipath_paths`].
+29#[cfg(doc)]
+30pub const DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED: NonZeroU32 =
+31    DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED_;
+32
+33/// Parameters governing the core QUIC state machine
+34///
+35/// Default values should be suitable for most internet applications. Applications protocols which
+36/// forbid remotely-initiated streams should set `max_concurrent_bidi_streams` and
+37/// `max_concurrent_uni_streams` to zero.
+38///
+39/// In some cases, performance or resource requirements can be improved by tuning these values to
+40/// suit a particular application and/or network connection. In particular, data window sizes can be
+41/// tuned for a particular expected round trip time, link capacity, and memory availability. Tuning
+42/// for higher bandwidths and latencies increases worst-case memory consumption, but does not impair
+43/// performance at lower bandwidths and latencies. The default configuration is tuned for a 100Mbps
+44/// link with a 100ms round trip time.
+45#[derive(Clone)]
+46pub struct TransportConfig {
+47    pub(crate) max_concurrent_bidi_streams: VarInt,
+48    pub(crate) max_concurrent_uni_streams: VarInt,
+49    pub(crate) max_idle_timeout: Option<VarInt>,
+50    pub(crate) stream_receive_window: VarInt,
+51    pub(crate) receive_window: VarInt,
+52    pub(crate) send_window: u64,
+53    pub(crate) send_fairness: bool,
+54
+55    pub(crate) packet_threshold: u32,
+56    pub(crate) time_threshold: f32,
+57    pub(crate) initial_rtt: Duration,
+58    pub(crate) initial_mtu: u16,
+59    pub(crate) min_mtu: u16,
+60    pub(crate) mtu_discovery_config: Option<MtuDiscoveryConfig>,
+61    pub(crate) pad_to_mtu: bool,
+62    pub(crate) ack_frequency_config: Option<AckFrequencyConfig>,
+63
+64    pub(crate) persistent_congestion_threshold: u32,
+65    pub(crate) keep_alive_interval: Option<Duration>,
+66    pub(crate) crypto_buffer_size: usize,
+67    pub(crate) allow_spin: bool,
+68    pub(crate) datagram_receive_buffer_size: Option<usize>,
+69    pub(crate) datagram_send_buffer_size: usize,
+70    #[cfg(test)]
+71    pub(crate) deterministic_packet_numbers: bool,
+72
+73    pub(crate) congestion_controller_factory: Arc<dyn congestion::ControllerFactory + Send + Sync>,
+74
+75    pub(crate) enable_segmentation_offload: bool,
+76
+77    pub(crate) address_discovery_role: address_discovery::Role,
+78
+79    pub(crate) max_concurrent_multipath_paths: Option<NonZeroU32>,
+80
+81    pub(crate) default_path_max_idle_timeout: Option<Duration>,
+82    pub(crate) default_path_keep_alive_interval: Option<Duration>,
+83
+84    pub(crate) max_remote_nat_traversal_addresses: Option<NonZeroU8>,
+85
+86    #[cfg(feature = "qlog")]
+87    pub(crate) qlog_factory: Option<Arc<dyn QlogFactory>>,
+88}
+89
+90impl TransportConfig {
+91    /// Maximum number of incoming bidirectional streams that may be open concurrently
+92    ///
+93    /// Must be nonzero for the peer to open any bidirectional streams.
+94    ///
+95    /// Worst-case memory use is directly proportional to `max_concurrent_bidi_streams *
+96    /// stream_receive_window`, with an upper bound proportional to `receive_window`.
+97    pub fn max_concurrent_bidi_streams(&mut self, value: VarInt) -> &mut Self {
+98        self.max_concurrent_bidi_streams = value;
+99        self
+100    }
+101
+102    /// Variant of `max_concurrent_bidi_streams` affecting unidirectional streams
+103    pub fn max_concurrent_uni_streams(&mut self, value: VarInt) -> &mut Self {
+104        self.max_concurrent_uni_streams = value;
+105        self
+106    }
+107
+108    /// Maximum duration of inactivity to accept before timing out the connection.
+109    ///
+110    /// The true idle timeout is the minimum of this and the peer's own max idle timeout. `None`
+111    /// represents an infinite timeout. Defaults to 30 seconds.
+112    ///
+113    /// **WARNING**: If a peer or its network path malfunctions or acts maliciously, an infinite
+114    /// idle timeout can result in permanently hung futures!
+115    ///
+116    /// ```
+117    /// # use std::{convert::TryInto, time::Duration};
+118    /// # use noq_proto::{TransportConfig, VarInt, VarIntBoundsExceeded};
+119    /// # fn main() -> Result<(), VarIntBoundsExceeded> {
+120    /// let mut config = TransportConfig::default();
+121    ///
+122    /// // Set the idle timeout as `VarInt`-encoded milliseconds
+123    /// config.max_idle_timeout(Some(VarInt::from_u32(10_000).into()));
+124    ///
+125    /// // Set the idle timeout as a `Duration`
+126    /// config.max_idle_timeout(Some(Duration::from_secs(10).try_into()?));
+127    /// # Ok(())
+128    /// # }
+129    /// ```
+130    pub fn max_idle_timeout(&mut self, value: Option<IdleTimeout>) -> &mut Self {
+131        self.max_idle_timeout = value.map(|t| t.0);
+132        self
+133    }
+134
+135    /// Maximum number of bytes the peer may transmit without acknowledgement on any one stream
+136    /// before becoming blocked.
+137    ///
+138    /// This should be set to at least the expected connection latency multiplied by the maximum
+139    /// desired throughput. Setting this smaller than `receive_window` helps ensure that a single
+140    /// stream doesn't monopolize receive buffers, which may otherwise occur if the application
+141    /// chooses not to read from a large stream for a time while still requiring data on other
+142    /// streams.
+143    pub fn stream_receive_window(&mut self, value: VarInt) -> &mut Self {
+144        self.stream_receive_window = value;
+145        self
+146    }
+147
+148    /// Maximum number of bytes the peer may transmit across all streams of a connection before
+149    /// becoming blocked.
+150    ///
+151    /// This should be set to at least the expected connection latency multiplied by the maximum
+152    /// desired throughput. Larger values can be useful to allow maximum throughput within a
+153    /// stream while another is blocked.
+154    pub fn receive_window(&mut self, value: VarInt) -> &mut Self {
+155        self.receive_window = value;
+156        self
+157    }
+158
+159    /// Maximum number of bytes to transmit to a peer without acknowledgment
+160    ///
+161    /// Provides an upper bound on memory when communicating with peers that issue large amounts of
+162    /// flow control credit. Endpoints that wish to handle large numbers of connections robustly
+163    /// should take care to set this low enough to guarantee memory exhaustion does not occur if
+164    /// every connection uses the entire window.
+165    pub fn send_window(&mut self, value: u64) -> &mut Self {
+166        self.send_window = value;
+167        self
+168    }
+169
+170    /// Whether to implement fair queuing for send streams having the same priority.
+171    ///
+172    /// When enabled, connections schedule data from outgoing streams having the same priority in a
+173    /// round-robin fashion. When disabled, streams are scheduled in the order they are written to.
+174    ///
+175    /// Note that this only affects streams with the same priority. Higher priority streams always
+176    /// take precedence over lower priority streams.
+177    ///
+178    /// Disabling fairness can reduce fragmentation and protocol overhead for workloads that use
+179    /// many small streams.
+180    pub fn send_fairness(&mut self, value: bool) -> &mut Self {
+181        self.send_fairness = value;
+182        self
+183    }
+184
+185    /// Maximum reordering in packet number space before FACK style loss detection considers a
+186    /// packet lost. Should not be less than 3, per RFC5681.
+187    pub fn packet_threshold(&mut self, value: u32) -> &mut Self {
+188        self.packet_threshold = value;
+189        self
+190    }
+191
+192    /// Maximum reordering in time space before time based loss detection considers a packet lost,
+193    /// as a factor of RTT
+194    pub fn time_threshold(&mut self, value: f32) -> &mut Self {
+195        self.time_threshold = value;
+196        self
+197    }
+198
+199    /// The RTT used before an RTT sample is taken
+200    pub fn initial_rtt(&mut self, value: Duration) -> &mut Self {
+201        self.initial_rtt = value;
+202        self
+203    }
+204
+205    /// The initial value to be used as the maximum UDP payload size before running MTU discovery
+206    /// (see [`TransportConfig::mtu_discovery_config`]).
+207    ///
+208    /// Must be at least 1200, which is the default, and known to be safe for typical internet
+209    /// applications. Larger values are more efficient, but increase the risk of packet loss due to
+210    /// exceeding the network path's IP MTU. If the provided value is higher than what the network
+211    /// path actually supports, packet loss will eventually trigger black hole detection and bring
+212    /// it down to [`TransportConfig::min_mtu`].
+213    pub fn initial_mtu(&mut self, value: u16) -> &mut Self {
+214        self.initial_mtu = value.max(INITIAL_MTU);
+215        self
+216    }
+217
+218    pub(crate) fn get_initial_mtu(&self) -> u16 {
+219        self.initial_mtu.max(self.min_mtu)
+220    }
+221
+222    /// The maximum UDP payload size guaranteed to be supported by the network.
+223    ///
+224    /// Must be at least 1200, which is the default, and lower than or equal to
+225    /// [`TransportConfig::initial_mtu`].
+226    ///
+227    /// Real-world MTUs can vary according to ISP, VPN, and properties of intermediate network links
+228    /// outside of either endpoint's control. Extreme care should be used when raising this value
+229    /// outside of private networks where these factors are fully controlled. If the provided value
+230    /// is higher than what the network path actually supports, the result will be unpredictable and
+231    /// catastrophic packet loss, without a possibility of repair. Prefer
+232    /// [`TransportConfig::initial_mtu`] together with
+233    /// [`TransportConfig::mtu_discovery_config`] to set a maximum UDP payload size that robustly
+234    /// adapts to the network.
+235    pub fn min_mtu(&mut self, value: u16) -> &mut Self {
+236        self.min_mtu = value.max(INITIAL_MTU);
+237        self
+238    }
+239
+240    /// Specifies the MTU discovery config (see [`MtuDiscoveryConfig`] for details).
+241    ///
+242    /// Enabled by default.
+243    pub fn mtu_discovery_config(&mut self, value: Option<MtuDiscoveryConfig>) -> &mut Self {
+244        self.mtu_discovery_config = value;
+245        self
+246    }
+247
+248    /// Pad UDP datagrams carrying application data to current maximum UDP payload size
+249    ///
+250    /// Disabled by default. UDP datagrams containing loss probes are exempt from padding.
+251    ///
+252    /// Enabling this helps mitigate traffic analysis by network observers, but it increases
+253    /// bandwidth usage. Without this mitigation precise plain text size of application datagrams as
+254    /// well as the total size of stream write bursts can be inferred by observers under certain
+255    /// conditions. This analysis requires either an uncongested connection or application datagrams
+256    /// too large to be coalesced.
+257    pub fn pad_to_mtu(&mut self, value: bool) -> &mut Self {
+258        self.pad_to_mtu = value;
+259        self
+260    }
+261
+262    /// Specifies the ACK frequency config (see [`AckFrequencyConfig`] for details)
+263    ///
+264    /// The provided configuration will be ignored if the peer does not support the acknowledgement
+265    /// frequency QUIC extension.
+266    ///
+267    /// Defaults to `None`, which disables controlling the peer's acknowledgement frequency. Even
+268    /// if set to `None`, the local side still supports the acknowledgement frequency QUIC
+269    /// extension and may use it in other ways.
+270    pub fn ack_frequency_config(&mut self, value: Option<AckFrequencyConfig>) -> &mut Self {
+271        self.ack_frequency_config = value;
+272        self
+273    }
+274
+275    /// Number of consecutive PTOs after which network is considered to be experiencing persistent congestion.
+276    pub fn persistent_congestion_threshold(&mut self, value: u32) -> &mut Self {
+277        self.persistent_congestion_threshold = value;
+278        self
+279    }
+280
+281    /// Period of inactivity before sending a keep-alive packet
+282    ///
+283    /// Keep-alive packets prevent an inactive but otherwise healthy connection from timing out.
+284    ///
+285    /// `None` to disable, which is the default. Only one side of any given connection needs keep-alive
+286    /// enabled for the connection to be preserved. Must be set lower than the idle_timeout of both
+287    /// peers to be effective.
+288    pub fn keep_alive_interval(&mut self, value: Option<Duration>) -> &mut Self {
+289        self.keep_alive_interval = value;
+290        self
+291    }
+292
+293    /// Maximum quantity of out-of-order crypto layer data to buffer
+294    pub fn crypto_buffer_size(&mut self, value: usize) -> &mut Self {
+295        self.crypto_buffer_size = value;
+296        self
+297    }
+298
+299    /// Whether the implementation is permitted to set the spin bit on this connection
+300    ///
+301    /// This allows passive observers to easily judge the round trip time of a connection, which can
+302    /// be useful for network administration but sacrifices a small amount of privacy.
+303    pub fn allow_spin(&mut self, value: bool) -> &mut Self {
+304        self.allow_spin = value;
+305        self
+306    }
+307
+308    /// Maximum number of incoming application datagram bytes to buffer, or None to disable
+309    /// incoming datagrams
+310    ///
+311    /// The peer is forbidden to send single datagrams larger than this size. If the aggregate size
+312    /// of all datagrams that have been received from the peer but not consumed by the application
+313    /// exceeds this value, old datagrams are dropped until it is no longer exceeded.
+314    pub fn datagram_receive_buffer_size(&mut self, value: Option<usize>) -> &mut Self {
+315        self.datagram_receive_buffer_size = value;
+316        self
+317    }
+318
+319    /// Maximum number of outgoing application datagram bytes to buffer
+320    ///
+321    /// While datagrams are sent ASAP, it is possible for an application to generate data faster
+322    /// than the link, or even the underlying hardware, can transmit them. This limits the amount of
+323    /// memory that may be consumed in that case. When the send buffer is full and a new datagram is
+324    /// sent, older datagrams are dropped until sufficient space is available.
+325    pub fn datagram_send_buffer_size(&mut self, value: usize) -> &mut Self {
+326        self.datagram_send_buffer_size = value;
+327        self
+328    }
+329
+330    /// Whether to force every packet number to be used
+331    ///
+332    /// By default, packet numbers are occasionally skipped to ensure peers aren't ACKing packets
+333    /// before they see them.
+334    #[cfg(test)]
+335    pub(crate) fn deterministic_packet_numbers(&mut self, enabled: bool) -> &mut Self {
+336        self.deterministic_packet_numbers = enabled;
+337        self
+338    }
+339
+340    /// How to construct new `congestion::Controller`s
+341    ///
+342    /// Typically the refcounted configuration of a `congestion::Controller`,
+343    /// e.g. a `congestion::NewRenoConfig`.
+344    ///
+345    /// # Example
+346    /// ```
+347    /// # use noq_proto::*; use std::sync::Arc;
+348    /// let mut config = TransportConfig::default();
+349    /// config.congestion_controller_factory(Arc::new(congestion::NewRenoConfig::default()));
+350    /// ```
+351    pub fn congestion_controller_factory(
+352        &mut self,
+353        factory: Arc<dyn congestion::ControllerFactory + Send + Sync + 'static>,
+354    ) -> &mut Self {
+355        self.congestion_controller_factory = factory;
+356        self
+357    }
+358
+359    /// Whether to use "Generic Segmentation Offload" to accelerate transmits, when supported by the
+360    /// environment
+361    ///
+362    /// Defaults to `true`.
+363    ///
+364    /// GSO dramatically reduces CPU consumption when sending large numbers of packets with the same
+365    /// headers, such as when transmitting bulk data on a connection. However, it is not supported
+366    /// by all network interface drivers or packet inspection tools. `noq-udp` will attempt to
+367    /// disable GSO automatically when unavailable, but this can lead to spurious packet loss at
+368    /// startup, temporarily degrading performance.
+369    pub fn enable_segmentation_offload(&mut self, enabled: bool) -> &mut Self {
+370        self.enable_segmentation_offload = enabled;
+371        self
+372    }
+373
+374    /// Whether to send observed address reports to peers.
+375    ///
+376    /// This will aid peers in inferring their reachable address, which in most NATd networks
+377    /// will not be easily available to them.
+378    pub fn send_observed_address_reports(&mut self, enabled: bool) -> &mut Self {
+379        self.address_discovery_role.send_reports_to_peers(enabled);
+380        self
+381    }
+382
+383    /// Whether to receive observed address reports from other peers.
+384    ///
+385    /// Peers with the address discovery extension enabled that are willing to provide observed
+386    /// address reports will do so if this transport parameter is set. In general, observed address
+387    /// reports cannot be trusted. This, however, can aid the current endpoint in inferring its
+388    /// reachable address, which in most NATd networks will not be easily available.
+389    pub fn receive_observed_address_reports(&mut self, enabled: bool) -> &mut Self {
+390        self.address_discovery_role
+391            .receive_reports_from_peers(enabled);
+392        self
+393    }
+394
+395    /// Enables the Multipath Extension for QUIC.
+396    ///
+397    /// Setting this to any nonzero value will enable the Multipath Extension for QUIC,
+398    /// <https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/>.
+399    ///
+400    /// The value provided specifies the number maximum number of paths this endpoint may open
+401    /// concurrently when multipath is negotiated. For any path to be opened, the remote must
+402    /// enable multipath as well.
+403    pub fn max_concurrent_multipath_paths(&mut self, max_concurrent: u32) -> &mut Self {
+404        self.max_concurrent_multipath_paths = NonZeroU32::new(max_concurrent);
+405        self
+406    }
+407
+408    /// Sets a default per-path maximum idle timeout
+409    ///
+410    /// If the path is idle for this long the path will be abandoned. Bear in mind this will
+411    /// interact with the [`TransportConfig::max_idle_timeout`], if the last path is
+412    /// abandoned the entire connection will be closed.
+413    ///
+414    /// You can also change this using [`Connection::set_path_max_idle_timeout`] for
+415    /// existing paths.
+416    ///
+417    /// [`Connection::set_path_max_idle_timeout`]: crate::Connection::set_path_max_idle_timeout
+418    pub fn default_path_max_idle_timeout(&mut self, timeout: Option<Duration>) -> &mut Self {
+419        self.default_path_max_idle_timeout = timeout;
+420        self
+421    }
+422
+423    /// Sets a default per-path keep alive interval
+424    ///
+425    /// Note that this does not interact with the connection-wide
+426    /// [`TransportConfig::keep_alive_interval`].  This setting will keep this path active,
+427    /// [`TransportConfig::keep_alive_interval`] will keep the connection active, with no
+428    /// control over which path is used for this.
+429    ///
+430    /// You can also change this using [`Connection::set_path_keep_alive_interval`] for
+431    /// existing path.
+432    ///
+433    /// [`Connection::set_path_keep_alive_interval`]: crate::Connection::set_path_keep_alive_interval
+434    pub fn default_path_keep_alive_interval(&mut self, interval: Option<Duration>) -> &mut Self {
+435        self.default_path_keep_alive_interval = interval;
+436        self
+437    }
+438
+439    /// Get the initial max [`crate::PathId`] this endpoint allows.
+440    ///
+441    /// Returns `None` if multipath is disabled.
+442    pub(crate) fn get_initial_max_path_id(&self) -> Option<crate::PathId> {
+443        self.max_concurrent_multipath_paths
+444            // a max_concurrent_multipath_paths value of 1 only allows the first path, which
+445            // has id 0
+446            .map(|nonzero_concurrent| nonzero_concurrent.get() - 1)
+447            .map(Into::into)
+448    }
+449
+450    /// Sets the maximum number of nat traversal addresses this endpoint allows the remote to
+451    /// advertise
+452    ///
+453    /// Setting this to any nonzero value will enable n0's nat traversal protocol, loosely based in
+454    /// the Nat Traversal Extension for QUIC, see
+455    /// <https://www.ietf.org/archive/id/draft-seemann-quic-nat-traversal-02.html>
+456    ///
+457    /// This implementation expects the multipath extension to be enabled as well. if not yet
+458    /// enabled via [`Self::max_concurrent_multipath_paths`], a default value of
+459    /// [`DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED`] will be used.
+460    pub fn set_max_remote_nat_traversal_addresses(&mut self, max_addresses: u8) -> &mut Self {
+461        self.max_remote_nat_traversal_addresses = NonZeroU8::new(max_addresses);
+462        if max_addresses != 0 && self.max_concurrent_multipath_paths.is_none() {
+463            self.max_concurrent_multipath_paths(
+464                DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED_.get(),
+465            );
+466        }
+467        self
+468    }
+469
+470    /// Configures qlog capturing by setting a [`QlogFactory`].
+471    ///
+472    /// This assigns a [`QlogFactory`] that produces qlog capture configurations for
+473    /// individual connections.
+474    #[cfg(feature = "qlog")]
+475    pub fn qlog_factory(&mut self, factory: Arc<dyn QlogFactory>) -> &mut Self {
+476        self.qlog_factory = Some(factory);
+477        self
+478    }
+479
+480    /// Configures qlog capturing through the `QLOGDIR` environment variable.
+481    ///
+482    /// This uses [`QlogFileFactory::from_env`] to create a factory to write qlog traces
+483    /// into the directory set through the `QLOGDIR` environment variable.
+484    ///
+485    /// If `QLOGDIR` is not set, no traces will be written. If `QLOGDIR` is set to a path
+486    /// that does not exist, it will be created.
+487    ///
+488    /// The files will be prefixed with `prefix`.
+489    #[cfg(feature = "qlog")]
+490    pub fn qlog_from_env(&mut self, prefix: &str) -> &mut Self {
+491        self.qlog_factory(Arc::new(QlogFileFactory::from_env().with_prefix(prefix)))
+492    }
+493
+494    /// Configures qlog capturing into a directory.
+495    ///
+496    /// This uses [`QlogFileFactory`] to create a factory to write qlog traces into
+497    /// the specified directory.  The files will be prefixed with `prefix`.
+498    #[cfg(feature = "qlog")]
+499    pub fn qlog_from_path(&mut self, path: impl AsRef<Path>, prefix: &str) -> &mut Self {
+500        self.qlog_factory(Arc::new(
+501            QlogFileFactory::new(path.as_ref().to_owned()).with_prefix(prefix),
+502        ))
+503    }
+504
+505    pub(crate) fn create_qlog_sink(
+506        &self,
+507        side: Side,
+508        remote: SocketAddr,
+509        initial_dst_cid: ConnectionId,
+510        now: Instant,
+511    ) -> QlogSink {
+512        #[cfg(not(feature = "qlog"))]
+513        let sink = {
+514            let _ = (side, remote, initial_dst_cid, now);
+515            QlogSink::default()
+516        };
+517
+518        #[cfg(feature = "qlog")]
+519        let sink = {
+520            if let Some(config) = self
+521                .qlog_factory
+522                .as_ref()
+523                .and_then(|factory| factory.for_connection(side, remote, initial_dst_cid, now))
+524            {
+525                QlogSink::new(config, initial_dst_cid, side, now)
+526            } else {
+527                QlogSink::default()
+528            }
+529        };
+530
+531        sink
+532    }
+533}
+534
+535impl Default for TransportConfig {
+536    fn default() -> Self {
+537        const EXPECTED_RTT: u32 = 100; // ms
+538        const MAX_STREAM_BANDWIDTH: u32 = 12500 * 1000; // bytes/s
+539        // Window size needed to avoid pipeline
+540        // stalls
+541        const STREAM_RWND: u32 = MAX_STREAM_BANDWIDTH / 1000 * EXPECTED_RTT;
+542
+543        Self {
+544            max_concurrent_bidi_streams: 100u32.into(),
+545            max_concurrent_uni_streams: 100u32.into(),
+546            // 30 second default recommended by RFC 9308 § 3.2
+547            max_idle_timeout: Some(VarInt(30_000)),
+548            stream_receive_window: STREAM_RWND.into(),
+549            receive_window: VarInt::MAX,
+550            send_window: (8 * STREAM_RWND).into(),
+551            send_fairness: true,
+552
+553            packet_threshold: 3,
+554            time_threshold: 9.0 / 8.0,
+555            initial_rtt: Duration::from_millis(333), // per spec, intentionally distinct from EXPECTED_RTT
+556            initial_mtu: INITIAL_MTU,
+557            min_mtu: INITIAL_MTU,
+558            mtu_discovery_config: Some(MtuDiscoveryConfig::default()),
+559            pad_to_mtu: false,
+560            ack_frequency_config: None,
+561
+562            persistent_congestion_threshold: 3,
+563            keep_alive_interval: None,
+564            crypto_buffer_size: 16 * 1024,
+565            allow_spin: true,
+566            datagram_receive_buffer_size: Some(STREAM_RWND as usize),
+567            datagram_send_buffer_size: 1024 * 1024,
+568            #[cfg(test)]
+569            deterministic_packet_numbers: false,
+570
+571            congestion_controller_factory: Arc::new(congestion::CubicConfig::default()),
+572
+573            enable_segmentation_offload: true,
+574
+575            address_discovery_role: address_discovery::Role::default(),
+576
+577            // disabled multipath by default
+578            max_concurrent_multipath_paths: None,
+579            default_path_max_idle_timeout: None,
+580            default_path_keep_alive_interval: None,
+581
+582            // nat traversal disabled by default
+583            max_remote_nat_traversal_addresses: None,
+584
+585            #[cfg(feature = "qlog")]
+586            qlog_factory: None,
+587        }
+588    }
+589}
+590
+591impl fmt::Debug for TransportConfig {
+592    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+593        let Self {
+594            max_concurrent_bidi_streams,
+595            max_concurrent_uni_streams,
+596            max_idle_timeout,
+597            stream_receive_window,
+598            receive_window,
+599            send_window,
+600            send_fairness,
+601            packet_threshold,
+602            time_threshold,
+603            initial_rtt,
+604            initial_mtu,
+605            min_mtu,
+606            mtu_discovery_config,
+607            pad_to_mtu,
+608            ack_frequency_config,
+609            persistent_congestion_threshold,
+610            keep_alive_interval,
+611            crypto_buffer_size,
+612            allow_spin,
+613            datagram_receive_buffer_size,
+614            datagram_send_buffer_size,
+615            #[cfg(test)]
+616                deterministic_packet_numbers: _,
+617            congestion_controller_factory: _,
+618            enable_segmentation_offload,
+619            address_discovery_role,
+620            max_concurrent_multipath_paths,
+621            default_path_max_idle_timeout,
+622            default_path_keep_alive_interval,
+623            max_remote_nat_traversal_addresses,
+624            #[cfg(feature = "qlog")]
+625            qlog_factory,
+626        } = self;
+627        let mut s = fmt.debug_struct("TransportConfig");
+628
+629        s.field("max_concurrent_bidi_streams", max_concurrent_bidi_streams)
+630            .field("max_concurrent_uni_streams", max_concurrent_uni_streams)
+631            .field("max_idle_timeout", max_idle_timeout)
+632            .field("stream_receive_window", stream_receive_window)
+633            .field("receive_window", receive_window)
+634            .field("send_window", send_window)
+635            .field("send_fairness", send_fairness)
+636            .field("packet_threshold", packet_threshold)
+637            .field("time_threshold", time_threshold)
+638            .field("initial_rtt", initial_rtt)
+639            .field("initial_mtu", initial_mtu)
+640            .field("min_mtu", min_mtu)
+641            .field("mtu_discovery_config", mtu_discovery_config)
+642            .field("pad_to_mtu", pad_to_mtu)
+643            .field("ack_frequency_config", ack_frequency_config)
+644            .field(
+645                "persistent_congestion_threshold",
+646                persistent_congestion_threshold,
+647            )
+648            .field("keep_alive_interval", keep_alive_interval)
+649            .field("crypto_buffer_size", crypto_buffer_size)
+650            .field("allow_spin", allow_spin)
+651            .field("datagram_receive_buffer_size", datagram_receive_buffer_size)
+652            .field("datagram_send_buffer_size", datagram_send_buffer_size)
+653            // congestion_controller_factory not debug
+654            .field("enable_segmentation_offload", enable_segmentation_offload)
+655            .field("address_discovery_role", address_discovery_role)
+656            .field(
+657                "max_concurrent_multipath_paths",
+658                max_concurrent_multipath_paths,
+659            )
+660            .field(
+661                "default_path_max_idle_timeout",
+662                default_path_max_idle_timeout,
+663            )
+664            .field(
+665                "default_path_keep_alive_interval",
+666                default_path_keep_alive_interval,
+667            )
+668            .field(
+669                "max_remote_nat_traversal_addresses",
+670                max_remote_nat_traversal_addresses,
+671            );
+672        #[cfg(feature = "qlog")]
+673        s.field("qlog_factory", &qlog_factory.is_some());
+674
+675        s.finish_non_exhaustive()
+676    }
+677}
+678
+679/// Parameters for controlling the peer's acknowledgement frequency
+680///
+681/// The parameters provided in this config will be sent to the peer at the beginning of the
+682/// connection, so it can take them into account when sending acknowledgements (see each parameter's
+683/// description for details on how it influences acknowledgement frequency).
+684///
+685/// noq's implementation follows the fourth draft of the
+686/// [QUIC Acknowledgement Frequency extension](https://datatracker.ietf.org/doc/html/draft-ietf-quic-ack-frequency-04).
+687/// The defaults produce behavior slightly different than the behavior without this extension,
+688/// because they change the way reordered packets are handled (see
+689/// [`AckFrequencyConfig::reordering_threshold`] for details).
+690#[derive(Clone, Debug)]
+691pub struct AckFrequencyConfig {
+692    pub(crate) ack_eliciting_threshold: VarInt,
+693    pub(crate) max_ack_delay: Option<Duration>,
+694    pub(crate) reordering_threshold: VarInt,
+695}
+696
+697impl AckFrequencyConfig {
+698    /// The ack-eliciting threshold we will request the peer to use
+699    ///
+700    /// This threshold represents the number of ack-eliciting packets an endpoint may receive
+701    /// without immediately sending an ACK.
+702    ///
+703    /// The remote peer should send at least one ACK frame when more than this number of
+704    /// ack-eliciting packets have been received. A value of 0 results in a receiver immediately
+705    /// acknowledging every ack-eliciting packet.
+706    ///
+707    /// Defaults to 1, which sends ACK frames for every other ack-eliciting packet.
+708    pub fn ack_eliciting_threshold(&mut self, value: VarInt) -> &mut Self {
+709        self.ack_eliciting_threshold = value;
+710        self
+711    }
+712
+713    /// The `max_ack_delay` we will request the peer to use
+714    ///
+715    /// This parameter represents the maximum amount of time that an endpoint waits before sending
+716    /// an ACK when the ack-eliciting threshold hasn't been reached.
+717    ///
+718    /// The effective `max_ack_delay` will be clamped to be at least the peer's `min_ack_delay`
+719    /// transport parameter, and at most the greater of the current path RTT or 25ms.
+720    ///
+721    /// Defaults to `None`, in which case the peer's original `max_ack_delay` will be used, as
+722    /// obtained from its transport parameters.
+723    pub fn max_ack_delay(&mut self, value: Option<Duration>) -> &mut Self {
+724        self.max_ack_delay = value;
+725        self
+726    }
+727
+728    /// The reordering threshold we will request the peer to use
+729    ///
+730    /// This threshold represents the amount of out-of-order packets that will trigger an endpoint
+731    /// to send an ACK, without waiting for `ack_eliciting_threshold` to be exceeded or for
+732    /// `max_ack_delay` to be elapsed.
+733    ///
+734    /// A value of 0 indicates out-of-order packets do not elicit an immediate ACK. A value of 1
+735    /// immediately acknowledges any packets that are received out of order (this is also the
+736    /// behavior when the extension is disabled).
+737    ///
+738    /// It is recommended to set this value to [`TransportConfig::packet_threshold`] minus one.
+739    /// Since the default value for [`TransportConfig::packet_threshold`] is 3, this value defaults
+740    /// to 2.
+741    pub fn reordering_threshold(&mut self, value: VarInt) -> &mut Self {
+742        self.reordering_threshold = value;
+743        self
+744    }
+745}
+746
+747impl Default for AckFrequencyConfig {
+748    fn default() -> Self {
+749        Self {
+750            ack_eliciting_threshold: VarInt(1),
+751            max_ack_delay: None,
+752            reordering_threshold: VarInt(2),
+753        }
+754    }
+755}
+756
+757/// Parameters governing MTU discovery.
+758///
+759/// # The why of MTU discovery
+760///
+761/// By design, QUIC ensures during the handshake that the network path between the client and the
+762/// server is able to transmit unfragmented UDP packets with a body of 1200 bytes. In other words,
+763/// once the connection is established, we know that the network path's maximum transmission unit
+764/// (MTU) is of at least 1200 bytes (plus IP and UDP headers). Because of this, a QUIC endpoint can
+765/// split outgoing data in packets of 1200 bytes, with confidence that the network will be able to
+766/// deliver them (if the endpoint were to send bigger packets, they could prove too big and end up
+767/// being dropped).
+768///
+769/// There is, however, a significant overhead associated to sending a packet. If the same
+770/// information can be sent in fewer packets, that results in higher throughput. The amount of
+771/// packets that need to be sent is inversely proportional to the MTU: the higher the MTU, the
+772/// bigger the packets that can be sent, and the fewer packets that are needed to transmit a given
+773/// amount of bytes.
+774///
+775/// Most networks have an MTU higher than 1200. Through MTU discovery, endpoints can detect the
+776/// path's MTU and, if it turns out to be higher, start sending bigger packets.
+777///
+778/// # MTU discovery internals
+779///
+780/// noq implements MTU discovery through DPLPMTUD (Datagram Packetization Layer Path MTU
+781/// Discovery), described in [section 14.3 of RFC
+782/// 9000](https://www.rfc-editor.org/rfc/rfc9000.html#section-14.3). This method consists of sending
+783/// QUIC packets padded to a particular size (called PMTU probes), and waiting to see if the remote
+784/// peer responds with an ACK. If an ACK is received, that means the probe arrived at the remote
+785/// peer, which in turn means that the network path's MTU is of at least the packet's size. If the
+786/// probe is lost, it is sent another 2 times before concluding that the MTU is lower than the
+787/// packet's size.
+788///
+789/// MTU discovery runs on a schedule (e.g. every 600 seconds) specified through
+790/// [`MtuDiscoveryConfig::interval`]. The first run happens right after the handshake, and
+791/// subsequent discoveries are scheduled to run when the interval has elapsed, starting from the
+792/// last time when MTU discovery completed.
+793///
+794/// Since the search space for MTUs is quite big (the smallest possible MTU is 1200, and the highest
+795/// is 65527), noq performs a binary search to keep the number of probes as low as possible. The
+796/// lower bound of the search is equal to [`TransportConfig::initial_mtu`] in the
+797/// initial MTU discovery run, and is equal to the currently discovered MTU in subsequent runs. The
+798/// upper bound is determined by the minimum of [`MtuDiscoveryConfig::upper_bound`] and the
+799/// `max_udp_payload_size` transport parameter received from the peer during the handshake.
+800///
+801/// # Black hole detection
+802///
+803/// If, at some point, the network path no longer accepts packets of the detected size, packet loss
+804/// will eventually trigger black hole detection and reset the detected MTU to 1200. In that case,
+805/// MTU discovery will be triggered after [`MtuDiscoveryConfig::black_hole_cooldown`] (ignoring the
+806/// timer that was set based on [`MtuDiscoveryConfig::interval`]).
+807///
+808/// # Interaction between peers
+809///
+810/// There is no guarantee that the MTU on the path between A and B is the same as the MTU of the
+811/// path between B and A. Therefore, each peer in the connection needs to run MTU discovery
+812/// independently in order to discover the path's MTU.
+813#[derive(Clone, Debug)]
+814pub struct MtuDiscoveryConfig {
+815    pub(crate) interval: Duration,
+816    pub(crate) upper_bound: u16,
+817    pub(crate) minimum_change: u16,
+818    pub(crate) black_hole_cooldown: Duration,
+819}
+820
+821impl MtuDiscoveryConfig {
+822    /// Specifies the time to wait after completing MTU discovery before starting a new MTU
+823    /// discovery run.
+824    ///
+825    /// Defaults to 600 seconds, as recommended by [RFC
+826    /// 8899](https://www.rfc-editor.org/rfc/rfc8899).
+827    pub fn interval(&mut self, value: Duration) -> &mut Self {
+828        self.interval = value;
+829        self
+830    }
+831
+832    /// Specifies the upper bound to the max UDP payload size that MTU discovery will search for.
+833    ///
+834    /// Defaults to 1452, to stay within Ethernet's MTU when using IPv4 and IPv6. The highest
+835    /// allowed value is 65527, which corresponds to the maximum permitted UDP payload on IPv6.
+836    ///
+837    /// It is safe to use an arbitrarily high upper bound, regardless of the network path's MTU. The
+838    /// only drawback is that MTU discovery might take more time to finish.
+839    pub fn upper_bound(&mut self, value: u16) -> &mut Self {
+840        self.upper_bound = value.min(MAX_UDP_PAYLOAD);
+841        self
+842    }
+843
+844    /// Specifies the amount of time that MTU discovery should wait after a black hole was detected
+845    /// before running again. Defaults to one minute.
+846    ///
+847    /// Black hole detection can be spuriously triggered in case of congestion, so it makes sense to
+848    /// try MTU discovery again after a short period of time.
+849    pub fn black_hole_cooldown(&mut self, value: Duration) -> &mut Self {
+850        self.black_hole_cooldown = value;
+851        self
+852    }
+853
+854    /// Specifies the minimum MTU change to stop the MTU discovery phase.
+855    /// Defaults to 20.
+856    pub fn minimum_change(&mut self, value: u16) -> &mut Self {
+857        self.minimum_change = value;
+858        self
+859    }
+860}
+861
+862impl Default for MtuDiscoveryConfig {
+863    fn default() -> Self {
+864        Self {
+865            interval: Duration::from_secs(600),
+866            upper_bound: 1452,
+867            black_hole_cooldown: Duration::from_secs(60),
+868            minimum_change: 20,
+869        }
+870    }
+871}
+872
+873/// Maximum duration of inactivity to accept before timing out the connection
+874///
+875/// This wraps an underlying [`VarInt`], representing the duration in milliseconds. Values can be
+876/// constructed by converting directly from `VarInt`, or using `TryFrom<Duration>`.
+877///
+878/// ```
+879/// # use std::{convert::TryFrom, time::Duration};
+880/// # use noq_proto::{IdleTimeout, VarIntBoundsExceeded, VarInt};
+881/// # fn main() -> Result<(), VarIntBoundsExceeded> {
+882/// // A `VarInt`-encoded value in milliseconds
+883/// let timeout = IdleTimeout::from(VarInt::from_u32(10_000));
+884///
+885/// // Try to convert a `Duration` into a `VarInt`-encoded timeout
+886/// let timeout = IdleTimeout::try_from(Duration::from_secs(10))?;
+887/// # Ok(())
+888/// # }
+889/// ```
+890#[derive(Default, Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
+891pub struct IdleTimeout(VarInt);
+892
+893impl From<VarInt> for IdleTimeout {
+894    fn from(inner: VarInt) -> Self {
+895        Self(inner)
+896    }
+897}
+898
+899impl std::convert::TryFrom<Duration> for IdleTimeout {
+900    type Error = VarIntBoundsExceeded;
+901
+902    fn try_from(timeout: Duration) -> Result<Self, Self::Error> {
+903        let inner = VarInt::try_from(timeout.as_millis())?;
+904        Ok(Self(inner))
+905    }
+906}
+907
+908impl fmt::Debug for IdleTimeout {
+909    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+910        self.0.fmt(f)
+911    }
+912}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/congestion.rs.html b/pr/509/docs/src/noq_proto/congestion.rs.html new file mode 100644 index 000000000..89f42787a --- /dev/null +++ b/pr/509/docs/src/noq_proto/congestion.rs.html @@ -0,0 +1,112 @@ +congestion.rs - source

noq_proto/
congestion.rs

1//! Logic for controlling the rate at which data is sent
+2
+3use crate::Instant;
+4use crate::connection::RttEstimator;
+5use std::any::Any;
+6use std::sync::Arc;
+7
+8mod bbr;
+9mod cubic;
+10mod new_reno;
+11
+12pub use bbr::{Bbr, BbrConfig};
+13pub use cubic::{Cubic, CubicConfig};
+14pub use new_reno::{NewReno, NewRenoConfig};
+15
+16/// Common interface for different congestion controllers
+17pub trait Controller: Send + Sync + std::fmt::Debug {
+18    /// One or more packets were just sent
+19    #[allow(unused_variables)]
+20    fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64) {}
+21
+22    /// Packet deliveries were confirmed
+23    ///
+24    /// `app_limited` indicates whether the connection was blocked on outgoing
+25    /// application data prior to receiving these acknowledgements.
+26    #[allow(unused_variables)]
+27    fn on_ack(
+28        &mut self,
+29        now: Instant,
+30        sent: Instant,
+31        bytes: u64,
+32        app_limited: bool,
+33        rtt: &RttEstimator,
+34    ) {
+35    }
+36
+37    /// Packets are acked in batches, all with the same `now` argument. This indicates one of those batches has completed.
+38    #[allow(unused_variables)]
+39    fn on_end_acks(
+40        &mut self,
+41        now: Instant,
+42        in_flight: u64,
+43        app_limited: bool,
+44        largest_packet_num_acked: Option<u64>,
+45    ) {
+46    }
+47
+48    /// Packets were deemed lost or marked congested
+49    ///
+50    /// `in_persistent_congestion` indicates whether all packets sent within the persistent
+51    /// congestion threshold period ending when the most recent packet in this batch was sent were
+52    /// lost.
+53    /// `lost_bytes` indicates how many bytes were lost. This value will be 0 for ECN triggers.
+54    fn on_congestion_event(
+55        &mut self,
+56        now: Instant,
+57        sent: Instant,
+58        is_persistent_congestion: bool,
+59        is_ecn: bool,
+60        lost_bytes: u64,
+61    );
+62
+63    /// Packets were incorrectly deemed lost
+64    ///
+65    /// This function is called when all packets that were deemed lost (for instance because
+66    /// of packet reordering) are acknowledged after the congestion event was raised.
+67    fn on_spurious_congestion_event(&mut self) {}
+68
+69    /// The known MTU for the current network path has been updated
+70    fn on_mtu_update(&mut self, new_mtu: u16);
+71
+72    /// Number of ack-eliciting bytes that may be in flight
+73    fn window(&self) -> u64;
+74
+75    /// Retrieve implementation-specific metrics used to populate `qlog` traces when they are enabled
+76    fn metrics(&self) -> ControllerMetrics {
+77        ControllerMetrics {
+78            congestion_window: self.window(),
+79            ssthresh: None,
+80            pacing_rate: None,
+81        }
+82    }
+83
+84    /// Duplicate the controller's state
+85    fn clone_box(&self) -> Box<dyn Controller>;
+86
+87    /// Initial congestion window
+88    fn initial_window(&self) -> u64;
+89
+90    /// Returns Self for use in down-casting to extract implementation details
+91    fn into_any(self: Box<Self>) -> Box<dyn Any>;
+92}
+93
+94/// Common congestion controller metrics
+95#[derive(Default, Debug)]
+96#[non_exhaustive]
+97pub struct ControllerMetrics {
+98    /// Congestion window (bytes)
+99    pub congestion_window: u64,
+100    /// Slow start threshold (bytes)
+101    pub ssthresh: Option<u64>,
+102    /// Pacing rate (bits/s)
+103    pub pacing_rate: Option<u64>,
+104}
+105
+106/// Constructs controllers on demand
+107pub trait ControllerFactory {
+108    /// Construct a fresh `Controller`
+109    fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller>;
+110}
+111
+112const BASE_DATAGRAM_SIZE: u64 = 1200;
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/congestion/bbr/bw_estimation.rs.html b/pr/509/docs/src/noq_proto/congestion/bbr/bw_estimation.rs.html new file mode 100644 index 000000000..a85f594a8 --- /dev/null +++ b/pr/509/docs/src/noq_proto/congestion/bbr/bw_estimation.rs.html @@ -0,0 +1,100 @@ +bw_estimation.rs - source

noq_proto/congestion/bbr/
bw_estimation.rs

1use std::fmt::{Debug, Display, Formatter};
+2
+3use super::min_max::MinMax;
+4use crate::{Duration, Instant};
+5
+6#[derive(Clone, Debug, Default)]
+7pub(crate) struct BandwidthEstimation {
+8    total_acked: u64,
+9    prev_total_acked: u64,
+10    acked_time: Option<Instant>,
+11    prev_acked_time: Option<Instant>,
+12    total_sent: u64,
+13    prev_total_sent: u64,
+14    sent_time: Option<Instant>,
+15    prev_sent_time: Option<Instant>,
+16    max_filter: MinMax,
+17    acked_at_last_window: u64,
+18}
+19
+20impl BandwidthEstimation {
+21    pub(crate) fn on_sent(&mut self, now: Instant, bytes: u64) {
+22        self.prev_total_sent = self.total_sent;
+23        self.total_sent += bytes;
+24        self.prev_sent_time = self.sent_time;
+25        self.sent_time = Some(now);
+26    }
+27
+28    pub(crate) fn on_ack(
+29        &mut self,
+30        now: Instant,
+31        _sent: Instant,
+32        bytes: u64,
+33        round: u64,
+34        app_limited: bool,
+35    ) {
+36        self.prev_total_acked = self.total_acked;
+37        self.total_acked += bytes;
+38        self.prev_acked_time = self.acked_time;
+39        self.acked_time = Some(now);
+40
+41        let Some(prev_sent_time) = self.prev_sent_time else {
+42            return;
+43        };
+44
+45        let send_rate = match self.sent_time {
+46            Some(sent_time) if sent_time > prev_sent_time => Self::bw_from_delta(
+47                self.total_sent - self.prev_total_sent,
+48                sent_time - prev_sent_time,
+49            )
+50            .unwrap_or(0),
+51            _ => u64::MAX, // will take the min of send and ack, so this is just a skip
+52        };
+53
+54        let ack_rate = match self.prev_acked_time {
+55            Some(prev_acked_time) => Self::bw_from_delta(
+56                self.total_acked - self.prev_total_acked,
+57                now - prev_acked_time,
+58            )
+59            .unwrap_or(0),
+60            None => 0,
+61        };
+62
+63        let bandwidth = send_rate.min(ack_rate);
+64        if !app_limited && self.max_filter.get() < bandwidth {
+65            self.max_filter.update_max(round, bandwidth);
+66        }
+67    }
+68
+69    pub(crate) fn bytes_acked_this_window(&self) -> u64 {
+70        self.total_acked - self.acked_at_last_window
+71    }
+72
+73    pub(crate) fn end_acks(&mut self, _current_round: u64, _app_limited: bool) {
+74        self.acked_at_last_window = self.total_acked;
+75    }
+76
+77    pub(crate) fn get_estimate(&self) -> u64 {
+78        self.max_filter.get()
+79    }
+80
+81    pub(crate) const fn bw_from_delta(bytes: u64, delta: Duration) -> Option<u64> {
+82        let window_duration_ns = delta.as_nanos();
+83        if window_duration_ns == 0 {
+84            return None;
+85        }
+86        let b_ns = bytes * 1_000_000_000;
+87        let bytes_per_second = b_ns / (window_duration_ns as u64);
+88        Some(bytes_per_second)
+89    }
+90}
+91
+92impl Display for BandwidthEstimation {
+93    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+94        write!(
+95            f,
+96            "{:.3} MB/s",
+97            self.get_estimate() as f32 / (1024 * 1024) as f32
+98        )
+99    }
+100}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/congestion/bbr/min_max.rs.html b/pr/509/docs/src/noq_proto/congestion/bbr/min_max.rs.html new file mode 100644 index 000000000..5bb0f3208 --- /dev/null +++ b/pr/509/docs/src/noq_proto/congestion/bbr/min_max.rs.html @@ -0,0 +1,152 @@ +min_max.rs - source

noq_proto/congestion/bbr/
min_max.rs

1/*
+2 * Based on Google code released under BSD license here:
+3 * https://groups.google.com/forum/#!topic/bbr-dev/3RTgkzi5ZD8
+4 */
+5
+6/*
+7 * Kathleen Nichols' algorithm for tracking the minimum (or maximum)
+8 * value of a data stream over some fixed time interval.  (E.g.,
+9 * the minimum RTT over the past five minutes.) It uses constant
+10 * space and constant time per update yet almost always delivers
+11 * the same minimum as an implementation that has to keep all the
+12 * data in the window.
+13 *
+14 * The algorithm keeps track of the best, 2nd best & 3rd best min
+15 * values, maintaining an invariant that the measurement time of
+16 * the n'th best >= n-1'th best. It also makes sure that the three
+17 * values are widely separated in the time window since that bounds
+18 * the worse case error when that data is monotonically increasing
+19 * over the window.
+20 *
+21 * Upon getting a new min, we can forget everything earlier because
+22 * it has no value - the new min is <= everything else in the window
+23 * by definition and it samples the most recent. So we restart fresh on
+24 * every new min and overwrites 2nd & 3rd choices. The same property
+25 * holds for 2nd & 3rd best.
+26 */
+27
+28use std::fmt::Debug;
+29
+30#[derive(Copy, Clone, Debug)]
+31pub(super) struct MinMax {
+32    /// round count, not a timestamp
+33    window: u64,
+34    samples: [MinMaxSample; 3],
+35}
+36
+37impl MinMax {
+38    pub(super) fn get(&self) -> u64 {
+39        self.samples[0].value
+40    }
+41
+42    fn fill(&mut self, sample: MinMaxSample) {
+43        self.samples.fill(sample);
+44    }
+45
+46    pub(super) fn reset(&mut self) {
+47        self.fill(Default::default())
+48    }
+49
+50    /// update_min is also defined in the original source, but removed here since it is not used.
+51    pub(super) fn update_max(&mut self, current_round: u64, measurement: u64) {
+52        let sample = MinMaxSample {
+53            time: current_round,
+54            value: measurement,
+55        };
+56
+57        if self.samples[0].value == 0  /* uninitialised */
+58            || /* found new max? */ sample.value >= self.samples[0].value
+59            || /* nothing left in window? */ sample.time - self.samples[2].time > self.window
+60        {
+61            self.fill(sample); /* forget earlier samples */
+62            return;
+63        }
+64
+65        if sample.value >= self.samples[1].value {
+66            self.samples[2] = sample;
+67            self.samples[1] = sample;
+68        } else if sample.value >= self.samples[2].value {
+69            self.samples[2] = sample;
+70        }
+71
+72        self.subwin_update(sample);
+73    }
+74
+75    /* As time advances, update the 1st, 2nd, and 3rd choices. */
+76    fn subwin_update(&mut self, sample: MinMaxSample) {
+77        let dt = sample.time - self.samples[0].time;
+78        if dt > self.window {
+79            /*
+80             * Passed entire window without a new sample so make 2nd
+81             * choice the new sample & 3rd choice the new 2nd choice.
+82             * we may have to iterate this since our 2nd choice
+83             * may also be outside the window (we checked on entry
+84             * that the third choice was in the window).
+85             */
+86            self.samples[0] = self.samples[1];
+87            self.samples[1] = self.samples[2];
+88            self.samples[2] = sample;
+89            if sample.time - self.samples[0].time > self.window {
+90                self.samples[0] = self.samples[1];
+91                self.samples[1] = self.samples[2];
+92                self.samples[2] = sample;
+93            }
+94        } else if self.samples[1].time == self.samples[0].time && dt > self.window / 4 {
+95            /*
+96             * We've passed a quarter of the window without a new sample
+97             * so take a 2nd choice from the 2nd quarter of the window.
+98             */
+99            self.samples[2] = sample;
+100            self.samples[1] = sample;
+101        } else if self.samples[2].time == self.samples[1].time && dt > self.window / 2 {
+102            /*
+103             * We've passed half the window without finding a new sample
+104             * so take a 3rd choice from the last half of the window
+105             */
+106            self.samples[2] = sample;
+107        }
+108    }
+109}
+110
+111impl Default for MinMax {
+112    fn default() -> Self {
+113        Self {
+114            window: 10,
+115            samples: [Default::default(); 3],
+116        }
+117    }
+118}
+119
+120#[derive(Debug, Copy, Clone, Default)]
+121struct MinMaxSample {
+122    /// round number, not a timestamp
+123    time: u64,
+124    value: u64,
+125}
+126
+127#[cfg(test)]
+128mod test {
+129    use super::*;
+130
+131    #[test]
+132    fn test() {
+133        let round = 25;
+134        let mut min_max = MinMax::default();
+135        min_max.update_max(round + 1, 100);
+136        assert_eq!(100, min_max.get());
+137        min_max.update_max(round + 3, 120);
+138        assert_eq!(120, min_max.get());
+139        min_max.update_max(round + 5, 160);
+140        assert_eq!(160, min_max.get());
+141        min_max.update_max(round + 7, 100);
+142        assert_eq!(160, min_max.get());
+143        min_max.update_max(round + 10, 100);
+144        assert_eq!(160, min_max.get());
+145        min_max.update_max(round + 14, 100);
+146        assert_eq!(160, min_max.get());
+147        min_max.update_max(round + 16, 100);
+148        assert_eq!(100, min_max.get());
+149        min_max.update_max(round + 18, 130);
+150        assert_eq!(130, min_max.get());
+151    }
+152}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/congestion/bbr/mod.rs.html b/pr/509/docs/src/noq_proto/congestion/bbr/mod.rs.html new file mode 100644 index 000000000..430d6217f --- /dev/null +++ b/pr/509/docs/src/noq_proto/congestion/bbr/mod.rs.html @@ -0,0 +1,651 @@ +mod.rs - source

noq_proto/congestion/bbr/
mod.rs

1use std::any::Any;
+2use std::fmt::Debug;
+3use std::sync::Arc;
+4
+5use rand::{Rng, SeedableRng};
+6
+7use crate::congestion::ControllerMetrics;
+8use crate::congestion::bbr::bw_estimation::BandwidthEstimation;
+9use crate::congestion::bbr::min_max::MinMax;
+10use crate::connection::RttEstimator;
+11use crate::{Duration, Instant};
+12
+13use super::{BASE_DATAGRAM_SIZE, Controller, ControllerFactory};
+14
+15mod bw_estimation;
+16mod min_max;
+17
+18/// Experimental! Use at your own risk.
+19///
+20/// Aims for reduced buffer bloat and improved performance over high bandwidth-delay product networks.
+21/// Based on google's quiche implementation <https://source.chromium.org/chromium/chromium/src/+/master:net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc>
+22/// of BBR <https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control>.
+23/// More discussion and links at <https://groups.google.com/g/bbr-dev>.
+24#[derive(Debug, Clone)]
+25pub struct Bbr {
+26    config: Arc<BbrConfig>,
+27    current_mtu: u64,
+28    max_bandwidth: BandwidthEstimation,
+29    acked_bytes: u64,
+30    mode: Mode,
+31    loss_state: LossState,
+32    recovery_state: RecoveryState,
+33    recovery_window: u64,
+34    is_at_full_bandwidth: bool,
+35    pacing_gain: f32,
+36    high_gain: f32,
+37    drain_gain: f32,
+38    cwnd_gain: f32,
+39    high_cwnd_gain: f32,
+40    last_cycle_start: Option<Instant>,
+41    current_cycle_offset: u8,
+42    init_cwnd: u64,
+43    min_cwnd: u64,
+44    prev_in_flight_count: u64,
+45    exit_probe_rtt_at: Option<Instant>,
+46    probe_rtt_last_started_at: Option<Instant>,
+47    min_rtt: Duration,
+48    exiting_quiescence: bool,
+49    pacing_rate: u64,
+50    max_acked_packet_number: u64,
+51    max_sent_packet_number: u64,
+52    end_recovery_at_packet_number: u64,
+53    cwnd: u64,
+54    current_round_trip_end_packet_number: u64,
+55    round_count: u64,
+56    bw_at_last_round: u64,
+57    round_wo_bw_gain: u64,
+58    ack_aggregation: AckAggregationState,
+59    random_number_generator: rand::rngs::StdRng,
+60}
+61
+62impl Bbr {
+63    /// Construct a state using the given `config` and current time `now`
+64    pub fn new(config: Arc<BbrConfig>, current_mtu: u16) -> Self {
+65        let initial_window = config.initial_window;
+66        Self {
+67            config,
+68            current_mtu: current_mtu as u64,
+69            max_bandwidth: BandwidthEstimation::default(),
+70            acked_bytes: 0,
+71            mode: Mode::Startup,
+72            loss_state: Default::default(),
+73            recovery_state: RecoveryState::NotInRecovery,
+74            recovery_window: 0,
+75            is_at_full_bandwidth: false,
+76            pacing_gain: K_DEFAULT_HIGH_GAIN,
+77            high_gain: K_DEFAULT_HIGH_GAIN,
+78            drain_gain: 1.0 / K_DEFAULT_HIGH_GAIN,
+79            cwnd_gain: K_DEFAULT_HIGH_GAIN,
+80            high_cwnd_gain: K_DEFAULT_HIGH_GAIN,
+81            last_cycle_start: None,
+82            current_cycle_offset: 0,
+83            init_cwnd: initial_window,
+84            min_cwnd: calculate_min_window(current_mtu as u64),
+85            prev_in_flight_count: 0,
+86            exit_probe_rtt_at: None,
+87            probe_rtt_last_started_at: None,
+88            min_rtt: Default::default(),
+89            exiting_quiescence: false,
+90            pacing_rate: 0,
+91            max_acked_packet_number: 0,
+92            max_sent_packet_number: 0,
+93            end_recovery_at_packet_number: 0,
+94            cwnd: initial_window,
+95            current_round_trip_end_packet_number: 0,
+96            round_count: 0,
+97            bw_at_last_round: 0,
+98            round_wo_bw_gain: 0,
+99            ack_aggregation: AckAggregationState::default(),
+100            random_number_generator: rand::rngs::StdRng::from_os_rng(),
+101        }
+102    }
+103
+104    fn enter_startup_mode(&mut self) {
+105        self.mode = Mode::Startup;
+106        self.pacing_gain = self.high_gain;
+107        self.cwnd_gain = self.high_cwnd_gain;
+108    }
+109
+110    fn enter_probe_bandwidth_mode(&mut self, now: Instant) {
+111        self.mode = Mode::ProbeBw;
+112        self.cwnd_gain = K_DERIVED_HIGH_CWNDGAIN;
+113        self.last_cycle_start = Some(now);
+114        // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is
+115        // excluded because in that case increased gain and decreased gain would not
+116        // follow each other.
+117        let mut rand_index = self
+118            .random_number_generator
+119            .random_range(0..K_PACING_GAIN.len() as u8 - 1);
+120        if rand_index >= 1 {
+121            rand_index += 1;
+122        }
+123        self.current_cycle_offset = rand_index;
+124        self.pacing_gain = K_PACING_GAIN[rand_index as usize];
+125    }
+126
+127    fn update_recovery_state(&mut self, is_round_start: bool) {
+128        // Exit recovery when there are no losses for a round.
+129        if self.loss_state.has_losses() {
+130            self.end_recovery_at_packet_number = self.max_sent_packet_number;
+131        }
+132        match self.recovery_state {
+133            // Enter conservation on the first loss.
+134            RecoveryState::NotInRecovery if self.loss_state.has_losses() => {
+135                self.recovery_state = RecoveryState::Conservation;
+136                // This will cause the |recovery_window| to be set to the
+137                // correct value in CalculateRecoveryWindow().
+138                self.recovery_window = 0;
+139                // Since the conservation phase is meant to be lasting for a whole
+140                // round, extend the current round as if it were started right now.
+141                self.current_round_trip_end_packet_number = self.max_sent_packet_number;
+142            }
+143            RecoveryState::Growth | RecoveryState::Conservation => {
+144                if self.recovery_state == RecoveryState::Conservation && is_round_start {
+145                    self.recovery_state = RecoveryState::Growth;
+146                }
+147                // Exit recovery if appropriate.
+148                if !self.loss_state.has_losses()
+149                    && self.max_acked_packet_number > self.end_recovery_at_packet_number
+150                {
+151                    self.recovery_state = RecoveryState::NotInRecovery;
+152                }
+153            }
+154            _ => {}
+155        }
+156    }
+157
+158    fn update_gain_cycle_phase(&mut self, now: Instant, in_flight: u64) {
+159        // In most cases, the cycle is advanced after an RTT passes.
+160        let mut should_advance_gain_cycling = self
+161            .last_cycle_start
+162            .map(|last_cycle_start| now.duration_since(last_cycle_start) > self.min_rtt)
+163            .unwrap_or(false);
+164        // If the pacing gain is above 1.0, the connection is trying to probe the
+165        // bandwidth by increasing the number of bytes in flight to at least
+166        // pacing_gain * BDP.  Make sure that it actually reaches the target, as
+167        // long as there are no losses suggesting that the buffers are not able to
+168        // hold that much.
+169        if self.pacing_gain > 1.0
+170            && !self.loss_state.has_losses()
+171            && self.prev_in_flight_count < self.get_target_cwnd(self.pacing_gain)
+172        {
+173            should_advance_gain_cycling = false;
+174        }
+175
+176        // If pacing gain is below 1.0, the connection is trying to drain the extra
+177        // queue which could have been incurred by probing prior to it.  If the
+178        // number of bytes in flight falls down to the estimated BDP value earlier,
+179        // conclude that the queue has been successfully drained and exit this cycle
+180        // early.
+181        if self.pacing_gain < 1.0 && in_flight <= self.get_target_cwnd(1.0) {
+182            should_advance_gain_cycling = true;
+183        }
+184
+185        if should_advance_gain_cycling {
+186            self.current_cycle_offset = (self.current_cycle_offset + 1) % K_PACING_GAIN.len() as u8;
+187            self.last_cycle_start = Some(now);
+188            // Stay in low gain mode until the target BDP is hit.  Low gain mode
+189            // will be exited immediately when the target BDP is achieved.
+190            if DRAIN_TO_TARGET
+191                && self.pacing_gain < 1.0
+192                && (K_PACING_GAIN[self.current_cycle_offset as usize] - 1.0).abs() < f32::EPSILON
+193                && in_flight > self.get_target_cwnd(1.0)
+194            {
+195                return;
+196            }
+197            self.pacing_gain = K_PACING_GAIN[self.current_cycle_offset as usize];
+198        }
+199    }
+200
+201    fn maybe_exit_startup_or_drain(&mut self, now: Instant, in_flight: u64) {
+202        if self.mode == Mode::Startup && self.is_at_full_bandwidth {
+203            self.mode = Mode::Drain;
+204            self.pacing_gain = self.drain_gain;
+205            self.cwnd_gain = self.high_cwnd_gain;
+206        }
+207        if self.mode == Mode::Drain && in_flight <= self.get_target_cwnd(1.0) {
+208            self.enter_probe_bandwidth_mode(now);
+209        }
+210    }
+211
+212    fn is_min_rtt_expired(&self, now: Instant, app_limited: bool) -> bool {
+213        !app_limited
+214            && self
+215                .probe_rtt_last_started_at
+216                .map(|last| now.saturating_duration_since(last) > Duration::from_secs(10))
+217                .unwrap_or(true)
+218    }
+219
+220    fn maybe_enter_or_exit_probe_rtt(
+221        &mut self,
+222        now: Instant,
+223        is_round_start: bool,
+224        bytes_in_flight: u64,
+225        app_limited: bool,
+226    ) {
+227        let min_rtt_expired = self.is_min_rtt_expired(now, app_limited);
+228        if min_rtt_expired && !self.exiting_quiescence && self.mode != Mode::ProbeRtt {
+229            self.mode = Mode::ProbeRtt;
+230            self.pacing_gain = 1.0;
+231            // Do not decide on the time to exit ProbeRtt until the
+232            // |bytes_in_flight| is at the target small value.
+233            self.exit_probe_rtt_at = None;
+234            self.probe_rtt_last_started_at = Some(now);
+235        }
+236
+237        if self.mode == Mode::ProbeRtt {
+238            match self.exit_probe_rtt_at {
+239                None => {
+240                    // If the window has reached the appropriate size, schedule exiting
+241                    // ProbeRtt.  The CWND during ProbeRtt is
+242                    // kMinimumCongestionWindow, but we allow an extra packet since QUIC
+243                    // checks CWND before sending a packet.
+244                    if bytes_in_flight < self.get_probe_rtt_cwnd() + self.current_mtu {
+245                        const K_PROBE_RTT_TIME: Duration = Duration::from_millis(200);
+246                        self.exit_probe_rtt_at = Some(now + K_PROBE_RTT_TIME);
+247                    }
+248                }
+249                Some(exit_time) if is_round_start && now >= exit_time => {
+250                    if !self.is_at_full_bandwidth {
+251                        self.enter_startup_mode();
+252                    } else {
+253                        self.enter_probe_bandwidth_mode(now);
+254                    }
+255                }
+256                Some(_) => {}
+257            }
+258        }
+259
+260        self.exiting_quiescence = false;
+261    }
+262
+263    fn get_target_cwnd(&self, gain: f32) -> u64 {
+264        let bw = self.max_bandwidth.get_estimate();
+265        let bdp = self.min_rtt.as_micros() as u64 * bw;
+266        let bdpf = bdp as f64;
+267        let cwnd = ((gain as f64 * bdpf) / 1_000_000f64) as u64;
+268        // BDP estimate will be zero if no bandwidth samples are available yet.
+269        if cwnd == 0 {
+270            return self.init_cwnd;
+271        }
+272        cwnd.max(self.min_cwnd)
+273    }
+274
+275    fn get_probe_rtt_cwnd(&self) -> u64 {
+276        const K_MODERATE_PROBE_RTT_MULTIPLIER: f32 = 0.75;
+277        if PROBE_RTT_BASED_ON_BDP {
+278            return self.get_target_cwnd(K_MODERATE_PROBE_RTT_MULTIPLIER);
+279        }
+280        self.min_cwnd
+281    }
+282
+283    fn calculate_pacing_rate(&mut self) {
+284        let bw = self.max_bandwidth.get_estimate();
+285        if bw == 0 {
+286            return;
+287        }
+288        let target_rate = (bw as f64 * self.pacing_gain as f64) as u64;
+289        if self.is_at_full_bandwidth {
+290            self.pacing_rate = target_rate;
+291            return;
+292        }
+293
+294        // Pace at the rate of initial_window / RTT as soon as RTT measurements are
+295        // available.
+296        if self.pacing_rate == 0 && self.min_rtt.as_nanos() != 0 {
+297            self.pacing_rate =
+298                BandwidthEstimation::bw_from_delta(self.init_cwnd, self.min_rtt).unwrap();
+299            return;
+300        }
+301
+302        // Do not decrease the pacing rate during startup.
+303        if self.pacing_rate < target_rate {
+304            self.pacing_rate = target_rate;
+305        }
+306    }
+307
+308    fn calculate_cwnd(&mut self, bytes_acked: u64, excess_acked: u64) {
+309        if self.mode == Mode::ProbeRtt {
+310            return;
+311        }
+312        let mut target_window = self.get_target_cwnd(self.cwnd_gain);
+313        if self.is_at_full_bandwidth {
+314            // Add the max recently measured ack aggregation to CWND.
+315            target_window += self.ack_aggregation.max_ack_height.get();
+316        } else {
+317            // Add the most recent excess acked.  Because CWND never decreases in
+318            // STARTUP, this will automatically create a very localized max filter.
+319            target_window += excess_acked;
+320        }
+321        // Instead of immediately setting the target CWND as the new one, BBR grows
+322        // the CWND towards |target_window| by only increasing it |bytes_acked| at a
+323        // time.
+324        if self.is_at_full_bandwidth {
+325            self.cwnd = target_window.min(self.cwnd + bytes_acked);
+326        } else if (self.cwnd_gain < target_window as f32) || (self.acked_bytes < self.init_cwnd) {
+327            // If the connection is not yet out of startup phase, do not decrease
+328            // the window.
+329            self.cwnd += bytes_acked;
+330        }
+331
+332        // Enforce the limits on the congestion window.
+333        if self.cwnd < self.min_cwnd {
+334            self.cwnd = self.min_cwnd;
+335        }
+336    }
+337
+338    fn calculate_recovery_window(&mut self, bytes_acked: u64, bytes_lost: u64, in_flight: u64) {
+339        if !self.recovery_state.in_recovery() {
+340            return;
+341        }
+342        // Set up the initial recovery window.
+343        if self.recovery_window == 0 {
+344            self.recovery_window = self.min_cwnd.max(in_flight + bytes_acked);
+345            return;
+346        }
+347
+348        // Remove losses from the recovery window, while accounting for a potential
+349        // integer underflow.
+350        if self.recovery_window >= bytes_lost {
+351            self.recovery_window -= bytes_lost;
+352        } else {
+353            // k_max_segment_size = current_mtu
+354            self.recovery_window = self.current_mtu;
+355        }
+356        // In CONSERVATION mode, just subtracting losses is sufficient.  In GROWTH,
+357        // release additional |bytes_acked| to achieve a slow-start-like behavior.
+358        if self.recovery_state == RecoveryState::Growth {
+359            self.recovery_window += bytes_acked;
+360        }
+361
+362        // Sanity checks.  Ensure that we always allow to send at least an MSS or
+363        // |bytes_acked| in response, whichever is larger.
+364        self.recovery_window = self
+365            .recovery_window
+366            .max(in_flight + bytes_acked)
+367            .max(self.min_cwnd);
+368    }
+369
+370    /// <https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control#section-4.3.2.2>
+371    fn check_if_full_bw_reached(&mut self, app_limited: bool) {
+372        if app_limited {
+373            return;
+374        }
+375        let target = (self.bw_at_last_round as f64 * K_STARTUP_GROWTH_TARGET as f64) as u64;
+376        let bw = self.max_bandwidth.get_estimate();
+377        if bw >= target {
+378            self.bw_at_last_round = bw;
+379            self.round_wo_bw_gain = 0;
+380            self.ack_aggregation.max_ack_height.reset();
+381            return;
+382        }
+383
+384        self.round_wo_bw_gain += 1;
+385        if self.round_wo_bw_gain >= K_ROUND_TRIPS_WITHOUT_GROWTH_BEFORE_EXITING_STARTUP as u64
+386            || (self.recovery_state.in_recovery())
+387        {
+388            self.is_at_full_bandwidth = true;
+389        }
+390    }
+391}
+392
+393impl Controller for Bbr {
+394    fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64) {
+395        self.max_sent_packet_number = last_packet_number;
+396        self.max_bandwidth.on_sent(now, bytes);
+397    }
+398
+399    fn on_ack(
+400        &mut self,
+401        now: Instant,
+402        sent: Instant,
+403        bytes: u64,
+404        app_limited: bool,
+405        rtt: &RttEstimator,
+406    ) {
+407        self.max_bandwidth
+408            .on_ack(now, sent, bytes, self.round_count, app_limited);
+409        self.acked_bytes += bytes;
+410        if self.is_min_rtt_expired(now, app_limited) || self.min_rtt > rtt.min() {
+411            self.min_rtt = rtt.min();
+412        }
+413    }
+414
+415    fn on_end_acks(
+416        &mut self,
+417        now: Instant,
+418        in_flight: u64,
+419        app_limited: bool,
+420        largest_packet_num_acked: Option<u64>,
+421    ) {
+422        let bytes_acked = self.max_bandwidth.bytes_acked_this_window();
+423        let excess_acked = self.ack_aggregation.update_ack_aggregation_bytes(
+424            bytes_acked,
+425            now,
+426            self.round_count,
+427            self.max_bandwidth.get_estimate(),
+428        );
+429        self.max_bandwidth.end_acks(self.round_count, app_limited);
+430        if let Some(largest_acked_packet) = largest_packet_num_acked {
+431            self.max_acked_packet_number = largest_acked_packet;
+432        }
+433
+434        let mut is_round_start = false;
+435        if bytes_acked > 0 {
+436            is_round_start =
+437                self.max_acked_packet_number > self.current_round_trip_end_packet_number;
+438            if is_round_start {
+439                self.current_round_trip_end_packet_number = self.max_sent_packet_number;
+440                self.round_count += 1;
+441            }
+442        }
+443
+444        self.update_recovery_state(is_round_start);
+445
+446        if self.mode == Mode::ProbeBw {
+447            self.update_gain_cycle_phase(now, in_flight);
+448        }
+449
+450        if is_round_start && !self.is_at_full_bandwidth {
+451            self.check_if_full_bw_reached(app_limited);
+452        }
+453
+454        self.maybe_exit_startup_or_drain(now, in_flight);
+455
+456        self.maybe_enter_or_exit_probe_rtt(now, is_round_start, in_flight, app_limited);
+457
+458        // After the model is updated, recalculate the pacing rate and congestion window.
+459        self.calculate_pacing_rate();
+460        self.calculate_cwnd(bytes_acked, excess_acked);
+461        self.calculate_recovery_window(bytes_acked, self.loss_state.lost_bytes, in_flight);
+462
+463        self.prev_in_flight_count = in_flight;
+464        self.loss_state.reset();
+465    }
+466
+467    fn on_congestion_event(
+468        &mut self,
+469        _now: Instant,
+470        _sent: Instant,
+471        _is_persistent_congestion: bool,
+472        _is_ecn: bool,
+473        lost_bytes: u64,
+474    ) {
+475        self.loss_state.lost_bytes += lost_bytes;
+476    }
+477
+478    fn on_mtu_update(&mut self, new_mtu: u16) {
+479        self.current_mtu = new_mtu as u64;
+480        self.min_cwnd = calculate_min_window(self.current_mtu);
+481        self.init_cwnd = self.config.initial_window.max(self.min_cwnd);
+482        self.cwnd = self.cwnd.max(self.min_cwnd);
+483    }
+484
+485    fn window(&self) -> u64 {
+486        if self.mode == Mode::ProbeRtt {
+487            return self.get_probe_rtt_cwnd();
+488        } else if self.recovery_state.in_recovery() && self.mode != Mode::Startup {
+489            return self.cwnd.min(self.recovery_window);
+490        }
+491        self.cwnd
+492    }
+493
+494    fn metrics(&self) -> ControllerMetrics {
+495        ControllerMetrics {
+496            congestion_window: self.window(),
+497            ssthresh: None,
+498            pacing_rate: Some(self.pacing_rate * 8),
+499        }
+500    }
+501
+502    fn clone_box(&self) -> Box<dyn Controller> {
+503        Box::new(self.clone())
+504    }
+505
+506    fn initial_window(&self) -> u64 {
+507        self.config.initial_window
+508    }
+509
+510    fn into_any(self: Box<Self>) -> Box<dyn Any> {
+511        self
+512    }
+513}
+514
+515/// Configuration for the [`Bbr`] congestion controller
+516#[derive(Debug, Clone)]
+517pub struct BbrConfig {
+518    initial_window: u64,
+519}
+520
+521impl BbrConfig {
+522    /// Default limit on the amount of outstanding data in bytes.
+523    ///
+524    /// Recommended value: `min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))`
+525    pub fn initial_window(&mut self, value: u64) -> &mut Self {
+526        self.initial_window = value;
+527        self
+528    }
+529}
+530
+531impl Default for BbrConfig {
+532    fn default() -> Self {
+533        Self {
+534            initial_window: K_MAX_INITIAL_CONGESTION_WINDOW * BASE_DATAGRAM_SIZE,
+535        }
+536    }
+537}
+538
+539impl ControllerFactory for BbrConfig {
+540    fn build(self: Arc<Self>, _now: Instant, current_mtu: u16) -> Box<dyn Controller> {
+541        Box::new(Bbr::new(self, current_mtu))
+542    }
+543}
+544
+545#[derive(Debug, Default, Copy, Clone)]
+546struct AckAggregationState {
+547    max_ack_height: MinMax,
+548    aggregation_epoch_start_time: Option<Instant>,
+549    aggregation_epoch_bytes: u64,
+550}
+551
+552impl AckAggregationState {
+553    fn update_ack_aggregation_bytes(
+554        &mut self,
+555        newly_acked_bytes: u64,
+556        now: Instant,
+557        round: u64,
+558        max_bandwidth: u64,
+559    ) -> u64 {
+560        // Compute how many bytes are expected to be delivered, assuming max
+561        // bandwidth is correct.
+562        let expected_bytes_acked = max_bandwidth
+563            * now
+564                .saturating_duration_since(self.aggregation_epoch_start_time.unwrap_or(now))
+565                .as_micros() as u64
+566            / 1_000_000;
+567
+568        // Reset the current aggregation epoch as soon as the ack arrival rate is
+569        // less than or equal to the max bandwidth.
+570        if self.aggregation_epoch_bytes <= expected_bytes_acked {
+571            // Reset to start measuring a new aggregation epoch.
+572            self.aggregation_epoch_bytes = newly_acked_bytes;
+573            self.aggregation_epoch_start_time = Some(now);
+574            return 0;
+575        }
+576
+577        // Compute how many extra bytes were delivered vs max bandwidth.
+578        // Include the bytes most recently acknowledged to account for stretch acks.
+579        self.aggregation_epoch_bytes += newly_acked_bytes;
+580        let diff = self.aggregation_epoch_bytes - expected_bytes_acked;
+581        self.max_ack_height.update_max(round, diff);
+582        diff
+583    }
+584}
+585
+586#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+587enum Mode {
+588    // Startup phase of the connection.
+589    Startup,
+590    // After achieving the highest possible bandwidth during the startup, lower
+591    // the pacing rate in order to drain the queue.
+592    Drain,
+593    // Cruising mode.
+594    ProbeBw,
+595    // Temporarily slow down sending in order to empty the buffer and measure
+596    // the real minimum RTT.
+597    ProbeRtt,
+598}
+599
+600// Indicates how the congestion control limits the amount of bytes in flight.
+601#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+602enum RecoveryState {
+603    // Do not limit.
+604    NotInRecovery,
+605    // Allow an extra outstanding byte for each byte acknowledged.
+606    Conservation,
+607    // Allow two extra outstanding bytes for each byte acknowledged (slow
+608    // start).
+609    Growth,
+610}
+611
+612impl RecoveryState {
+613    pub(super) fn in_recovery(&self) -> bool {
+614        !matches!(self, Self::NotInRecovery)
+615    }
+616}
+617
+618#[derive(Debug, Clone, Default)]
+619struct LossState {
+620    lost_bytes: u64,
+621}
+622
+623impl LossState {
+624    pub(super) fn reset(&mut self) {
+625        self.lost_bytes = 0;
+626    }
+627
+628    pub(super) fn has_losses(&self) -> bool {
+629        self.lost_bytes != 0
+630    }
+631}
+632
+633fn calculate_min_window(current_mtu: u64) -> u64 {
+634    4 * current_mtu
+635}
+636
+637// The gain used for the STARTUP, equal to 2/ln(2).
+638const K_DEFAULT_HIGH_GAIN: f32 = 2.885;
+639// The newly derived CWND gain for STARTUP, 2.
+640const K_DERIVED_HIGH_CWNDGAIN: f32 = 2.0;
+641// The cycle of gains used during the ProbeBw stage.
+642const K_PACING_GAIN: [f32; 8] = [1.25, 0.75, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
+643
+644const K_STARTUP_GROWTH_TARGET: f32 = 1.25;
+645const K_ROUND_TRIPS_WITHOUT_GROWTH_BEFORE_EXITING_STARTUP: u8 = 3;
+646
+647// Do not allow initial congestion window to be greater than 200 packets.
+648const K_MAX_INITIAL_CONGESTION_WINDOW: u64 = 200;
+649
+650const PROBE_RTT_BASED_ON_BDP: bool = true;
+651const DRAIN_TO_TARGET: bool = true;
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/congestion/cubic.rs.html b/pr/509/docs/src/noq_proto/congestion/cubic.rs.html new file mode 100644 index 000000000..db23f47ef --- /dev/null +++ b/pr/509/docs/src/noq_proto/congestion/cubic.rs.html @@ -0,0 +1,299 @@ +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        app_limited: bool,
+111        rtt: &RttEstimator,
+112    ) {
+113        if app_limited
+114            || self
+115                .state
+116                .recovery_start_time
+117                .map(|recovery_start_time| sent <= recovery_start_time)
+118                .unwrap_or(false)
+119        {
+120            return;
+121        }
+122
+123        if self.state.window < self.state.ssthresh {
+124            // Slow start
+125            self.state.window += bytes;
+126        } else {
+127            // Congestion avoidance.
+128            let ca_start_time;
+129
+130            match self.state.recovery_start_time {
+131                Some(t) => ca_start_time = t,
+132                None => {
+133                    // When we come here without congestion_event() triggered,
+134                    // initialize congestion_recovery_start_time, w_max and k.
+135                    ca_start_time = now;
+136                    self.state.recovery_start_time = Some(now);
+137
+138                    self.state.w_max = self.state.window as f64;
+139                    self.state.k = 0.0;
+140                }
+141            }
+142
+143            let t = now - ca_start_time;
+144
+145            // w_cubic(t + rtt)
+146            let w_cubic = self.state.w_cubic(t + rtt.get(), self.current_mtu);
+147
+148            // w_est(t)
+149            let w_est = self.state.w_est(t, rtt.get(), self.current_mtu);
+150
+151            let mut cubic_cwnd = self.state.window;
+152
+153            if w_cubic < w_est {
+154                // TCP friendly region.
+155                cubic_cwnd = cmp::max(cubic_cwnd, w_est as u64);
+156            } else if cubic_cwnd < w_cubic as u64 {
+157                // Concave region or convex region use same increment.
+158                let cubic_inc =
+159                    (w_cubic - cubic_cwnd as f64) / cubic_cwnd as f64 * self.current_mtu as f64;
+160
+161                cubic_cwnd += cubic_inc as u64;
+162            }
+163
+164            // Update the increment and increase cwnd by MSS.
+165            self.state.cwnd_inc += cubic_cwnd - self.state.window;
+166
+167            // cwnd_inc can be more than 1 MSS in the late stage of max probing.
+168            // however RFC9002 §7.3.3 (Congestion Avoidance) limits
+169            // the increase of cwnd to 1 max_datagram_size per cwnd acknowledged.
+170            if self.state.cwnd_inc >= self.current_mtu {
+171                self.state.window += self.current_mtu;
+172                self.state.cwnd_inc = 0;
+173            }
+174        }
+175    }
+176
+177    fn on_congestion_event(
+178        &mut self,
+179        now: Instant,
+180        sent: Instant,
+181        is_persistent_congestion: bool,
+182        is_ecn: bool,
+183        _lost_bytes: u64,
+184    ) {
+185        if self
+186            .state
+187            .recovery_start_time
+188            .map(|recovery_start_time| sent <= recovery_start_time)
+189            .unwrap_or(false)
+190        {
+191            return;
+192        }
+193
+194        // Save state in case this event ends up being spurious
+195        if !is_ecn {
+196            self.pre_congestion_state = Some(self.state.clone());
+197        }
+198
+199        self.state.recovery_start_time = Some(now);
+200
+201        // Fast convergence
+202        if (self.state.window as f64) < self.state.w_max {
+203            self.state.w_max = self.state.window as f64 * (1.0 + BETA_CUBIC) / 2.0;
+204        } else {
+205            self.state.w_max = self.state.window as f64;
+206        }
+207
+208        self.state.ssthresh = cmp::max(
+209            (self.state.w_max * BETA_CUBIC) as u64,
+210            self.minimum_window(),
+211        );
+212        self.state.window = self.state.ssthresh;
+213        self.state.k = self.state.cubic_k(self.current_mtu);
+214
+215        self.state.cwnd_inc = (self.state.cwnd_inc as f64 * BETA_CUBIC) as u64;
+216
+217        if is_persistent_congestion {
+218            self.state.recovery_start_time = None;
+219            self.state.w_max = self.state.window as f64;
+220
+221            // 4.7 Timeout - reduce ssthresh based on BETA_CUBIC
+222            self.state.ssthresh = cmp::max(
+223                (self.state.window as f64 * BETA_CUBIC) as u64,
+224                self.minimum_window(),
+225            );
+226
+227            self.state.cwnd_inc = 0;
+228
+229            self.state.window = self.minimum_window();
+230        }
+231    }
+232
+233    fn on_spurious_congestion_event(&mut self) {
+234        if let Some(prior_state) = self.pre_congestion_state.take()
+235            && self.state.window < prior_state.window
+236        {
+237            self.state = prior_state;
+238        }
+239    }
+240
+241    fn on_mtu_update(&mut self, new_mtu: u16) {
+242        self.current_mtu = new_mtu as u64;
+243        self.state.window = self.state.window.max(self.minimum_window());
+244    }
+245
+246    fn window(&self) -> u64 {
+247        self.state.window
+248    }
+249
+250    fn metrics(&self) -> super::ControllerMetrics {
+251        super::ControllerMetrics {
+252            congestion_window: self.window(),
+253            ssthresh: Some(self.state.ssthresh),
+254            pacing_rate: None,
+255        }
+256    }
+257
+258    fn clone_box(&self) -> Box<dyn Controller> {
+259        Box::new(self.clone())
+260    }
+261
+262    fn initial_window(&self) -> u64 {
+263        self.config.initial_window
+264    }
+265
+266    fn into_any(self: Box<Self>) -> Box<dyn Any> {
+267        self
+268    }
+269}
+270
+271/// Configuration for the `Cubic` congestion controller
+272#[derive(Debug, Clone)]
+273pub struct CubicConfig {
+274    initial_window: u64,
+275}
+276
+277impl CubicConfig {
+278    /// Default limit on the amount of outstanding data in bytes.
+279    ///
+280    /// Recommended value: `min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))`
+281    pub fn initial_window(&mut self, value: u64) -> &mut Self {
+282        self.initial_window = value;
+283        self
+284    }
+285}
+286
+287impl Default for CubicConfig {
+288    fn default() -> Self {
+289        Self {
+290            initial_window: 14720.clamp(2 * BASE_DATAGRAM_SIZE, 10 * BASE_DATAGRAM_SIZE),
+291        }
+292    }
+293}
+294
+295impl ControllerFactory for CubicConfig {
+296    fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller> {
+297        Box::new(Cubic::new(self, now, current_mtu))
+298    }
+299}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/congestion/new_reno.rs.html b/pr/509/docs/src/noq_proto/congestion/new_reno.rs.html new file mode 100644 index 000000000..3153b27f9 --- /dev/null +++ b/pr/509/docs/src/noq_proto/congestion/new_reno.rs.html @@ -0,0 +1,173 @@ +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        app_limited: bool,
+50        _rtt: &RttEstimator,
+51    ) {
+52        if app_limited || sent <= self.recovery_start_time {
+53            return;
+54        }
+55
+56        if self.window < self.ssthresh {
+57            // Slow start
+58            self.window += bytes;
+59
+60            if self.window >= self.ssthresh {
+61                // Exiting slow start
+62                // Initialize `bytes_acked` for congestion avoidance. The idea
+63                // here is that any bytes over `sshthresh` will already be counted
+64                // towards the congestion avoidance phase - independent of when
+65                // how close to `sshthresh` the `window` was when switching states,
+66                // and independent of datagram sizes.
+67                self.bytes_acked = self.window - self.ssthresh;
+68            }
+69        } else {
+70            // Congestion avoidance
+71            // This implementation uses the method which does not require
+72            // floating point math, which also increases the window by 1 datagram
+73            // for every round trip.
+74            // This mechanism is called Appropriate Byte Counting in
+75            // https://tools.ietf.org/html/rfc3465
+76            self.bytes_acked += bytes;
+77
+78            if self.bytes_acked >= self.window {
+79                self.bytes_acked -= self.window;
+80                self.window += self.current_mtu;
+81            }
+82        }
+83    }
+84
+85    fn on_congestion_event(
+86        &mut self,
+87        now: Instant,
+88        sent: Instant,
+89        is_persistent_congestion: bool,
+90        _is_ecn: bool,
+91        _lost_bytes: u64,
+92    ) {
+93        if sent <= self.recovery_start_time {
+94            return;
+95        }
+96
+97        self.recovery_start_time = now;
+98        self.window = (self.window as f32 * self.config.loss_reduction_factor) as u64;
+99        self.window = self.window.max(self.minimum_window());
+100        self.ssthresh = self.window;
+101
+102        if is_persistent_congestion {
+103            self.window = self.minimum_window();
+104        }
+105    }
+106
+107    fn on_mtu_update(&mut self, new_mtu: u16) {
+108        self.current_mtu = new_mtu as u64;
+109        self.window = self.window.max(self.minimum_window());
+110    }
+111
+112    fn window(&self) -> u64 {
+113        self.window
+114    }
+115
+116    fn metrics(&self) -> super::ControllerMetrics {
+117        super::ControllerMetrics {
+118            congestion_window: self.window(),
+119            ssthresh: Some(self.ssthresh),
+120            pacing_rate: None,
+121        }
+122    }
+123
+124    fn clone_box(&self) -> Box<dyn Controller> {
+125        Box::new(self.clone())
+126    }
+127
+128    fn initial_window(&self) -> u64 {
+129        self.config.initial_window
+130    }
+131
+132    fn into_any(self: Box<Self>) -> Box<dyn Any> {
+133        self
+134    }
+135}
+136
+137/// Configuration for the `NewReno` congestion controller
+138#[derive(Debug, Clone)]
+139pub struct NewRenoConfig {
+140    initial_window: u64,
+141    loss_reduction_factor: f32,
+142}
+143
+144impl NewRenoConfig {
+145    /// Default limit on the amount of outstanding data in bytes.
+146    ///
+147    /// Recommended value: `min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))`
+148    pub fn initial_window(&mut self, value: u64) -> &mut Self {
+149        self.initial_window = value;
+150        self
+151    }
+152
+153    /// Reduction in congestion window when a new loss event is detected.
+154    pub fn loss_reduction_factor(&mut self, value: f32) -> &mut Self {
+155        self.loss_reduction_factor = value;
+156        self
+157    }
+158}
+159
+160impl Default for NewRenoConfig {
+161    fn default() -> Self {
+162        Self {
+163            initial_window: 14720.clamp(2 * BASE_DATAGRAM_SIZE, 10 * BASE_DATAGRAM_SIZE),
+164            loss_reduction_factor: 0.5,
+165        }
+166    }
+167}
+168
+169impl ControllerFactory for NewRenoConfig {
+170    fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller> {
+171        Box::new(NewReno::new(self, now, current_mtu))
+172    }
+173}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/connection/ack_frequency.rs.html b/pr/509/docs/src/noq_proto/connection/ack_frequency.rs.html new file mode 100644 index 000000000..517f15560 --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/connection/assembler.rs.html b/pr/509/docs/src/noq_proto/connection/assembler.rs.html new file mode 100644 index 000000000..c91f8b57f --- /dev/null +++ b/pr/509/docs/src/noq_proto/connection/assembler.rs.html @@ -0,0 +1,845 @@ +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 test_strategy::{Arbitrary, proptest};
+680
+681    use super::*;
+682
+683    const MAX_OFFSET: u64 = 512;
+684    const MAX_LEN: usize = 64;
+685
+686    #[derive(Debug, Clone, Arbitrary)]
+687    enum Op {
+688        #[weight(10)]
+689        Insert {
+690            #[strategy(0..MAX_OFFSET)]
+691            offset: u64,
+692            #[strategy(1..MAX_LEN)]
+693            len: usize,
+694        },
+695        #[weight(10)]
+696        Read {
+697            #[strategy(1..MAX_LEN)]
+698            max_len: usize,
+699        },
+700        #[weight(1)]
+701        EnsureOrdering { ordered: bool },
+702        #[weight(1)]
+703        Defragment,
+704    }
+705
+706    /// Tracks the state of the assembler for verification
+707    struct RefState {
+708        received: Vec<bool>,
+709        returned: Vec<bool>,
+710        ordered: bool,
+711    }
+712
+713    fn set_range(bits: &mut [bool], start: u64, len: usize) {
+714        for i in start..(start + len as u64).min(bits.len() as u64) {
+715            bits[i as usize] = true;
+716        }
+717    }
+718
+719    impl RefState {
+720        fn new() -> Self {
+721            Self {
+722                received: vec![false; MAX_OFFSET as usize],
+723                returned: vec![false; MAX_OFFSET as usize],
+724                ordered: true,
+725            }
+726        }
+727
+728        fn insert(&mut self, offset: u64, len: usize) {
+729            set_range(&mut self.received, offset, len);
+730        }
+731
+732        fn ensure_ordering(&mut self, ordered: bool) -> bool {
+733            if ordered && !self.ordered {
+734                return false;
+735            }
+736            self.ordered = ordered;
+737            true
+738        }
+739
+740        fn bytes_read(&self) -> u64 {
+741            self.returned.iter().filter(|&&x| x).count() as u64
+742        }
+743    }
+744
+745    fn make_data() -> Vec<u8> {
+746        use rand::{Rng, SeedableRng};
+747        let mut rng = rand::rngs::StdRng::seed_from_u64(0xDEADBEEF);
+748        let mut data = vec![0u8; MAX_OFFSET as usize];
+749        rng.fill(data.as_mut_slice());
+750        data
+751    }
+752
+753    fn get_slice(data: &[u8], offset: u64, len: usize) -> Bytes {
+754        let start = offset as usize;
+755        let end = (start + len).min(data.len());
+756        Bytes::copy_from_slice(&data[start..end])
+757    }
+758
+759    fn verify_chunk(data: &[u8], chunk: &Chunk) -> bool {
+760        let start = chunk.offset as usize;
+761        chunk.bytes[..] == data[start..start + chunk.bytes.len()]
+762    }
+763
+764    #[proptest]
+765    fn assembler_matches_reference(
+766        #[strategy(proptest::collection::vec(any::<Op>(), 1..100))] ops: Vec<Op>,
+767    ) {
+768        let data = make_data();
+769        let mut asm = Assembler::new();
+770        let mut reference = RefState::new();
+771
+772        for op in ops {
+773            match op {
+774                Op::Insert { offset, len } => {
+775                    let bytes = get_slice(&data, offset, len);
+776                    asm.insert(offset, bytes, len);
+777                    reference.insert(offset, len);
+778                }
+779                Op::Read { max_len } => {
+780                    let ordered = reference.ordered;
+781                    let actual = asm.read(max_len, ordered);
+782
+783                    match actual {
+784                        None => {
+785                            // Should only be None if no unreturned received bytes available
+786                            let has_available = if ordered {
+787                                // In ordered mode, check if the first unreturned byte is received
+788                                reference
+789                                    .returned
+790                                    .iter()
+791                                    .position(|&x| !x)
+792                                    .is_some_and(|pos| reference.received[pos])
+793                            } else {
+794                                // In unordered mode, check if any unreturned received byte exists
+795                                reference
+796                                    .received
+797                                    .iter()
+798                                    .zip(&reference.returned)
+799                                    .any(|(&r, &ret)| r && !ret)
+800                            };
+801                            prop_assert!(
+802                                !has_available,
+803                                "read returned None but data was available"
+804                            );
+805                        }
+806                        Some(chunk) => {
+807                            prop_assert!(chunk.bytes.len() <= max_len, "chunk exceeds max_len");
+808                            prop_assert!(verify_chunk(&data, &chunk), "data corruption");
+809                            // Mark as returned, check for duplicates
+810                            for i in 0..chunk.bytes.len() {
+811                                let offset = chunk.offset as usize + i;
+812                                prop_assert!(
+813                                    reference.received[offset],
+814                                    "returned unreceived byte at {offset}"
+815                                );
+816                                prop_assert!(
+817                                    !reference.returned[offset],
+818                                    "duplicate byte at {offset}"
+819                                );
+820                                reference.returned[offset] = true;
+821                            }
+822                        }
+823                    }
+824                }
+825                Op::EnsureOrdering { ordered } => {
+826                    let actual = asm.ensure_ordering(ordered).is_ok();
+827                    let expected = reference.ensure_ordering(ordered);
+828                    prop_assert_eq!(actual, expected, "ensure_ordering result mismatch");
+829                }
+830                Op::Defragment => {
+831                    if asm.state.is_ordered() {
+832                        asm.defragment();
+833                    }
+834                }
+835            }
+836        }
+837
+838        // Invariant: bytes_read matches
+839        prop_assert_eq!(
+840            asm.bytes_read(),
+841            reference.bytes_read(),
+842            "bytes_read mismatch"
+843        );
+844    }
+845}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/connection/cid_state.rs.html b/pr/509/docs/src/noq_proto/connection/cid_state.rs.html new file mode 100644 index 000000000..ea127fbbc --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/connection/datagrams.rs.html b/pr/509/docs/src/noq_proto/connection/datagrams.rs.html new file mode 100644 index 000000000..74515ad08 --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/connection/mod.rs.html b/pr/509/docs/src/noq_proto/connection/mod.rs.html new file mode 100644 index 000000000..838c48894 --- /dev/null +++ b/pr/509/docs/src/noq_proto/connection/mod.rs.html @@ -0,0 +1,7410 @@ +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::{IpAddr, SocketAddr},
+7    num::{NonZeroU32, NonZeroUsize},
+8    sync::Arc,
+9};
+10
+11use bytes::{Bytes, BytesMut};
+12use frame::StreamMetaVec;
+13
+14use rand::{Rng, 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        timer::{ConnTimer, PathTimer},
+31    },
+32    crypto::{self, Keys},
+33    frame::{
+34        self, Close, DataBlocked, Datagram, FrameStruct, NewToken, ObservedAddr, StreamDataBlocked,
+35        StreamsBlocked,
+36    },
+37    n0_nat_traversal,
+38    packet::{
+39        FixedLengthConnectionIdParser, Header, InitialHeader, InitialPacket, LongType, Packet,
+40        PacketNumber, PartialDecode, SpaceId,
+41    },
+42    range_set::ArrayRangeSet,
+43    shared::{
+44        ConnectionEvent, ConnectionEventInner, ConnectionId, DatagramConnectionEvent, EcnCodepoint,
+45        EndpointEvent, EndpointEventInner,
+46    },
+47    token::{ResetToken, Token, TokenPayload},
+48    transport_parameters::TransportParameters,
+49};
+50
+51mod ack_frequency;
+52use ack_frequency::AckFrequencyState;
+53
+54mod assembler;
+55pub use assembler::Chunk;
+56
+57mod cid_state;
+58use cid_state::CidState;
+59
+60mod datagrams;
+61use datagrams::DatagramState;
+62pub use datagrams::{Datagrams, SendDatagramError};
+63
+64mod mtud;
+65mod pacing;
+66
+67mod packet_builder;
+68use packet_builder::{PacketBuilder, PadDatagram};
+69
+70mod packet_crypto;
+71use packet_crypto::CryptoState;
+72pub(crate) use packet_crypto::EncryptionLevel;
+73
+74mod paths;
+75pub use paths::{
+76    ClosedPath, PathAbandonReason, PathEvent, PathId, PathStatus, RttEstimator, SetPathStatusError,
+77};
+78use paths::{PathData, PathState};
+79
+80pub(crate) mod qlog;
+81pub(crate) mod send_buffer;
+82
+83mod spaces;
+84#[cfg(fuzzing)]
+85pub use spaces::Retransmits;
+86#[cfg(not(fuzzing))]
+87use spaces::Retransmits;
+88pub(crate) use spaces::SpaceKind;
+89use spaces::{PacketSpace, SendableFrames, SentPacket, ThinRetransmits};
+90
+91mod stats;
+92pub use stats::{ConnectionStats, FrameStats, PathStats, UdpStats};
+93
+94mod streams;
+95#[cfg(fuzzing)]
+96pub use streams::StreamsState;
+97#[cfg(not(fuzzing))]
+98use streams::StreamsState;
+99pub use streams::{
+100    Chunks, ClosedStream, FinishError, ReadError, ReadableError, RecvStream, SendStream,
+101    ShouldTransmit, StreamEvent, Streams, WriteError, Written,
+102};
+103
+104mod timer;
+105use timer::{Timer, TimerTable};
+106
+107mod transmit_buf;
+108use transmit_buf::TransmitBuf;
+109
+110mod state;
+111
+112#[cfg(not(fuzzing))]
+113use state::State;
+114#[cfg(fuzzing)]
+115pub use state::State;
+116use state::StateType;
+117
+118/// Protocol state and logic for a single QUIC connection
+119///
+120/// Objects of this type receive [`ConnectionEvent`]s and emit [`EndpointEvent`]s and application
+121/// [`Event`]s to make progress. To handle timeouts, a `Connection` returns timer updates and
+122/// expects timeouts through various methods. A number of simple getter methods are exposed
+123/// to allow callers to inspect some of the connection state.
+124///
+125/// `Connection` has roughly 4 types of methods:
+126///
+127/// - A. Simple getters, taking `&self`
+128/// - B. Handlers for incoming events from the network or system, named `handle_*`.
+129/// - C. State machine mutators, for incoming commands from the application. For convenience we
+130///   refer to this as "performing I/O" below, however as per the design of this library none of the
+131///   functions actually perform system-level I/O. For example, [`read`](RecvStream::read) and
+132///   [`write`](SendStream::write), but also things like [`reset`](SendStream::reset).
+133/// - D. Polling functions for outgoing events or actions for the caller to
+134///   take, named `poll_*`.
+135///
+136/// The simplest way to use this API correctly is to call (B) and (C) whenever
+137/// appropriate, then after each of those calls, as soon as feasible call all
+138/// polling methods (D) and deal with their outputs appropriately, e.g. by
+139/// passing it to the application or by making a system-level I/O call. You
+140/// should call the polling functions in this order:
+141///
+142/// 1. [`poll_transmit`](Self::poll_transmit)
+143/// 2. [`poll_timeout`](Self::poll_timeout)
+144/// 3. [`poll_endpoint_events`](Self::poll_endpoint_events)
+145/// 4. [`poll`](Self::poll)
+146///
+147/// Currently the only actual dependency is from (2) to (1), however additional
+148/// dependencies may be added in future, so the above order is recommended.
+149///
+150/// (A) may be called whenever desired.
+151///
+152/// Care should be made to ensure that the input events represent monotonically
+153/// increasing time. Specifically, calling [`handle_timeout`](Self::handle_timeout)
+154/// with events of the same [`Instant`] may be interleaved in any order with a
+155/// call to [`handle_event`](Self::handle_event) at that same instant; however
+156/// events or timeouts with different instants must not be interleaved.
+157pub struct Connection {
+158    endpoint_config: Arc<EndpointConfig>,
+159    config: Arc<TransportConfig>,
+160    rng: StdRng,
+161    /// Consolidated cryptographic state
+162    crypto_state: CryptoState,
+163    /// The CID we initially chose, for use during the handshake
+164    handshake_cid: ConnectionId,
+165    /// The CID the peer initially chose, for use during the handshake
+166    remote_handshake_cid: ConnectionId,
+167    /// The [`PathData`] for each path
+168    ///
+169    /// This needs to be ordered because [`Connection::poll_transmit`] needs to
+170    /// deterministically select the next PathId to send on.
+171    // TODO(flub): well does it really? But deterministic is nice for now.
+172    paths: BTreeMap<PathId, PathState>,
+173    /// Counter to uniquely identify every [`PathData`] created in this connection.
+174    ///
+175    /// Each [`PathData`] gets a [`PathData::generation`] that is unique among all
+176    /// [`PathData`]s created in the lifetime of this connection. This helps identify the
+177    /// correct path when RFC9000-style migrations happen, even when they are
+178    /// aborted.
+179    ///
+180    /// Multipath does not change this, each path can also undergo RFC9000-style
+181    /// migrations. So a single multipath path ID could see several [`PathData`]s each with
+182    /// their unique [`PathData::generation].
+183    path_generation_counter: u64,
+184    /// Whether MTU detection is supported in this environment
+185    allow_mtud: bool,
+186    state: State,
+187    side: ConnectionSide,
+188    /// Transport parameters set by the peer
+189    peer_params: TransportParameters,
+190    /// Source ConnectionId of the first packet received from the peer
+191    original_remote_cid: ConnectionId,
+192    /// Destination ConnectionId sent by the client on the first Initial
+193    initial_dst_cid: ConnectionId,
+194    /// The value that the server included in the Source Connection ID field of a Retry packet, if
+195    /// one was received
+196    retry_src_cid: Option<ConnectionId>,
+197    /// Events returned by [`Connection::poll`]
+198    events: VecDeque<Event>,
+199    endpoint_events: VecDeque<EndpointEventInner>,
+200    /// Whether the spin bit is in use for this connection
+201    spin_enabled: bool,
+202    /// Outgoing spin bit state
+203    spin: bool,
+204    /// Packet number spaces: initial, handshake, 1-RTT
+205    spaces: [PacketSpace; 3],
+206    /// Highest usable packet space.
+207    highest_space: SpaceKind,
+208    /// Whether the idle timer should be reset the next time an ack-eliciting packet is transmitted.
+209    permit_idle_reset: bool,
+210    /// Negotiated idle timeout
+211    idle_timeout: Option<Duration>,
+212    timers: TimerTable,
+213    /// Number of packets received which could not be authenticated
+214    authentication_failures: u64,
+215
+216    //
+217    // Queued non-retransmittable 1-RTT data
+218    //
+219    /// If the CONNECTION_CLOSE frame needs to be sent
+220    connection_close_pending: bool,
+221
+222    //
+223    // ACK frequency
+224    //
+225    ack_frequency: AckFrequencyState,
+226
+227    //
+228    // Congestion Control
+229    //
+230    /// Whether the most recently received packet had an ECN codepoint set
+231    receiving_ecn: bool,
+232    /// Number of packets authenticated
+233    total_authed_packets: u64,
+234    /// Whether the last `poll_transmit` call yielded no data because there was
+235    /// no outgoing application data.
+236    app_limited: bool,
+237
+238    //
+239    // ObservedAddr
+240    //
+241    /// Sequence number for the next observed address frame sent to the peer.
+242    next_observed_addr_seq_no: VarInt,
+243
+244    streams: StreamsState,
+245    /// Active and surplus CIDs issued by the remote, for future use on new paths.
+246    ///
+247    /// These are given out before multiple paths exist, also for paths that will never
+248    /// exist.  So if multipath is supported the number of paths here will be higher than
+249    /// the actual number of paths in use.
+250    remote_cids: FxHashMap<PathId, CidQueue>,
+251    /// Attributes of CIDs generated by local endpoint
+252    ///
+253    /// Any path that is allowed to be opened is present in this map, as well as the already
+254    /// opened paths. However since CIDs are issued async by the endpoint driver via
+255    /// connection events it can not be used to know if CIDs have been issued for a path or
+256    /// not. See [`Connection::max_path_id_with_cids`] for this.
+257    local_cid_state: FxHashMap<PathId, CidState>,
+258    /// State of the unreliable datagram extension
+259    datagrams: DatagramState,
+260    /// Connection level statistics
+261    stats: ConnectionStats,
+262    /// Path level statistics
+263    path_stats: FxHashMap<PathId, PathStats>,
+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            permit_idle_reset: true,
+394            idle_timeout: match config.max_idle_timeout {
+395                None | Some(VarInt(0)) => None,
+396                Some(dur) => Some(Duration::from_millis(dur.0)),
+397            },
+398            timers: TimerTable::default(),
+399            authentication_failures: 0,
+400            connection_close_pending: false,
+401
+402            ack_frequency: AckFrequencyState::new(get_max_ack_delay(
+403                &TransportParameters::default(),
+404            )),
+405
+406            app_limited: false,
+407            receiving_ecn: false,
+408            total_authed_packets: 0,
+409
+410            next_observed_addr_seq_no: 0u32.into(),
+411
+412            streams: StreamsState::new(
+413                side,
+414                config.max_concurrent_uni_streams,
+415                config.max_concurrent_bidi_streams,
+416                config.send_window,
+417                config.receive_window,
+418                config.stream_receive_window,
+419            ),
+420            datagrams: DatagramState::default(),
+421            config,
+422            remote_cids: FxHashMap::from_iter([(PathId::ZERO, CidQueue::new(remote_cid))]),
+423            rng,
+424            stats: ConnectionStats::default(),
+425            path_stats: Default::default(),
+426            version,
+427
+428            // peer params are not yet known, so multipath is not enabled
+429            max_concurrent_paths: NonZeroU32::MIN,
+430            local_max_path_id: PathId::ZERO,
+431            remote_max_path_id: PathId::ZERO,
+432            max_path_id_with_cids: PathId::ZERO,
+433            abandoned_paths: Default::default(),
+434
+435            n0_nat_traversal: Default::default(),
+436            qlog,
+437        };
+438        if path_validated {
+439            this.on_path_validated(PathId::ZERO);
+440        }
+441        if side.is_client() {
+442            // Kick off the connection
+443            this.write_crypto();
+444            this.init_0rtt(now);
+445        }
+446        this.qlog
+447            .emit_tuple_assigned(PathId::ZERO, network_path, now);
+448        this
+449    }
+450
+451    /// Returns the next time at which `handle_timeout` should be called
+452    ///
+453    /// The value returned may change after:
+454    /// - the application performed some I/O on the connection
+455    /// - a call was made to `handle_event`
+456    /// - a call to `poll_transmit` returned `Some`
+457    /// - a call was made to `handle_timeout`
+458    #[must_use]
+459    pub fn poll_timeout(&mut self) -> Option<Instant> {
+460        self.timers.peek()
+461    }
+462
+463    /// Returns application-facing events
+464    ///
+465    /// Connections should be polled for events after:
+466    /// - a call was made to `handle_event`
+467    /// - a call was made to `handle_timeout`
+468    #[must_use]
+469    pub fn poll(&mut self) -> Option<Event> {
+470        if let Some(x) = self.events.pop_front() {
+471            return Some(x);
+472        }
+473
+474        if let Some(event) = self.streams.poll() {
+475            return Some(Event::Stream(event));
+476        }
+477
+478        if let Some(reason) = self.state.take_error() {
+479            return Some(Event::ConnectionLost { reason });
+480        }
+481
+482        None
+483    }
+484
+485    /// Return endpoint-facing events
+486    #[must_use]
+487    pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent> {
+488        self.endpoint_events.pop_front().map(EndpointEvent)
+489    }
+490
+491    /// Provide control over streams
+492    #[must_use]
+493    pub fn streams(&mut self) -> Streams<'_> {
+494        Streams {
+495            state: &mut self.streams,
+496            conn_state: &self.state,
+497        }
+498    }
+499
+500    /// Provide control over streams
+501    #[must_use]
+502    pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_> {
+503        assert!(id.dir() == Dir::Bi || id.initiator() != self.side.side());
+504        RecvStream {
+505            id,
+506            state: &mut self.streams,
+507            pending: &mut self.spaces[SpaceId::Data].pending,
+508        }
+509    }
+510
+511    /// Provide control over streams
+512    #[must_use]
+513    pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_> {
+514        assert!(id.dir() == Dir::Bi || id.initiator() == self.side.side());
+515        SendStream {
+516            id,
+517            state: &mut self.streams,
+518            pending: &mut self.spaces[SpaceId::Data].pending,
+519            conn_state: &self.state,
+520        }
+521    }
+522
+523    /// Opens a new path only if no path on the same network path currently exists.
+524    ///
+525    /// This comparison will use [`FourTuple::is_probably_same_path`] on the given `network_path`
+526    /// and pass it existing path's network paths.
+527    ///
+528    /// This means that you can pass `local_ip: None` to make the comparison only compare
+529    /// remote addresses.
+530    ///
+531    /// This avoids having to guess which local interface will be used to communicate with the
+532    /// remote, should it not be known yet. We assume that if we already have a path to the remote,
+533    /// the OS is likely to use the same interface to talk to said remote.
+534    ///
+535    /// See also [`open_path`]. Returns `(path_id, true)` if the path already existed. `(path_id,
+536    /// false)` if was opened.
+537    ///
+538    /// [`open_path`]: Connection::open_path
+539    pub fn open_path_ensure(
+540        &mut self,
+541        network_path: FourTuple,
+542        initial_status: PathStatus,
+543        now: Instant,
+544    ) -> Result<(PathId, bool), PathError> {
+545        let existing_open_path = self.paths.iter().find(|(id, path)| {
+546            network_path.is_probably_same_path(&path.data.network_path)
+547                && !self.abandoned_paths.contains(*id)
+548        });
+549        match existing_open_path {
+550            Some((path_id, _state)) => Ok((*path_id, true)),
+551            None => Ok((self.open_path(network_path, initial_status, now)?, false)),
+552        }
+553    }
+554
+555    /// Opens a new path
+556    ///
+557    /// Further errors might occur and they will be emitted in [`PathEvent::Abandoned`] events with this path id.
+558    /// When the path is opened it will be reported as an [`PathEvent::Opened`].
+559    pub fn open_path(
+560        &mut self,
+561        network_path: FourTuple,
+562        initial_status: PathStatus,
+563        now: Instant,
+564    ) -> Result<PathId, PathError> {
+565        if !self.is_multipath_negotiated() {
+566            return Err(PathError::MultipathNotNegotiated);
+567        }
+568        if self.side().is_server() {
+569            return Err(PathError::ServerSideNotAllowed);
+570        }
+571
+572        let max_abandoned = self.abandoned_paths.iter().max().copied();
+573        let max_used = self.paths.keys().last().copied();
+574        let path_id = max_abandoned
+575            .max(max_used)
+576            .unwrap_or(PathId::ZERO)
+577            .saturating_add(1u8);
+578
+579        if Some(path_id) > self.max_path_id() {
+580            return Err(PathError::MaxPathIdReached);
+581        }
+582        if path_id > self.remote_max_path_id {
+583            self.spaces[SpaceId::Data].pending.paths_blocked = true;
+584            return Err(PathError::MaxPathIdReached);
+585        }
+586        if self
+587            .remote_cids
+588            .get(&path_id)
+589            .map(CidQueue::active)
+590            .is_none()
+591        {
+592            self.spaces[SpaceId::Data]
+593                .pending
+594                .path_cids_blocked
+595                .insert(path_id);
+596            return Err(PathError::RemoteCidsExhausted);
+597        }
+598
+599        let path = self.ensure_path(path_id, network_path, now, None);
+600        path.status.local_update(initial_status);
+601
+602        Ok(path_id)
+603    }
+604
+605    /// Closes a path and sends a PATH_ABANDON frame with the passed error code.
+606    ///
+607    /// This will not allow closing the last path. It does allow closing paths which have
+608    /// not yet been opened, as e.g. is the case when receiving a PATH_ABANDON from the peer
+609    /// for a path that was never opened locally.
+610    pub fn close_path(
+611        &mut self,
+612        now: Instant,
+613        path_id: PathId,
+614        error_code: VarInt,
+615    ) -> Result<(), ClosePathError> {
+616        self.close_path_inner(
+617            now,
+618            path_id,
+619            PathAbandonReason::ApplicationClosed { error_code },
+620        )
+621    }
+622
+623    /// Closes a path and sends a PATH_ABANDON frame.
+624    ///
+625    /// Other than [`Self::close_path`] this allows to specify the reason for the path being closed.
+626    /// Internally, this should be used over [`Self::close_path`].
+627    fn close_path_inner(
+628        &mut self,
+629        now: Instant,
+630        path_id: PathId,
+631        reason: PathAbandonReason,
+632    ) -> Result<(), ClosePathError> {
+633        if self.state.is_drained() {
+634            return Ok(());
+635        }
+636
+637        if !self.is_multipath_negotiated() {
+638            return Err(ClosePathError::MultipathNotNegotiated);
+639        }
+640        if self.abandoned_paths.contains(&path_id)
+641            || Some(path_id) > self.max_path_id()
+642            || !self.paths.contains_key(&path_id)
+643        {
+644            return Err(ClosePathError::ClosedPath);
+645        }
+646
+647        if reason.is_locally_initiated() {
+648            let has_remaining_validated_paths = self.paths.iter().any(|(id, path)| {
+649                *id != path_id && !self.abandoned_paths.contains(id) && path.data.validated
+650            });
+651            if !has_remaining_validated_paths {
+652                return Err(ClosePathError::LastOpenPath);
+653            }
+654        } else {
+655            // The remote abandoned this path. We should always "accept" this. Doing so right now,
+656            // however, breaks assumptions throughout the code. We error instead, for the
+657            // connection to be killed. See <https://github.com/n0-computer/noq/issues/397>
+658            let has_remaining_paths = self
+659                .paths
+660                .keys()
+661                .any(|id| *id != path_id && !self.abandoned_paths.contains(id));
+662            if !has_remaining_paths {
+663                return Err(ClosePathError::LastOpenPath);
+664            }
+665        }
+666
+667        trace!(%path_id, ?reason, "closing path");
+668
+669        // Send PATH_ABANDON
+670        self.spaces[SpaceId::Data]
+671            .pending
+672            .path_abandon
+673            .insert(path_id, reason.error_code());
+674
+675        // Remove pending NEW CIDs for this path
+676        let pending_space = &mut self.spaces[SpaceId::Data].pending;
+677        pending_space.new_cids.retain(|cid| cid.path_id != path_id);
+678        pending_space.path_cids_blocked.retain(|&id| id != path_id);
+679        pending_space.path_status.retain(|&id| id != path_id);
+680
+681        // Cleanup retransmits across ALL paths (CIDs for path_id may have been transmitted on other paths)
+682        for space in self.spaces[SpaceId::Data].iter_paths_mut() {
+683            for sent_packet in space.sent_packets.values_mut() {
+684                if let Some(retransmits) = sent_packet.retransmits.get_mut() {
+685                    retransmits.new_cids.retain(|cid| cid.path_id != path_id);
+686                    retransmits.path_cids_blocked.retain(|&id| id != path_id);
+687                    retransmits.path_status.retain(|&id| id != path_id);
+688                }
+689            }
+690        }
+691
+692        debug_assert!(!self.state.is_drained()); // requirement for endpoint_events, checked above
+693        self.endpoint_events
+694            .push_back(EndpointEventInner::RetireResetToken(path_id));
+695
+696        trace!(%path_id, "abandoning path");
+697        self.abandoned_paths.insert(path_id);
+698
+699        for timer in timer::PathTimer::VALUES {
+700            // match for completeness
+701            let keep_timer = match timer {
+702                // These timers deal with sending and receiving PATH_CHALLENGE and
+703                // PATH_RESPONSE, but now that the path is abandoned, we no longer care about
+704                // these frames or their timing
+705                PathTimer::PathValidation | PathTimer::PathChallengeLost | PathTimer::PathOpen => {
+706                    false
+707                }
+708                // These timers deal with the lifetime of the path. Now that the path is abandoned,
+709                // these are not relevant.
+710                PathTimer::PathKeepAlive | PathTimer::PathIdle => false,
+711                // The path has already been informed that outstanding acks should be sent
+712                // immediately
+713                PathTimer::MaxAckDelay => false,
+714                // This timer should not be set, for completeness it's not kept as it's set when
+715                // the PATH_ABANDON frame is sent.
+716                PathTimer::DiscardPath => false,
+717                // Sent packets still need to be identified as lost to trigger timely
+718                // retransmission.
+719                PathTimer::LossDetection => true,
+720                // This path should not be used for sending after the PATH_ABANDON frame is sent.
+721                // However, any outstanding data that should be sent before PATH_ABANDON, should
+722                // still respect pacing.
+723                PathTimer::Pacing => true,
+724            };
+725
+726            if !keep_timer {
+727                let qlog = self.qlog.with_time(now);
+728                self.timers.stop(Timer::PerPath(path_id, timer), qlog);
+729            }
+730        }
+731
+732        // Emit event to the application.
+733        self.events.push_back(Event::Path(PathEvent::Abandoned {
+734            id: path_id,
+735            reason,
+736        }));
+737
+738        Ok(())
+739    }
+740
+741    /// Gets the [`PathData`] for a known [`PathId`].
+742    ///
+743    /// Will panic if the path_id does not reference any known path.
+744    #[track_caller]
+745    fn path_data(&self, path_id: PathId) -> &PathData {
+746        if let Some(data) = self.paths.get(&path_id) {
+747            &data.data
+748        } else {
+749            panic!(
+750                "unknown path: {path_id}, currently known paths: {:?}",
+751                self.paths.keys().collect::<Vec<_>>()
+752            );
+753        }
+754    }
+755
+756    /// Gets a reference to the [`PathData`] for a [`PathId`]
+757    fn path(&self, path_id: PathId) -> Option<&PathData> {
+758        self.paths.get(&path_id).map(|path_state| &path_state.data)
+759    }
+760
+761    /// Gets a mutable reference to the [`PathData`] for a [`PathId`]
+762    fn path_mut(&mut self, path_id: PathId) -> Option<&mut PathData> {
+763        self.paths
+764            .get_mut(&path_id)
+765            .map(|path_state| &mut path_state.data)
+766    }
+767
+768    /// Returns all known paths.
+769    ///
+770    /// There is no guarantee any of these paths are open or usable.
+771    pub fn paths(&self) -> Vec<PathId> {
+772        self.paths.keys().copied().collect()
+773    }
+774
+775    /// Gets the local [`PathStatus`] for a known [`PathId`]
+776    pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath> {
+777        self.path(path_id)
+778            .map(PathData::local_status)
+779            .ok_or(ClosedPath { _private: () })
+780    }
+781
+782    /// Returns the path's network path represented as a 4-tuple.
+783    pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath> {
+784        self.path(path_id)
+785            .map(|path| path.network_path)
+786            .ok_or(ClosedPath { _private: () })
+787    }
+788
+789    /// Sets the [`PathStatus`] for a known [`PathId`]
+790    ///
+791    /// Returns the previous path status on success.
+792    pub fn set_path_status(
+793        &mut self,
+794        path_id: PathId,
+795        status: PathStatus,
+796    ) -> Result<PathStatus, SetPathStatusError> {
+797        if !self.is_multipath_negotiated() {
+798            return Err(SetPathStatusError::MultipathNotNegotiated);
+799        }
+800        let path = self
+801            .path_mut(path_id)
+802            .ok_or(SetPathStatusError::ClosedPath)?;
+803        let prev = match path.status.local_update(status) {
+804            Some(prev) => {
+805                self.spaces[SpaceId::Data]
+806                    .pending
+807                    .path_status
+808                    .insert(path_id);
+809                prev
+810            }
+811            None => path.local_status(),
+812        };
+813        Ok(prev)
+814    }
+815
+816    /// Returns the remote path status
+817    // TODO(flub): Probably should also be some kind of path event?  Not even sure if I like
+818    //    this as an API, but for now it allows me to write a test easily.
+819    // TODO(flub): Technically this should be a Result<Option<PathSTatus>>?
+820    pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus> {
+821        self.path(path_id).and_then(|path| path.remote_status())
+822    }
+823
+824    /// Sets the max_idle_timeout for a specific path
+825    ///
+826    /// See [`TransportConfig::default_path_max_idle_timeout`] for details.
+827    ///
+828    /// Returns the previous value of the setting.
+829    pub fn set_path_max_idle_timeout(
+830        &mut self,
+831        path_id: PathId,
+832        timeout: Option<Duration>,
+833    ) -> Result<Option<Duration>, ClosedPath> {
+834        let path = self
+835            .paths
+836            .get_mut(&path_id)
+837            .ok_or(ClosedPath { _private: () })?;
+838        Ok(std::mem::replace(&mut path.data.idle_timeout, timeout))
+839    }
+840
+841    /// Sets the keep_alive_interval for a specific path
+842    ///
+843    /// See [`TransportConfig::default_path_keep_alive_interval`] for details.
+844    ///
+845    /// Returns the previous value of the setting.
+846    pub fn set_path_keep_alive_interval(
+847        &mut self,
+848        path_id: PathId,
+849        interval: Option<Duration>,
+850    ) -> Result<Option<Duration>, ClosedPath> {
+851        let path = self
+852            .paths
+853            .get_mut(&path_id)
+854            .ok_or(ClosedPath { _private: () })?;
+855        Ok(std::mem::replace(&mut path.data.keep_alive, interval))
+856    }
+857
+858    /// Gets the [`PathData`] for a known [`PathId`].
+859    ///
+860    /// Will panic if the path_id does not reference any known path.
+861    #[track_caller]
+862    fn path_data_mut(&mut self, path_id: PathId) -> &mut PathData {
+863        &mut self.paths.get_mut(&path_id).expect("known path").data
+864    }
+865
+866    /// Find an open, validated path that's on the same network path as the given network path.
+867    ///
+868    /// Returns the first path matching, even if there's multiple.
+869    fn find_validated_path_on_network_path(
+870        &self,
+871        network_path: FourTuple,
+872    ) -> Option<(&PathId, &PathState)> {
+873        self.paths.iter().find(|(path_id, path_state)| {
+874            path_state.data.validated
+875                // Would this use the same network path, if network_path were used to send right now?
+876                && network_path.is_probably_same_path(&path_state.data.network_path)
+877                && !self.abandoned_paths.contains(path_id)
+878        })
+879        // TODO(@divma): we might want to ensure the path has been recently active to consider the
+880        // address validated
+881        // matheus23: Perhaps looking at !self.abandoned_paths.contains(path_id) is enough, given keep-alives?
+882    }
+883
+884    /// Creates the [`PathData`] for a new [`PathId`].
+885    ///
+886    /// Called for incoming packets as well as when opening a new path locally.
+887    fn ensure_path(
+888        &mut self,
+889        path_id: PathId,
+890        network_path: FourTuple,
+891        now: Instant,
+892        pn: Option<u64>,
+893    ) -> &mut PathData {
+894        let valid_path = self.find_validated_path_on_network_path(network_path);
+895        let validated = valid_path.is_some();
+896        let initial_rtt = valid_path.map(|(_, path)| path.data.rtt.conservative());
+897        let vacant_entry = match self.paths.entry(path_id) {
+898            btree_map::Entry::Vacant(vacant_entry) => vacant_entry,
+899            btree_map::Entry::Occupied(occupied_entry) => {
+900                return &mut occupied_entry.into_mut().data;
+901            }
+902        };
+903
+904        debug!(%validated, %path_id, %network_path, "path added");
+905        let peer_max_udp_payload_size =
+906            u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX);
+907        self.path_generation_counter = self.path_generation_counter.wrapping_add(1);
+908        let mut data = PathData::new(
+909            network_path,
+910            self.allow_mtud,
+911            Some(peer_max_udp_payload_size),
+912            self.path_generation_counter,
+913            now,
+914            &self.config,
+915        );
+916
+917        data.validated = validated;
+918        if let Some(initial_rtt) = initial_rtt {
+919            data.rtt.reset_initial_rtt(initial_rtt);
+920        }
+921
+922        // To open a path locally we need to send a packet on the path. Sending a challenge
+923        // guarantees this.
+924        data.pending_on_path_challenge = true;
+925
+926        let path = vacant_entry.insert(PathState { data, prev: None });
+927
+928        let mut pn_space = spaces::PacketNumberSpace::new(now, SpaceId::Data, &mut self.rng);
+929        if let Some(pn) = pn {
+930            pn_space.dedup.insert(pn);
+931        }
+932        self.spaces[SpaceId::Data]
+933            .number_spaces
+934            .insert(path_id, pn_space);
+935        self.qlog.emit_tuple_assigned(path_id, network_path, now);
+936
+937        // If the remote opened this path we may not have CIDs for it. For locally opened
+938        // paths the caller should have already made sure we have CIDs and refused to open
+939        // it if there were none.
+940        if !self.remote_cids.contains_key(&path_id) {
+941            debug!(%path_id, "Remote opened path without issuing CIDs");
+942            self.spaces[SpaceId::Data]
+943                .pending
+944                .path_cids_blocked
+945                .insert(path_id);
+946            // Do not abandon this path right away. CIDs might be in-flight still and arrive
+947            // soon. It is up to the remote to handle this situation.
+948        }
+949
+950        &mut path.data
+951    }
+952
+953    /// Returns packets to transmit
+954    ///
+955    /// Connections should be polled for transmit after:
+956    /// - the application performed some I/O on the connection
+957    /// - a call was made to `handle_event`
+958    /// - a call was made to `handle_timeout`
+959    ///
+960    /// `max_datagrams` specifies how many datagrams can be returned inside a
+961    /// single Transmit using GSO. This must be at least 1.
+962    #[must_use]
+963    pub fn poll_transmit(
+964        &mut self,
+965        now: Instant,
+966        max_datagrams: NonZeroUsize,
+967        buf: &mut Vec<u8>,
+968    ) -> Option<Transmit> {
+969        let max_datagrams = match self.config.enable_segmentation_offload {
+970            false => NonZeroUsize::MIN,
+971            true => max_datagrams,
+972        };
+973
+974        // Each call to poll_transmit can only send datagrams to one destination, because
+975        // all datagrams in a GSO batch are for the same destination.  Therefore only
+976        // datagrams for one destination address are produced for each poll_transmit call.
+977
+978        // Check whether we need to send a close message
+979        let connection_close_pending = match self.state.as_type() {
+980            StateType::Drained => {
+981                self.app_limited = true;
+982                return None;
+983            }
+984            StateType::Draining | StateType::Closed => {
+985                // self.connection_close_pending is only reset once the associated packet
+986                // had been encoded successfully
+987                if !self.connection_close_pending {
+988                    self.app_limited = true;
+989                    return None;
+990                }
+991                true
+992            }
+993            _ => false,
+994        };
+995
+996        // Schedule an ACK_FREQUENCY frame if a new one needs to be sent.
+997        if let Some(config) = &self.config.ack_frequency_config {
+998            let rtt = self
+999                .paths
+1000                .values()
+1001                .map(|p| p.data.rtt.get())
+1002                .min()
+1003                .expect("one path exists");
+1004            self.spaces[SpaceId::Data].pending.ack_frequency = self
+1005                .ack_frequency
+1006                .should_send_ack_frequency(rtt, config, &self.peer_params)
+1007                && self.highest_space == SpaceKind::Data
+1008                && self.peer_supports_ack_frequency();
+1009        }
+1010
+1011        // If we end up not sending anything, we need to know if that was because there was
+1012        // nothing to send or because we were congestion blocked.
+1013        let mut congestion_blocked = false;
+1014
+1015        let mut next_path_id = self.paths.first_entry().map(|e| *e.key());
+1016        while let Some(path_id) = next_path_id {
+1017            if !connection_close_pending
+1018                && let Some(transmit) = self.poll_transmit_off_path(now, buf, path_id)
+1019            {
+1020                return Some(transmit);
+1021            }
+1022
+1023            let info = self.scheduling_info(path_id);
+1024            match self.poll_transmit_on_path(
+1025                now,
+1026                buf,
+1027                path_id,
+1028                max_datagrams,
+1029                &info,
+1030                connection_close_pending,
+1031            ) {
+1032                PollPathStatus::Send(transmit) => {
+1033                    return Some(transmit);
+1034                }
+1035                PollPathStatus::NothingToSend {
+1036                    congestion_blocked: cb,
+1037                } => {
+1038                    congestion_blocked |= cb;
+1039                    // Continue checking other paths, tail-loss probes may need to be sent
+1040                    // in all spaces.
+1041                    debug_assert!(
+1042                        buf.is_empty(),
+1043                        "nothing to send on path but buffer not empty"
+1044                    );
+1045                }
+1046            }
+1047
+1048            next_path_id = self.paths.keys().find(|i| **i > path_id).copied();
+1049        }
+1050
+1051        // We didn't produce any application data packet
+1052        debug_assert!(
+1053            buf.is_empty(),
+1054            "there was data in the buffer, but it was not sent"
+1055        );
+1056
+1057        self.app_limited = !congestion_blocked;
+1058
+1059        if self.state.is_established() {
+1060            // Try MTU probing now
+1061            let mut next_path_id = self.paths.first_entry().map(|e| *e.key());
+1062            while let Some(path_id) = next_path_id {
+1063                if let Some(transmit) = self.poll_transmit_mtu_probe(now, buf, path_id) {
+1064                    return Some(transmit);
+1065                }
+1066                next_path_id = self.paths.keys().find(|i| **i > path_id).copied();
+1067            }
+1068        }
+1069
+1070        None
+1071    }
+1072
+1073    /// Computes the packet scheduling information for this path.
+1074    ///
+1075    /// While this information is only returned for a single path, it is important to know
+1076    /// that this information remains static for the entire span of a single
+1077    /// [`Connection::poll_transmit`] call. In other words, the return value is purely
+1078    /// functional and only depends on the [`PathId`] **during a single** `poll_transmit`
+1079    /// call. It can be computed up-front for all paths but we don't do that because it
+1080    /// involves an allocation.
+1081    ///
+1082    /// See the inline comments for how the  packet scheduling works.
+1083    ///
+1084    /// # Panics
+1085    ///
+1086    /// This will panic if called for a path for which we do not have any [`PathData`], like
+1087    /// so many other functions we have. But this is the only one to document this in its
+1088    /// doc comment. Maybe that should change. Eventually we'll refactor things for this
+1089    /// panic to go away.
+1090    fn scheduling_info(&self, path_id: PathId) -> PathSchedulingInfo {
+1091        // Such a space is preferred for SpaceKind::Data frames.
+1092        let have_validated_status_available_space = self.paths.iter().any(|(path_id, path)| {
+1093            self.remote_cids.contains_key(path_id)
+1094                && !self.abandoned_paths.contains(path_id)
+1095                && path.data.validated
+1096                && path.data.local_status() == PathStatus::Available
+1097        });
+1098
+1099        // Such a space is able to send SpaceKind::Data frames.
+1100        let have_validated_space = self.paths.iter().any(|(path_id, path)| {
+1101            self.remote_cids.contains_key(path_id)
+1102                && !self.abandoned_paths.contains(path_id)
+1103                && path.data.validated
+1104        });
+1105
+1106        let is_handshaking = self.is_handshaking();
+1107        let has_cids = self.remote_cids.contains_key(&path_id);
+1108        let abandoned = self.abandoned_paths.contains(&path_id);
+1109        let path_data = self.path_data(path_id);
+1110        let validated = path_data.validated;
+1111        let status = path_data.local_status();
+1112
+1113        // This is the core packet scheduling, whether this space ID may send
+1114        // SpaceKind::Data frames.
+1115        let may_send_data = has_cids
+1116            && !abandoned
+1117            && if is_handshaking {
+1118                // There is only one path during the handshake. We want to
+1119                // already send 0-RTT and 0.5-RTT (permitting anti-amplification
+1120                // limit) data.
+1121                true
+1122            } else if !validated {
+1123                // TODO(flub): When we have a network change we might end up
+1124                //    having to abandon all paths and re-open new ones to the
+1125                //    same remotes. This leaves us without any validated
+1126                //    path. Perhaps we should have a way to figure out if the
+1127                //    path is to a previously-validated remote address and allow
+1128                //    sending data to such remotes immediately.
+1129                false
+1130            } else {
+1131                match status {
+1132                    PathStatus::Available => {
+1133                        // Best possible space to send data on.
+1134                        true
+1135                    }
+1136                    PathStatus::Backup => {
+1137                        // If there is a status-available path we prefer that.
+1138                        !have_validated_status_available_space
+1139                    }
+1140                }
+1141            };
+1142
+1143        // CONNECTION_CLOSE is allowed to be sent on a non-validated
+1144        // path. Particularly during the handshake we want to send it before the
+1145        // path is validated. Later if there is no validated path available we
+1146        // will also accept sending it on an un-validated path.
+1147        let may_send_close = has_cids
+1148            && !abandoned
+1149            && if !validated && have_validated_status_available_space {
+1150                // We have a better space to send on.
+1151                false
+1152            } else {
+1153                // No other validated space, this is as good as it gets.
+1154                true
+1155            };
+1156
+1157        // PATH_ABANDON is normally sent together with other SpaceKind::Data frames. But if
+1158        // there is no other validated space to send it on, it can be sent on the path to be
+1159        // abandoned itself if that was validated.
+1160        let may_self_abandon = has_cids && validated && !have_validated_space;
+1161
+1162        PathSchedulingInfo {
+1163            is_abandoned: abandoned,
+1164            may_send_data,
+1165            may_send_close,
+1166            may_self_abandon,
+1167        }
+1168    }
+1169
+1170    fn build_transmit(&mut self, path_id: PathId, transmit: TransmitBuf<'_>) -> Transmit {
+1171        debug_assert!(
+1172            !transmit.is_empty(),
+1173            "must not be called with an empty transmit buffer"
+1174        );
+1175
+1176        let network_path = self.path_data(path_id).network_path;
+1177        trace!(
+1178            segment_size = transmit.segment_size(),
+1179            last_datagram_len = transmit.len() % transmit.segment_size(),
+1180            %network_path,
+1181            "sending {} bytes in {} datagrams",
+1182            transmit.len(),
+1183            transmit.num_datagrams()
+1184        );
+1185        self.path_data_mut(path_id)
+1186            .inc_total_sent(transmit.len() as u64);
+1187
+1188        self.stats
+1189            .udp_tx
+1190            .on_sent(transmit.num_datagrams() as u64, transmit.len());
+1191        self.path_stats
+1192            .entry(path_id)
+1193            .or_default()
+1194            .udp_tx
+1195            .on_sent(transmit.num_datagrams() as u64, transmit.len());
+1196
+1197        Transmit {
+1198            destination: network_path.remote,
+1199            size: transmit.len(),
+1200            ecn: if self.path_data(path_id).sending_ecn {
+1201                Some(EcnCodepoint::Ect0)
+1202            } else {
+1203                None
+1204            },
+1205            segment_size: match transmit.num_datagrams() {
+1206                1 => None,
+1207                _ => Some(transmit.segment_size()),
+1208            },
+1209            src_ip: network_path.local_ip,
+1210        }
+1211    }
+1212
+1213    /// poll_transmit logic for off-path data.
+1214    fn poll_transmit_off_path(
+1215        &mut self,
+1216        now: Instant,
+1217        buf: &mut Vec<u8>,
+1218        path_id: PathId,
+1219    ) -> Option<Transmit> {
+1220        if let Some(challenge) = self.send_prev_path_challenge(now, buf, path_id) {
+1221            return Some(challenge);
+1222        }
+1223        if let Some(response) = self.send_off_path_path_response(now, buf, path_id) {
+1224            return Some(response);
+1225        }
+1226        if let Some(challenge) = self.send_nat_traversal_path_challenge(now, buf, path_id) {
+1227            return Some(challenge);
+1228        }
+1229        None
+1230    }
+1231
+1232    /// poll_transmit logic for on-path data.
+1233    ///
+1234    /// This is not quite the same as for a multipath packet space, since [`PathId::ZERO`]
+1235    /// has 3 packet spaces, which this handles.
+1236    ///
+1237    /// See [`Self::poll_transmit_off_path`] for off-path data.
+1238    #[must_use]
+1239    fn poll_transmit_on_path(
+1240        &mut self,
+1241        now: Instant,
+1242        buf: &mut Vec<u8>,
+1243        path_id: PathId,
+1244        max_datagrams: NonZeroUsize,
+1245        scheduling_info: &PathSchedulingInfo,
+1246        connection_close_pending: bool,
+1247    ) -> PollPathStatus {
+1248        // Check if there is at least one active CID to use for sending
+1249        let Some(remote_cid) = self.remote_cids.get(&path_id).map(CidQueue::active) else {
+1250            if !self.abandoned_paths.contains(&path_id) {
+1251                debug!(%path_id, "no remote CIDs for path");
+1252            }
+1253            return PollPathStatus::NothingToSend {
+1254                congestion_blocked: false,
+1255            };
+1256        };
+1257
+1258        // Whether the last packet in the datagram must be padded so the datagram takes up
+1259        // an exact size. An earlier space can decide to not fill an entire datagram and
+1260        // require the next space to fill it further. But may need a specific size of the
+1261        // datagram containing the packet. The final packet built in the datagram must pad
+1262        // to this size.
+1263        let mut pad_datagram = PadDatagram::No;
+1264
+1265        // The packet number of the last built packet. This is kept kept across spaces.
+1266        // QUIC is supposed to have a single congestion controller for the Initial,
+1267        // Handshake and Data(PathId::ZERO) spaces.
+1268        let mut last_packet_number = None;
+1269
+1270        // If we end up not sending anything, we need to know if that was because there was
+1271        // nothing to send or because we were congestion blocked.
+1272        let mut congestion_blocked = false;
+1273
+1274        // Set the segment size to this path's MTU for on-path data.
+1275        let pmtu = self.path_data(path_id).current_mtu().into();
+1276        let mut transmit = TransmitBuf::new(buf, max_datagrams, pmtu);
+1277
+1278        // Iterate over the available spaces.
+1279        for space_id in SpaceId::iter() {
+1280            // Only PathId::ZERO uses non Data space ids.
+1281            if path_id != PathId::ZERO && space_id != SpaceId::Data {
+1282                continue;
+1283            }
+1284            match self.poll_transmit_path_space(
+1285                now,
+1286                &mut transmit,
+1287                path_id,
+1288                space_id,
+1289                remote_cid,
+1290                scheduling_info,
+1291                connection_close_pending,
+1292                pad_datagram,
+1293            ) {
+1294                PollPathSpaceStatus::NothingToSend {
+1295                    congestion_blocked: cb,
+1296                } => {
+1297                    congestion_blocked |= cb;
+1298                    // Continue checking other spaces, tail-loss probes may need to be sent
+1299                    // in all spaces.
+1300                }
+1301                PollPathSpaceStatus::WrotePacket {
+1302                    last_packet_number: pn,
+1303                    pad_datagram: pad,
+1304                } => {
+1305                    debug_assert!(!transmit.is_empty(), "transmit must contain packets");
+1306                    last_packet_number = Some(pn);
+1307                    pad_datagram = pad;
+1308                    // Always check higher spaces. If the transmit is full or they have
+1309                    // nothing to send they will not write packets. But if they can, they
+1310                    // must always be allowed to add to this transmit because coalescing may
+1311                    // be required.
+1312                    continue;
+1313                }
+1314                PollPathSpaceStatus::Send {
+1315                    last_packet_number: pn,
+1316                } => {
+1317                    debug_assert!(!transmit.is_empty(), "transmit must contain packets");
+1318                    last_packet_number = Some(pn);
+1319                    break;
+1320                }
+1321            }
+1322        }
+1323
+1324        if last_packet_number.is_some() || congestion_blocked {
+1325            self.qlog.emit_recovery_metrics(
+1326                path_id,
+1327                &mut self.paths.get_mut(&path_id).unwrap().data,
+1328                now,
+1329            );
+1330        }
+1331
+1332        match last_packet_number {
+1333            Some(last_packet_number) => {
+1334                // Note that when sending in multiple spaces the last packet number will be
+1335                // the one from the highest space.
+1336                self.path_data_mut(path_id).congestion.on_sent(
+1337                    now,
+1338                    transmit.len() as u64,
+1339                    last_packet_number,
+1340                );
+1341                PollPathStatus::Send(self.build_transmit(path_id, transmit))
+1342            }
+1343            None => PollPathStatus::NothingToSend { congestion_blocked },
+1344        }
+1345    }
+1346
+1347    /// poll_transmit logic for a QUIC-MULTIPATH packet number space (PathID + SpaceId).
+1348    #[must_use]
+1349    fn poll_transmit_path_space(
+1350        &mut self,
+1351        now: Instant,
+1352        transmit: &mut TransmitBuf<'_>,
+1353        path_id: PathId,
+1354        space_id: SpaceId,
+1355        remote_cid: ConnectionId,
+1356        scheduling_info: &PathSchedulingInfo,
+1357        // If we need to send a CONNECTION_CLOSE frame.
+1358        connection_close_pending: bool,
+1359        // Whether the current datagram needs to be padded to a certain size.
+1360        mut pad_datagram: PadDatagram,
+1361    ) -> PollPathSpaceStatus {
+1362        // Keep track of the last packet number we wrote. If None we did not write any
+1363        // packets.
+1364        let mut last_packet_number = None;
+1365
+1366        // Each loop of this may build one packet. It works logically as follows:
+1367        //
+1368        // - Check if something *needs* to be sent in this space and *can* be sent.
+1369        //   - If not, return to the caller who will call us again for the next space.
+1370        // - Start a new datagram.
+1371        //   - Unless coalescing the packet into an existing datagram.
+1372        // - Write the packet header and payload.
+1373        // - Check if coalescing a next packet into the datagram is possible.
+1374        // - If coalescing, finish packet without padding to leave space in the datagram.
+1375        // - If not coalescing, complete the datagram:
+1376        //   - Finish packet with padding.
+1377        //   - Set the transmit segment size if this is the first datagram.
+1378        // - Loop: next iteration will exit the loop if nothing more to send in this
+1379        //   space. The TransmitBuf will contain a started datagram with space if
+1380        //   coalescing, or completely filled datagram if not coalescing.
+1381        loop {
+1382            // Determine if anything can be sent in this packet number space.
+1383            let max_packet_size = if transmit.datagram_remaining_mut() > 0 {
+1384                // A datagram is started already, we are coalescing another packet into it.
+1385                transmit.datagram_remaining_mut()
+1386            } else {
+1387                // A new datagram needs to be started.
+1388                transmit.segment_size()
+1389            };
+1390            let can_send =
+1391                self.space_can_send(space_id, path_id, max_packet_size, connection_close_pending);
+1392            let needs_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0;
+1393            let space_will_send = {
+1394                if scheduling_info.is_abandoned {
+1395                    // If this path is abandoned then we might still have to send
+1396                    // PATH_ABANDON itself on it if there was no better space
+1397                    // available. Otherwise we want to send the PATH_ABANDON as permitted by
+1398                    // may_send_data however.
+1399                    scheduling_info.may_self_abandon
+1400                        && self.spaces[space_id]
+1401                            .pending
+1402                            .path_abandon
+1403                            .contains_key(&path_id)
+1404                } else if can_send.close && scheduling_info.may_send_close {
+1405                    // This is the best path to send a CONNECTION_CLOSE on.
+1406                    true
+1407                } else if needs_loss_probe || can_send.space_only {
+1408                    // We always send a loss probe or space-specific frames if the path is
+1409                    // not abandoned.
+1410                    true
+1411                } else {
+1412                    // Anything else we only send if we're the best path for SpaceKind::Data
+1413                    // frames.
+1414                    !can_send.is_empty() && scheduling_info.may_send_data
+1415                }
+1416            };
+1417
+1418            if !space_will_send {
+1419                // Nothing more to send. Previous iterations of this loop may have built
+1420                // packets already.
+1421                return match last_packet_number {
+1422                    Some(pn) => PollPathSpaceStatus::WrotePacket {
+1423                        last_packet_number: pn,
+1424                        pad_datagram,
+1425                    },
+1426                    None => {
+1427                        // Only log for spaces which have crypto.
+1428                        if self.crypto_state.has_keys(space_id.encryption_level())
+1429                            || (space_id == SpaceId::Data
+1430                                && self.crypto_state.has_keys(EncryptionLevel::ZeroRtt))
+1431                        {
+1432                            trace!(?space_id, %path_id, "nothing to send in space");
+1433                        }
+1434                        PollPathSpaceStatus::NothingToSend {
+1435                            congestion_blocked: false,
+1436                        }
+1437                    }
+1438                };
+1439            }
+1440
+1441            // We want to send on this space, check congestion control if we can. But only
+1442            // if we will need to start a new datagram. If we are coalescing into an already
+1443            // started datagram we do not need to check congestion control again.
+1444            if transmit.datagram_remaining_mut() == 0 {
+1445                let congestion_blocked =
+1446                    self.path_congestion_check(space_id, path_id, transmit, &can_send, now);
+1447                if congestion_blocked != PathBlocked::No {
+1448                    // Previous iterations of this loop may have built packets already.
+1449                    return match last_packet_number {
+1450                        Some(pn) => PollPathSpaceStatus::WrotePacket {
+1451                            last_packet_number: pn,
+1452                            pad_datagram,
+1453                        },
+1454                        None => {
+1455                            return PollPathSpaceStatus::NothingToSend {
+1456                                congestion_blocked: true,
+1457                            };
+1458                        }
+1459                    };
+1460                }
+1461            }
+1462
+1463            // If the datagram is full (or there never was one started), we need to start a
+1464            // new one.
+1465            if transmit.datagram_remaining_mut() == 0 {
+1466                if transmit.num_datagrams() >= transmit.max_datagrams().get() {
+1467                    // No more datagrams allowed.
+1468                    // Previous iterations of this loop may have built packets already.
+1469                    return match last_packet_number {
+1470                        Some(pn) => PollPathSpaceStatus::WrotePacket {
+1471                            last_packet_number: pn,
+1472                            pad_datagram,
+1473                        },
+1474                        None => {
+1475                            return PollPathSpaceStatus::NothingToSend {
+1476                                congestion_blocked: false,
+1477                            };
+1478                        }
+1479                    };
+1480                }
+1481
+1482                match self.spaces[space_id].for_path(path_id).loss_probes {
+1483                    0 => transmit.start_new_datagram(),
+1484                    _ => {
+1485                        // We need something to send for a tail-loss probe.
+1486                        let request_immediate_ack =
+1487                            space_id == SpaceId::Data && self.peer_supports_ack_frequency();
+1488                        self.spaces[space_id].maybe_queue_probe(
+1489                            path_id,
+1490                            request_immediate_ack,
+1491                            &self.streams,
+1492                        );
+1493
+1494                        self.spaces[space_id].for_path(path_id).loss_probes -= 1;
+1495
+1496                        // Clamp the datagram to at most the minimum MTU to ensure that loss
+1497                        // probes can get through and enable recovery even if the path MTU
+1498                        // has shrank unexpectedly.
+1499                        transmit.start_new_datagram_with_size(std::cmp::min(
+1500                            usize::from(INITIAL_MTU),
+1501                            transmit.segment_size(),
+1502                        ));
+1503                    }
+1504                }
+1505                trace!(count = transmit.num_datagrams(), "new datagram started");
+1506
+1507                // We started a new datagram, we decide later if it needs padding.
+1508                pad_datagram = PadDatagram::No;
+1509            }
+1510
+1511            // If coalescing another packet into the existing datagram, there should
+1512            // still be enough space for a whole packet.
+1513            if transmit.datagram_start_offset() < transmit.len() {
+1514                debug_assert!(transmit.datagram_remaining_mut() >= MIN_PACKET_SPACE);
+1515            }
+1516
+1517            //
+1518            // From here on, we've determined that a packet will definitely be sent.
+1519            //
+1520
+1521            if self.crypto_state.has_keys(EncryptionLevel::Initial)
+1522                && space_id == SpaceId::Handshake
+1523                && self.side.is_client()
+1524            {
+1525                // A client stops both sending and processing Initial packets when it
+1526                // sends its first Handshake packet.
+1527                self.discard_space(now, SpaceKind::Initial);
+1528            }
+1529            if let Some(ref mut prev) = self.crypto_state.prev_crypto {
+1530                prev.update_unacked = false;
+1531            }
+1532
+1533            let Some(mut builder) = PacketBuilder::new(
+1534                now,
+1535                space_id,
+1536                path_id,
+1537                remote_cid,
+1538                transmit,
+1539                can_send.is_ack_eliciting(),
+1540                self,
+1541            ) else {
+1542                // Confidentiality limit is exceeded and the connection has been killed. We
+1543                // should not send any other packets. This works in a roundabout way: We
+1544                // have started a datagram but not written anything into it. So even if we
+1545                // get called again for another space we will see an already started
+1546                // datagram and try and start another packet here. Then be stopped by the
+1547                // same confidentiality limit.
+1548                return PollPathSpaceStatus::NothingToSend {
+1549                    congestion_blocked: false,
+1550                };
+1551            };
+1552            last_packet_number = Some(builder.packet_number);
+1553
+1554            if space_id == SpaceId::Initial
+1555                && (self.side.is_client() || can_send.is_ack_eliciting())
+1556            {
+1557                // https://www.rfc-editor.org/rfc/rfc9000.html#section-14.1
+1558                pad_datagram |= PadDatagram::ToMinMtu;
+1559            }
+1560            if space_id == SpaceId::Data && self.config.pad_to_mtu {
+1561                pad_datagram |= PadDatagram::ToSegmentSize;
+1562            }
+1563
+1564            if scheduling_info.may_send_close && can_send.close {
+1565                trace!("sending CONNECTION_CLOSE");
+1566                // Encode ACKs before the ConnectionClose message, to give the receiver
+1567                // a better approximate on what data has been processed. This is
+1568                // especially important with ack delay, since the peer might not
+1569                // have gotten any other ACK for the data earlier on.
+1570                let is_multipath_negotiated = self.is_multipath_negotiated();
+1571                for path_id in self.spaces[space_id]
+1572                    .number_spaces
+1573                    .iter()
+1574                    .filter(|(_, pns)| !pns.pending_acks.ranges().is_empty())
+1575                    .map(|(&path_id, _)| path_id)
+1576                    .collect::<Vec<_>>()
+1577                {
+1578                    Self::populate_acks(
+1579                        now,
+1580                        self.receiving_ecn,
+1581                        path_id,
+1582                        space_id,
+1583                        &mut self.spaces[space_id],
+1584                        is_multipath_negotiated,
+1585                        &mut builder,
+1586                        &mut self.stats.frame_tx,
+1587                        self.crypto_state.has_keys(space_id.encryption_level()),
+1588                    );
+1589                }
+1590
+1591                // Since there only 64 ACK frames there will always be enough space
+1592                // to encode the ConnectionClose frame too. However we still have the
+1593                // check here to prevent crashes if something changes.
+1594
+1595                // TODO(flub): This needs fixing for multipath, to ensure we can always
+1596                //    write the CONNECTION_CLOSE even if we have many PATH_ACKs to send:
+1597                //    https://github.com/n0-computer/noq/issues/367.
+1598                debug_assert!(
+1599                    builder.frame_space_remaining() > frame::ConnectionClose::SIZE_BOUND,
+1600                    "ACKs should leave space for ConnectionClose"
+1601                );
+1602                let stats = &mut self.stats.frame_tx;
+1603                if frame::ConnectionClose::SIZE_BOUND < builder.frame_space_remaining() {
+1604                    let max_frame_size = builder.frame_space_remaining();
+1605                    let close: Close = match self.state.as_type() {
+1606                        StateType::Closed => {
+1607                            let reason: Close =
+1608                                self.state.as_closed().expect("checked").clone().into();
+1609                            if space_id == SpaceId::Data || reason.is_transport_layer() {
+1610                                reason
+1611                            } else {
+1612                                TransportError::APPLICATION_ERROR("").into()
+1613                            }
+1614                        }
+1615                        StateType::Draining => TransportError::NO_ERROR("").into(),
+1616                        _ => unreachable!(
+1617                            "tried to make a close packet when the connection wasn't closed"
+1618                        ),
+1619                    };
+1620                    builder.write_frame(close.encoder(max_frame_size), stats);
+1621                }
+1622                let last_pn = builder.packet_number;
+1623                builder.finish_and_track(now, self, path_id, pad_datagram);
+1624                if space_id.kind() == self.highest_space {
+1625                    // Don't send another close packet. Even with multipath we only send
+1626                    // CONNECTION_CLOSE on a single path since we expect our paths to work.
+1627                    self.connection_close_pending = false;
+1628                }
+1629                // Send a close frame in every possible space for robustness, per
+1630                // RFC9000 "Immediate Close during the Handshake". Don't bother trying
+1631                // to send anything else.
+1632                // TODO(flub): This breaks during the handshake if we can not coalesce
+1633                //    packets due to space reasons: the next space would either fail a
+1634                //    debug_assert checking for enough packet space or produce an invalid
+1635                //    packet. We need to keep track of per-space pending CONNECTION_CLOSE to
+1636                //    be able to send these across multiple calls to poll_transmit. Then
+1637                //    check for coalescing space here because initial packets need to be in
+1638                //    padded datagrams. And also add space checks for CONNECTION_CLOSE in
+1639                //    space_can_send so it would stop a GSO batch if the datagram is too
+1640                //    small for another CONNECTION_CLOSE packet.
+1641                return PollPathSpaceStatus::WrotePacket {
+1642                    last_packet_number: last_pn,
+1643                    pad_datagram,
+1644                };
+1645            }
+1646
+1647            self.populate_packet(now, space_id, path_id, scheduling_info, &mut builder);
+1648
+1649            // ACK-only packets should only be sent when explicitly allowed. If we write them due to
+1650            // any other reason, there is a bug which leads to one component announcing write
+1651            // readiness while not writing any data. This degrades performance. The condition is
+1652            // only checked if the full MTU is available and when potentially large fixed-size
+1653            // frames aren't queued, so that lack of space in the datagram isn't the reason for just
+1654            // writing ACKs.
+1655            debug_assert!(
+1656                !(builder.sent_frames().is_ack_only(&self.streams)
+1657                    && !can_send.acks
+1658                    && (can_send.other || can_send.space_only)
+1659                    && builder.buf.segment_size()
+1660                        == self.path_data(path_id).current_mtu() as usize
+1661                    && self.datagrams.outgoing.is_empty()),
+1662                "SendableFrames was {can_send:?}, but only ACKs have been written"
+1663            );
+1664            if builder.sent_frames().requires_padding {
+1665                pad_datagram |= PadDatagram::ToMinMtu;
+1666            }
+1667
+1668            for (path_id, _pn) in builder.sent_frames().largest_acked.iter() {
+1669                self.spaces[space_id]
+1670                    .for_path(*path_id)
+1671                    .pending_acks
+1672                    .acks_sent();
+1673                self.timers.stop(
+1674                    Timer::PerPath(*path_id, PathTimer::MaxAckDelay),
+1675                    self.qlog.with_time(now),
+1676                );
+1677            }
+1678
+1679            // Now we need to finish the packet.  Before we do so we need to know if we will
+1680            // be coalescing the next packet into this one, or will be ending the datagram
+1681            // as well.  Because if this is the last packet in the datagram more padding
+1682            // might be needed because of the packet type, or to fill the GSO segment size.
+1683
+1684            // Are we allowed to coalesce AND is there enough space for another *packet* in
+1685            // this datagram AND will we definitely send another packet?
+1686            if builder.can_coalesce && path_id == PathId::ZERO && {
+1687                let max_packet_size = builder
+1688                    .buf
+1689                    .datagram_remaining_mut()
+1690                    .saturating_sub(builder.predict_packet_end());
+1691                max_packet_size > MIN_PACKET_SPACE
+1692                    && self.has_pending_packet(space_id, max_packet_size, connection_close_pending)
+1693            } {
+1694                // We can append/coalesce the next packet into the current
+1695                // datagram. Finish the current packet without adding extra padding.
+1696                trace!("will coalesce with next packet");
+1697                builder.finish_and_track(now, self, path_id, PadDatagram::No);
+1698            } else {
+1699                // We need a new datagram for the next packet.  Finish the current
+1700                // packet with padding.
+1701                // TODO(flub): if there isn't any more data to be sent, this will still pad
+1702                //    to the segment size and only discover there is nothing to send before
+1703                //    starting the next packet. That is wasting up to 32 bytes.
+1704                if builder.buf.num_datagrams() > 1 && matches!(pad_datagram, PadDatagram::No) {
+1705                    // If too many padding bytes would be required to continue the
+1706                    // GSO batch after this packet, end the GSO batch here. Ensures
+1707                    // that fixed-size frames with heterogeneous sizes
+1708                    // (e.g. application datagrams) won't inadvertently waste large
+1709                    // amounts of bandwidth. The exact threshold is a bit arbitrary
+1710                    // and might benefit from further tuning, though there's no
+1711                    // universally optimal value.
+1712                    const MAX_PADDING: usize = 32;
+1713                    if builder.buf.datagram_remaining_mut()
+1714                        > builder.predict_packet_end() + MAX_PADDING
+1715                    {
+1716                        trace!(
+1717                            "GSO truncated by demand for {} padding bytes",
+1718                            builder.buf.datagram_remaining_mut() - builder.predict_packet_end()
+1719                        );
+1720                        let last_pn = builder.packet_number;
+1721                        builder.finish_and_track(now, self, path_id, PadDatagram::No);
+1722                        return PollPathSpaceStatus::Send {
+1723                            last_packet_number: last_pn,
+1724                        };
+1725                    }
+1726
+1727                    // Pad the current datagram to GSO segment size so it can be
+1728                    // included in the GSO batch.
+1729                    builder.finish_and_track(now, self, path_id, PadDatagram::ToSegmentSize);
+1730                } else {
+1731                    builder.finish_and_track(now, self, path_id, pad_datagram);
+1732                }
+1733
+1734                // If this is the first datagram we set the segment size to the size of the
+1735                // first datagram.
+1736                if transmit.num_datagrams() == 1 {
+1737                    transmit.clip_segment_size();
+1738                }
+1739            }
+1740        }
+1741    }
+1742
+1743    fn poll_transmit_mtu_probe(
+1744        &mut self,
+1745        now: Instant,
+1746        buf: &mut Vec<u8>,
+1747        path_id: PathId,
+1748    ) -> Option<Transmit> {
+1749        let (active_cid, probe_size) = self.get_mtu_probe_data(now, path_id)?;
+1750
+1751        // We are definitely sending a DPLPMTUD probe.
+1752        let mut transmit = TransmitBuf::new(buf, NonZeroUsize::MIN, probe_size as usize);
+1753        transmit.start_new_datagram_with_size(probe_size as usize);
+1754
+1755        let mut builder = PacketBuilder::new(
+1756            now,
+1757            SpaceId::Data,
+1758            path_id,
+1759            active_cid,
+1760            &mut transmit,
+1761            true,
+1762            self,
+1763        )?;
+1764
+1765        // We implement MTU probes as ping packets padded up to the probe size
+1766        trace!(?probe_size, "writing MTUD probe");
+1767        builder.write_frame(frame::Ping, &mut self.stats.frame_tx);
+1768
+1769        // If supported by the peer, we want no delays to the probe's ACK
+1770        if self.peer_supports_ack_frequency() {
+1771            builder.write_frame(frame::ImmediateAck, &mut self.stats.frame_tx);
+1772        }
+1773
+1774        builder.finish_and_track(now, self, path_id, PadDatagram::ToSize(probe_size));
+1775
+1776        self.path_stats
+1777            .entry(path_id)
+1778            .or_default()
+1779            .sent_plpmtud_probes += 1;
+1780
+1781        Some(self.build_transmit(path_id, transmit))
+1782    }
+1783
+1784    /// Returns the CID and probe size if a DPLPMTUD probe is needed.
+1785    ///
+1786    /// We MTU probe all paths for which all of the following is true:
+1787    /// - We have an active destination CID for the path.
+1788    /// - The remote address *and* path are validated.
+1789    /// - The path is not abandoned.
+1790    /// - The MTU Discovery subsystem wants to probe the path.
+1791    fn get_mtu_probe_data(&mut self, now: Instant, path_id: PathId) -> Option<(ConnectionId, u16)> {
+1792        let active_cid = self.remote_cids.get(&path_id).map(CidQueue::active)?;
+1793        let is_eligible = self.path_data(path_id).validated
+1794            && !self.path_data(path_id).is_validating_path()
+1795            && !self.abandoned_paths.contains(&path_id);
+1796
+1797        if !is_eligible {
+1798            return None;
+1799        }
+1800        let next_pn = self.spaces[SpaceId::Data]
+1801            .for_path(path_id)
+1802            .peek_tx_number();
+1803        let probe_size = self
+1804            .path_data_mut(path_id)
+1805            .mtud
+1806            .poll_transmit(now, next_pn)?;
+1807
+1808        Some((active_cid, probe_size))
+1809    }
+1810
+1811    /// Returns true if there is a further packet to send on [`PathId::ZERO`].
+1812    ///
+1813    /// In other words this is predicting whether the next call to
+1814    /// [`Connection::space_can_send`] issued will return some frames to be sent. Including
+1815    /// having to predict which packet number space it will be invoked with. This depends on
+1816    /// how both [`Connection::poll_transmit_on_path`] and
+1817    /// [`Connection::poll_transmit_path_space`] behave.
+1818    ///
+1819    /// This is needed to determine if packet coalescing can happen. Because the last packet
+1820    /// in a datagram may need to be padded and thus we must know if another packet will
+1821    /// follow or not.
+1822    ///
+1823    /// The next packet can be either in the same space, or in one of the following spaces
+1824    /// on the same path. Because a 0-RTT packet can be coalesced with a 1-RTT packet and
+1825    /// both are in the Data(PathId::ZERO) space. Previous spaces are not checked, because
+1826    /// packets are built from Initial to Handshake to Data spaces.
+1827    fn has_pending_packet(
+1828        &mut self,
+1829        current_space_id: SpaceId,
+1830        max_packet_size: usize,
+1831        connection_close_pending: bool,
+1832    ) -> bool {
+1833        let mut space_id = current_space_id;
+1834        loop {
+1835            let can_send = self.space_can_send(
+1836                space_id,
+1837                PathId::ZERO,
+1838                max_packet_size,
+1839                connection_close_pending,
+1840            );
+1841            if !can_send.is_empty() {
+1842                return true;
+1843            }
+1844            match space_id.next() {
+1845                Some(next_space_id) => space_id = next_space_id,
+1846                None => break,
+1847            }
+1848        }
+1849        false
+1850    }
+1851
+1852    /// Checks if creating a new datagram would be blocked by congestion control
+1853    fn path_congestion_check(
+1854        &mut self,
+1855        space_id: SpaceId,
+1856        path_id: PathId,
+1857        transmit: &TransmitBuf<'_>,
+1858        can_send: &SendableFrames,
+1859        now: Instant,
+1860    ) -> PathBlocked {
+1861        // Anti-amplification is only based on `total_sent`, which gets updated after
+1862        // the transmit is sent. Therefore we pass the amount of bytes for datagrams
+1863        // that are already created, as well as 1 byte for starting another datagram. If
+1864        // there is any anti-amplification budget left, we always allow a full MTU to be
+1865        // sent (see https://github.com/quinn-rs/quinn/issues/1082).
+1866        if self.side().is_server()
+1867            && self
+1868                .path_data(path_id)
+1869                .anti_amplification_blocked(transmit.len() as u64 + 1)
+1870        {
+1871            trace!(?space_id, %path_id, "blocked by anti-amplification");
+1872            return PathBlocked::AntiAmplification;
+1873        }
+1874
+1875        // Congestion control check.
+1876        // Tail loss probes must not be blocked by congestion, or a deadlock could arise.
+1877        let bytes_to_send = transmit.segment_size() as u64;
+1878        let need_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0;
+1879
+1880        if can_send.other && !need_loss_probe && !can_send.close {
+1881            let path = self.path_data(path_id);
+1882            if path.in_flight.bytes + bytes_to_send >= path.congestion.window() {
+1883                trace!(
+1884                    ?space_id,
+1885                    %path_id,
+1886                    in_flight=%path.in_flight.bytes,
+1887                    congestion_window=%path.congestion.window(),
+1888                    "blocked by congestion control",
+1889                );
+1890                return PathBlocked::Congestion;
+1891            }
+1892        }
+1893
+1894        // Pacing check.
+1895        if let Some(delay) = self.path_data_mut(path_id).pacing_delay(bytes_to_send, now) {
+1896            self.timers.set(
+1897                Timer::PerPath(path_id, PathTimer::Pacing),
+1898                delay,
+1899                self.qlog.with_time(now),
+1900            );
+1901            // Loss probes and CONNECTION_CLOSE should be subject to pacing, even though
+1902            // they are not congestion controlled.
+1903            trace!(?space_id, %path_id, "blocked by pacing");
+1904            return PathBlocked::Pacing;
+1905        }
+1906
+1907        PathBlocked::No
+1908    }
+1909
+1910    /// Send PATH_CHALLENGE for a previous path if necessary
+1911    ///
+1912    /// QUIC-TRANSPORT section 9.3.3
+1913    /// <https://www.rfc-editor.org/rfc/rfc9000.html#name-off-path-packet-forwarding>
+1914    fn send_prev_path_challenge(
+1915        &mut self,
+1916        now: Instant,
+1917        buf: &mut Vec<u8>,
+1918        path_id: PathId,
+1919    ) -> Option<Transmit> {
+1920        let (prev_cid, prev_path) = self.paths.get_mut(&path_id)?.prev.as_mut()?;
+1921        // TODO (matheus23): We could use !prev_path.is_validating() here instead to
+1922        // (possibly) also re-send challenges when they get lost.
+1923        if !prev_path.pending_on_path_challenge {
+1924            return None;
+1925        };
+1926        prev_path.pending_on_path_challenge = false;
+1927        let token = self.rng.random();
+1928        let network_path = prev_path.network_path;
+1929        prev_path.record_path_challenge_sent(now, token, network_path);
+1930
+1931        debug_assert_eq!(
+1932            self.highest_space,
+1933            SpaceKind::Data,
+1934            "PATH_CHALLENGE queued without 1-RTT keys"
+1935        );
+1936        let buf = &mut TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into());
+1937        buf.start_new_datagram();
+1938
+1939        // Use the previous CID to avoid linking the new path with the previous path. We
+1940        // don't bother accounting for possible retirement of that prev_cid because this is
+1941        // sent once, immediately after migration, when the CID is known to be valid. Even
+1942        // if a post-migration packet caused the CID to be retired, it's fair to pretend
+1943        // this is sent first.
+1944        let mut builder =
+1945            PacketBuilder::new(now, SpaceId::Data, path_id, *prev_cid, buf, false, self)?;
+1946        let challenge = frame::PathChallenge(token);
+1947        let stats = &mut self.stats.frame_tx;
+1948        builder.write_frame_with_log_msg(challenge, stats, Some("validating previous path"));
+1949
+1950        // An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame
+1951        // to at least the smallest allowed maximum datagram size of 1200 bytes,
+1952        // unless the anti-amplification limit for the path does not permit
+1953        // sending a datagram of this size
+1954        builder.pad_to(MIN_INITIAL_SIZE);
+1955
+1956        builder.finish(self, now);
+1957        self.stats.udp_tx.on_sent(1, buf.len());
+1958        self.path_stats
+1959            .entry(path_id)
+1960            .or_default()
+1961            .udp_tx
+1962            .on_sent(1, buf.len());
+1963
+1964        Some(Transmit {
+1965            destination: network_path.remote,
+1966            size: buf.len(),
+1967            ecn: None,
+1968            segment_size: None,
+1969            src_ip: network_path.local_ip,
+1970        })
+1971    }
+1972
+1973    fn send_off_path_path_response(
+1974        &mut self,
+1975        now: Instant,
+1976        buf: &mut Vec<u8>,
+1977        path_id: PathId,
+1978    ) -> Option<Transmit> {
+1979        let path = self.paths.get_mut(&path_id).map(|state| &mut state.data)?;
+1980        let cid_queue = self.remote_cids.get_mut(&path_id)?;
+1981        let (token, network_path) = path.path_responses.pop_off_path(path.network_path)?;
+1982
+1983        let cid = cid_queue
+1984            .next_reserved()
+1985            .unwrap_or_else(|| cid_queue.active());
+1986        // TODO(@divma): we should take a different approach when there is no fresh CID to use.
+1987        // https://github.com/quinn-rs/quinn/issues/2184
+1988
+1989        let frame = frame::PathResponse(token);
+1990
+1991        let buf = &mut TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into());
+1992        buf.start_new_datagram();
+1993
+1994        let mut builder = PacketBuilder::new(now, SpaceId::Data, path_id, cid, buf, false, self)?;
+1995        let stats = &mut self.stats.frame_tx;
+1996        builder.write_frame_with_log_msg(frame, stats, Some("(off-path)"));
+1997        builder.finish_and_track(now, self, path_id, PadDatagram::ToMinMtu);
+1998
+1999        let size = buf.len();
+2000
+2001        self.stats.udp_tx.on_sent(1, size);
+2002        self.path_stats
+2003            .entry(path_id)
+2004            .or_default()
+2005            .udp_tx
+2006            .on_sent(1, size);
+2007        Some(Transmit {
+2008            destination: network_path.remote,
+2009            size,
+2010            ecn: None,
+2011            segment_size: None,
+2012            src_ip: network_path.local_ip,
+2013        })
+2014    }
+2015
+2016    /// Send a nat traversal challenge (off-path) on this path if possible.
+2017    ///
+2018    /// This will ensure the path still has a remaining CID to use if the active one should be
+2019    /// retired.
+2020    fn send_nat_traversal_path_challenge(
+2021        &mut self,
+2022        now: Instant,
+2023        buf: &mut Vec<u8>,
+2024        path_id: PathId,
+2025    ) -> Option<Transmit> {
+2026        let server_side = self.n0_nat_traversal.server_side_mut().ok()?;
+2027        let probe = server_side.next_probe()?;
+2028        if !self.paths.get(&path_id)?.data.validated {
+2029            // Path is not usable for probing
+2030            return None;
+2031        }
+2032
+2033        let remote_cids = self.remote_cids.get_mut(&path_id)?;
+2034
+2035        // Check if this path has enough CIDs to send a probe. One to be reserved, one in case the
+2036        // active CID needs to be retired.
+2037        if remote_cids.remaining() < 2 {
+2038            return None;
+2039        }
+2040
+2041        let cid = remote_cids.next_reserved()?;
+2042        let remote = probe.remote();
+2043        let token = self.rng.random();
+2044        probe.mark_as_sent();
+2045
+2046        let frame = frame::PathChallenge(token);
+2047
+2048        let mut buf = TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into());
+2049        buf.start_new_datagram();
+2050
+2051        let mut builder =
+2052            PacketBuilder::new(now, SpaceId::Data, path_id, cid, &mut buf, false, self)?;
+2053        let stats = &mut self.stats.frame_tx;
+2054        builder.write_frame_with_log_msg(frame, stats, Some("(nat-traversal)"));
+2055        builder.finish_and_track(now, self, path_id, PadDatagram::ToMinMtu);
+2056
+2057        let path = &mut self.paths.get_mut(&path_id).expect("checked").data;
+2058        let network_path = FourTuple {
+2059            remote,
+2060            local_ip: None,
+2061        };
+2062
+2063        path.record_path_challenge_sent(now, token, network_path);
+2064
+2065        let size = buf.len();
+2066
+2067        self.stats.udp_tx.on_sent(1, size);
+2068        self.path_stats
+2069            .entry(path_id)
+2070            .or_default()
+2071            .udp_tx
+2072            .on_sent(1, size);
+2073
+2074        Some(Transmit {
+2075            destination: remote,
+2076            size,
+2077            ecn: None,
+2078            segment_size: None,
+2079            src_ip: None,
+2080        })
+2081    }
+2082
+2083    /// Indicate what types of frames are ready to send for the given space.
+2084    ///
+2085    /// Only for on-path data.
+2086    ///
+2087    /// *packet_size* is the number of bytes available to build the next packet.
+2088    /// *connection_close_pending* indicates whether a CONNECTION_CLOSE frame needs to be
+2089    /// sent.
+2090    fn space_can_send(
+2091        &mut self,
+2092        space_id: SpaceId,
+2093        path_id: PathId,
+2094        packet_size: usize,
+2095        connection_close_pending: bool,
+2096    ) -> SendableFrames {
+2097        let space = &mut self.spaces[space_id];
+2098        let space_has_crypto = self.crypto_state.has_keys(space_id.encryption_level());
+2099
+2100        if !space_has_crypto
+2101            && (space_id != SpaceId::Data
+2102                || !self.crypto_state.has_keys(EncryptionLevel::ZeroRtt)
+2103                || self.side.is_server())
+2104        {
+2105            // Nothing to send in this space
+2106            return SendableFrames::empty();
+2107        }
+2108
+2109        let mut can_send = space.can_send(path_id, &self.streams);
+2110
+2111        // Check for 1RTT space.
+2112        if space_id == SpaceId::Data {
+2113            let pn = space.for_path(path_id).peek_tx_number();
+2114            // Number of bytes available for frames if this is a 1-RTT packet. We're
+2115            // guaranteed to be able to send an individual frame at least this large in the
+2116            // next 1-RTT packet. This could be generalized to support every space, but it's
+2117            // only needed to handle large fixed-size frames, which only exist in 1-RTT
+2118            // (application datagrams).
+2119            let frame_space_1rtt =
+2120                packet_size.saturating_sub(self.predict_1rtt_overhead(pn, path_id));
+2121            can_send |= self.can_send_1rtt(path_id, frame_space_1rtt);
+2122        }
+2123
+2124        can_send.close = connection_close_pending && space_has_crypto;
+2125
+2126        can_send
+2127    }
+2128
+2129    /// Process `ConnectionEvent`s generated by the associated `Endpoint`
+2130    ///
+2131    /// Will execute protocol logic upon receipt of a connection event, in turn preparing signals
+2132    /// (including application `Event`s, `EndpointEvent`s and outgoing datagrams) that should be
+2133    /// extracted through the relevant methods.
+2134    pub fn handle_event(&mut self, event: ConnectionEvent) {
+2135        use ConnectionEventInner::*;
+2136        match event.0 {
+2137            Datagram(DatagramConnectionEvent {
+2138                now,
+2139                network_path,
+2140                path_id,
+2141                ecn,
+2142                first_decode,
+2143                remaining,
+2144            }) => {
+2145                let span = trace_span!("pkt", %path_id);
+2146                let _guard = span.enter();
+2147
+2148                if self.update_network_path_or_discard(network_path, path_id) {
+2149                    // A return value of true indicates we should discard this packet.
+2150                    return;
+2151                }
+2152
+2153                let was_anti_amplification_blocked = self
+2154                    .path(path_id)
+2155                    .map(|path| path.anti_amplification_blocked(1))
+2156                    // We never tried to send on an non-existing (new) path so have not been
+2157                    // anti-amplification blocked for it previously.
+2158                    .unwrap_or(false);
+2159
+2160                self.stats.udp_rx.datagrams += 1;
+2161                self.stats.udp_rx.bytes += first_decode.len() as u64;
+2162                let rx = &mut self.path_stats.entry(path_id).or_default().udp_rx;
+2163                rx.datagrams += 1;
+2164                rx.bytes += first_decode.len() as u64;
+2165                let data_len = first_decode.len();
+2166
+2167                self.handle_decode(now, network_path, path_id, ecn, first_decode);
+2168                // The current `path` might have changed inside `handle_decode` since the packet
+2169                // could have triggered a migration. The packet might also belong to an unknown
+2170                // path and have been rejected. Make sure the data received is accounted for the
+2171                // most recent path by accessing `path` after `handle_decode`.
+2172                if let Some(path) = self.path_mut(path_id) {
+2173                    path.inc_total_recvd(data_len as u64);
+2174                }
+2175
+2176                if let Some(data) = remaining {
+2177                    self.stats.udp_rx.bytes += data.len() as u64;
+2178                    self.path_stats.entry(path_id).or_default().udp_rx.bytes += data.len() as u64;
+2179                    self.handle_coalesced(now, network_path, path_id, ecn, data);
+2180                }
+2181
+2182                if let Some(path) = self.paths.get_mut(&path_id) {
+2183                    self.qlog
+2184                        .emit_recovery_metrics(path_id, &mut path.data, now);
+2185                }
+2186
+2187                if was_anti_amplification_blocked {
+2188                    // A prior attempt to set the loss detection timer may have failed due to
+2189                    // anti-amplification, so ensure it's set now. Prevents a handshake deadlock if
+2190                    // the server's first flight is lost.
+2191                    self.set_loss_detection_timer(now, path_id);
+2192                }
+2193            }
+2194            NewIdentifiers(ids, now, cid_len, cid_lifetime) => {
+2195                let path_id = ids.first().map(|issued| issued.path_id).unwrap_or_default();
+2196                debug_assert!(ids.iter().all(|issued| issued.path_id == path_id));
+2197                let cid_state = self
+2198                    .local_cid_state
+2199                    .entry(path_id)
+2200                    .or_insert_with(|| CidState::new(cid_len, cid_lifetime, now, 0));
+2201                cid_state.new_cids(&ids, now);
+2202
+2203                ids.into_iter().rev().for_each(|frame| {
+2204                    self.spaces[SpaceId::Data].pending.new_cids.push(frame);
+2205                });
+2206                // Always update Timer::PushNewCid
+2207                self.reset_cid_retirement(now);
+2208            }
+2209        }
+2210    }
+2211
+2212    /// Updates the network path for `path_id`.
+2213    ///
+2214    /// Returns true if a packet coming in for this `path_id` over given `network_path` should be discarded.
+2215    /// Returns false if the path was updated and the packet doesn't need to be discarded.
+2216    fn update_network_path_or_discard(&mut self, network_path: FourTuple, path_id: PathId) -> bool {
+2217        let remote_may_migrate = self.side.remote_may_migrate(&self.state);
+2218        let local_ip_may_migrate = self.side.is_client();
+2219        // If this packet could initiate a migration and we're a client or a server that
+2220        // forbids migration, drop the datagram. This could be relaxed to heuristically
+2221        // permit NAT-rebinding-like migration.
+2222        if let Some(known_path) = self.path_mut(path_id) {
+2223            if network_path.remote != known_path.network_path.remote && !remote_may_migrate {
+2224                trace!(
+2225                    %path_id,
+2226                    %network_path,
+2227                    %known_path.network_path,
+2228                    "discarding packet from unrecognized peer"
+2229                );
+2230                return true;
+2231            }
+2232
+2233            if known_path.network_path.local_ip.is_some()
+2234                && network_path.local_ip.is_some()
+2235                && known_path.network_path.local_ip != network_path.local_ip
+2236                && !local_ip_may_migrate
+2237            {
+2238                trace!(
+2239                    %path_id,
+2240                    %network_path,
+2241                    %known_path.network_path,
+2242                    "discarding packet sent to incorrect interface"
+2243                );
+2244                return true;
+2245            }
+2246            // If the datagram indicates that we've changed our local IP, we update it.
+2247            // This is alluded to in Section 5.2 of the Multipath RFC draft 18:
+2248            // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#name-using-multiple-paths-on-the
+2249            // > Client receives the packet, recognizes a path migration, updates the source address of path 2 to 192.0.2.1.
+2250            if let Some(local_ip) = network_path.local_ip {
+2251                if known_path
+2252                    .network_path
+2253                    .local_ip
+2254                    .is_some_and(|ip| ip != local_ip)
+2255                {
+2256                    debug!(
+2257                        %path_id,
+2258                        %network_path,
+2259                        %known_path.network_path,
+2260                        "path's local address seemingly migrated"
+2261                    );
+2262                }
+2263                // We update the address without path validation on the client side.
+2264                // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#section-5.1
+2265                // > Servers observing a 4-tuple change will perform path validation (see Section 9 of [QUIC-TRANSPORT]).
+2266                // This sounds like it's *only* the server endpoints that do this.
+2267                // TODO(matheus23): We should still consider doing a proper migration on the client side in the future.
+2268                // For now, this preserves the behavior of this code pre 4-tuple tracking.
+2269                known_path.network_path.local_ip = Some(local_ip);
+2270            }
+2271        }
+2272        false
+2273    }
+2274
+2275    /// Process timer expirations
+2276    ///
+2277    /// Executes protocol logic, potentially preparing signals (including application `Event`s,
+2278    /// `EndpointEvent`s and outgoing datagrams) that should be extracted through the relevant
+2279    /// methods.
+2280    ///
+2281    /// It is most efficient to call this immediately after the system clock reaches the latest
+2282    /// `Instant` that was output by `poll_timeout`; however spurious extra calls will simply
+2283    /// no-op and therefore are safe.
+2284    pub fn handle_timeout(&mut self, now: Instant) {
+2285        while let Some((timer, _time)) = self.timers.expire_before(now, &self.qlog) {
+2286            // TODO(@divma): remove `at` when the unicorn is born
+2287            trace!(?timer, at=?now, "timeout");
+2288            match timer {
+2289                Timer::Conn(timer) => match timer {
+2290                    ConnTimer::Close => {
+2291                        self.state.move_to_drained(None);
+2292                        // move_to_drained checks that we weren't in drained before.
+2293                        // Adding events to endpoint_events is only legal if `Drained` was never queued before.
+2294                        self.endpoint_events.push_back(EndpointEventInner::Drained);
+2295                    }
+2296                    ConnTimer::Idle => {
+2297                        self.kill(ConnectionError::TimedOut);
+2298                    }
+2299                    ConnTimer::KeepAlive => {
+2300                        trace!("sending keep-alive");
+2301                        self.ping();
+2302                    }
+2303                    ConnTimer::KeyDiscard => {
+2304                        self.crypto_state.discard_temporary_keys();
+2305                    }
+2306                    ConnTimer::PushNewCid => {
+2307                        while let Some((path_id, when)) = self.next_cid_retirement() {
+2308                            if when > now {
+2309                                break;
+2310                            }
+2311                            match self.local_cid_state.get_mut(&path_id) {
+2312                                None => error!(%path_id, "No local CID state for path"),
+2313                                Some(cid_state) => {
+2314                                    // Update `retire_prior_to` field in NEW_CONNECTION_ID frame
+2315                                    let num_new_cid = cid_state.on_cid_timeout().into();
+2316                                    if !self.state.is_closed() {
+2317                                        trace!(
+2318                                            "push a new CID to peer RETIRE_PRIOR_TO field {}",
+2319                                            cid_state.retire_prior_to()
+2320                                        );
+2321                                        self.endpoint_events.push_back(
+2322                                            EndpointEventInner::NeedIdentifiers(
+2323                                                path_id,
+2324                                                now,
+2325                                                num_new_cid,
+2326                                            ),
+2327                                        );
+2328                                    }
+2329                                }
+2330                            }
+2331                        }
+2332                    }
+2333                },
+2334                // TODO: add path_id as span somehow
+2335                Timer::PerPath(path_id, timer) => {
+2336                    let span = trace_span!("per-path timer fired", %path_id, ?timer);
+2337                    let _guard = span.enter();
+2338                    match timer {
+2339                        PathTimer::PathIdle => {
+2340                            if let Err(err) =
+2341                                self.close_path_inner(now, path_id, PathAbandonReason::TimedOut)
+2342                            {
+2343                                warn!(?err, "failed closing path");
+2344                            }
+2345                        }
+2346
+2347                        PathTimer::PathKeepAlive => {
+2348                            trace!("sending keep-alive on path");
+2349                            self.ping_path(path_id).ok();
+2350                        }
+2351                        PathTimer::LossDetection => {
+2352                            self.on_loss_detection_timeout(now, path_id);
+2353                            self.qlog.emit_recovery_metrics(
+2354                                path_id,
+2355                                &mut self.paths.get_mut(&path_id).unwrap().data,
+2356                                now,
+2357                            );
+2358                        }
+2359                        PathTimer::PathValidation => {
+2360                            let Some(path) = self.paths.get_mut(&path_id) else {
+2361                                continue;
+2362                            };
+2363                            self.timers.stop(
+2364                                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
+2365                                self.qlog.with_time(now),
+2366                            );
+2367                            debug!("path validation failed");
+2368                            if let Some((_, prev)) = path.prev.take() {
+2369                                path.data = prev;
+2370                            }
+2371                            path.data.reset_on_path_challenges();
+2372                        }
+2373                        PathTimer::PathChallengeLost => {
+2374                            let Some(path) = self.paths.get_mut(&path_id) else {
+2375                                continue;
+2376                            };
+2377                            trace!("path challenge deemed lost");
+2378                            path.data.pending_on_path_challenge = true;
+2379                        }
+2380                        PathTimer::PathOpen => {
+2381                            let Some(path) = self.paths.get_mut(&path_id) else {
+2382                                continue;
+2383                            };
+2384                            path.data.reset_on_path_challenges();
+2385                            self.timers.stop(
+2386                                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
+2387                                self.qlog.with_time(now),
+2388                            );
+2389                            debug!("new path validation failed");
+2390                            if let Err(err) = self.close_path_inner(
+2391                                now,
+2392                                path_id,
+2393                                PathAbandonReason::ValidationFailed,
+2394                            ) {
+2395                                warn!(?err, "failed closing path");
+2396                            }
+2397                        }
+2398                        PathTimer::Pacing => trace!("pacing timer expired"),
+2399                        PathTimer::MaxAckDelay => {
+2400                            trace!("max ack delay reached");
+2401                            // This timer is only armed in the Data space
+2402                            self.spaces[SpaceId::Data]
+2403                                .for_path(path_id)
+2404                                .pending_acks
+2405                                .on_max_ack_delay_timeout()
+2406                        }
+2407                        PathTimer::DiscardPath => {
+2408                            // The path was abandoned and 3*PTO has expired since.  Clean up all
+2409                            // remaining state and install stateless reset token.
+2410                            self.timers.stop_per_path(path_id, self.qlog.with_time(now));
+2411                            if let Some(local_cid_state) = self.local_cid_state.remove(&path_id) {
+2412                                debug_assert!(!self.state.is_drained()); // requirement for endpoint_events. All timers should be cleared in drained connections.
+2413                                let (min_seq, max_seq) = local_cid_state.active_seq();
+2414                                for seq in min_seq..=max_seq {
+2415                                    self.endpoint_events.push_back(
+2416                                        EndpointEventInner::RetireConnectionId(
+2417                                            now, path_id, seq, false,
+2418                                        ),
+2419                                    );
+2420                                }
+2421                            }
+2422                            self.discard_path(path_id, now);
+2423                        }
+2424                    }
+2425                }
+2426            }
+2427        }
+2428    }
+2429
+2430    /// Close a connection immediately
+2431    ///
+2432    /// This does not ensure delivery of outstanding data. It is the application's responsibility to
+2433    /// call this only when all important communications have been completed, e.g. by calling
+2434    /// [`SendStream::finish`] on outstanding streams and waiting for the corresponding
+2435    /// [`StreamEvent::Finished`] event.
+2436    ///
+2437    /// If [`Streams::send_streams`] returns 0, all outstanding stream data has been
+2438    /// delivered. There may still be data from the peer that has not been received.
+2439    ///
+2440    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+2441    pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes) {
+2442        self.close_inner(
+2443            now,
+2444            Close::Application(frame::ApplicationClose { error_code, reason }),
+2445        )
+2446    }
+2447
+2448    /// Close the connection immediately, initiated by an API call.
+2449    ///
+2450    /// This will not produce a [`ConnectionLost`] event propagated by the
+2451    /// [`Connection::poll`] call, because the API call already propagated the error to the
+2452    /// user.
+2453    ///
+2454    /// Not to be used when entering immediate close due to an internal state change based
+2455    /// on an event. See [`State::move_to_closed_local`] for details.
+2456    ///
+2457    /// This initiates immediate close from
+2458    /// <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2>, moving to the closed
+2459    /// state.
+2460    ///
+2461    /// [`ConnectionLost`]: crate::Event::ConnectionLost
+2462    /// [`Connection::poll`]: super::Connection::poll
+2463    fn close_inner(&mut self, now: Instant, reason: Close) {
+2464        let was_closed = self.state.is_closed();
+2465        if !was_closed {
+2466            self.close_common();
+2467            self.set_close_timer(now);
+2468            self.connection_close_pending = true;
+2469            self.state.move_to_closed_local(reason);
+2470        }
+2471    }
+2472
+2473    /// Control datagrams
+2474    pub fn datagrams(&mut self) -> Datagrams<'_> {
+2475        Datagrams { conn: self }
+2476    }
+2477
+2478    /// Returns connection statistics
+2479    pub fn stats(&mut self) -> ConnectionStats {
+2480        self.stats.clone()
+2481    }
+2482
+2483    /// Returns path statistics
+2484    pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats> {
+2485        let path = self.paths.get(&path_id)?;
+2486        let stats = self.path_stats.entry(path_id).or_default();
+2487        stats.rtt = path.data.rtt.get();
+2488        stats.cwnd = path.data.congestion.window();
+2489        stats.current_mtu = path.data.mtud.current_mtu();
+2490        Some(*stats)
+2491    }
+2492
+2493    /// Ping the remote endpoint
+2494    ///
+2495    /// Causes an ACK-eliciting packet to be transmitted on the connection.
+2496    pub fn ping(&mut self) {
+2497        // TODO(flub): This is very brute-force: it pings *all* the paths.  Instead it would
+2498        //    be nice if we could only send a single packet for this.
+2499        for path_data in self.spaces[self.highest_space].number_spaces.values_mut() {
+2500            path_data.ping_pending = true;
+2501        }
+2502    }
+2503
+2504    /// Ping the remote endpoint over a specific path
+2505    ///
+2506    /// Causes an ACK-eliciting packet to be transmitted on the path.
+2507    pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath> {
+2508        let path_data = self.spaces[self.highest_space]
+2509            .number_spaces
+2510            .get_mut(&path)
+2511            .ok_or(ClosedPath { _private: () })?;
+2512        path_data.ping_pending = true;
+2513        Ok(())
+2514    }
+2515
+2516    /// Update traffic keys spontaneously
+2517    ///
+2518    /// This can be useful for testing key updates, as they otherwise only happen infrequently.
+2519    pub fn force_key_update(&mut self) {
+2520        if !self.state.is_established() {
+2521            debug!("ignoring forced key update in illegal state");
+2522            return;
+2523        }
+2524        if self.crypto_state.prev_crypto.is_some() {
+2525            // We already just updated, or are currently updating, the keys. Concurrent key updates
+2526            // are illegal.
+2527            debug!("ignoring redundant forced key update");
+2528            return;
+2529        }
+2530        self.crypto_state.update_keys(None, false);
+2531    }
+2532
+2533    /// Get a session reference
+2534    pub fn crypto_session(&self) -> &dyn crypto::Session {
+2535        self.crypto_state.session.as_ref()
+2536    }
+2537
+2538    /// Whether the connection is in the process of being established
+2539    ///
+2540    /// If this returns `false`, the connection may be either established or closed, signaled by the
+2541    /// emission of a `Connected` or `ConnectionLost` message respectively.
+2542    pub fn is_handshaking(&self) -> bool {
+2543        self.state.is_handshake()
+2544    }
+2545
+2546    /// Whether the connection is closed
+2547    ///
+2548    /// Closed connections cannot transport any further data. A connection becomes closed when
+2549    /// either peer application intentionally closes it, or when either transport layer detects an
+2550    /// error such as a time-out or certificate validation failure.
+2551    ///
+2552    /// A `ConnectionLost` event is emitted with details when the connection becomes closed.
+2553    pub fn is_closed(&self) -> bool {
+2554        self.state.is_closed()
+2555    }
+2556
+2557    /// Whether there is no longer any need to keep the connection around
+2558    ///
+2559    /// Closed connections become drained after a brief timeout to absorb any remaining in-flight
+2560    /// packets from the peer. All drained connections have been closed.
+2561    pub fn is_drained(&self) -> bool {
+2562        self.state.is_drained()
+2563    }
+2564
+2565    /// For clients, if the peer accepted the 0-RTT data packets
+2566    ///
+2567    /// The value is meaningless until after the handshake completes.
+2568    pub fn accepted_0rtt(&self) -> bool {
+2569        self.crypto_state.accepted_0rtt
+2570    }
+2571
+2572    /// Whether 0-RTT is/was possible during the handshake
+2573    pub fn has_0rtt(&self) -> bool {
+2574        self.crypto_state.zero_rtt_enabled
+2575    }
+2576
+2577    /// Whether there are any pending retransmits
+2578    pub fn has_pending_retransmits(&self) -> bool {
+2579        !self.spaces[SpaceId::Data].pending.is_empty(&self.streams)
+2580    }
+2581
+2582    /// Look up whether we're the client or server of this Connection
+2583    pub fn side(&self) -> Side {
+2584        self.side.side()
+2585    }
+2586
+2587    /// Get the address observed by the remote over the given path
+2588    pub fn path_observed_address(&self, path_id: PathId) -> Result<Option<SocketAddr>, ClosedPath> {
+2589        self.path(path_id)
+2590            .map(|path_data| {
+2591                path_data
+2592                    .last_observed_addr_report
+2593                    .as_ref()
+2594                    .map(|observed| observed.socket_addr())
+2595            })
+2596            .ok_or(ClosedPath { _private: () })
+2597    }
+2598
+2599    /// Current best estimate of this connection's latency (round-trip-time)
+2600    pub fn rtt(&self, path_id: PathId) -> Option<Duration> {
+2601        self.path(path_id).map(|d| d.rtt.get())
+2602    }
+2603
+2604    /// Current state of this connection's congestion controller, for debugging purposes
+2605    pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller> {
+2606        self.path(path_id).map(|d| d.congestion.as_ref())
+2607    }
+2608
+2609    /// Modify the number of remotely initiated streams that may be concurrently open
+2610    ///
+2611    /// No streams may be opened by the peer unless fewer than `count` are already open. Large
+2612    /// `count`s increase both minimum and worst-case memory consumption.
+2613    pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt) {
+2614        self.streams.set_max_concurrent(dir, count);
+2615        // If the limit was reduced, then a flow control update previously deemed insignificant may
+2616        // now be significant.
+2617        let pending = &mut self.spaces[SpaceId::Data].pending;
+2618        self.streams.queue_max_stream_id(pending);
+2619    }
+2620
+2621    /// Modify the number of open paths allowed when multipath is enabled
+2622    ///
+2623    /// When reducing the number of concurrent paths this will only affect delaying sending
+2624    /// new MAX_PATH_ID frames until fewer than this number of paths are possible.  To
+2625    /// actively reduce paths they must be closed using [`Connection::close_path`], which
+2626    /// can also be used to close not-yet-opened paths.
+2627    ///
+2628    /// If multipath is not negotiated (see the [`TransportConfig`]) this can not enable
+2629    /// multipath and will fail.
+2630    pub fn set_max_concurrent_paths(
+2631        &mut self,
+2632        now: Instant,
+2633        count: NonZeroU32,
+2634    ) -> Result<(), MultipathNotNegotiated> {
+2635        if !self.is_multipath_negotiated() {
+2636            return Err(MultipathNotNegotiated { _private: () });
+2637        }
+2638        self.max_concurrent_paths = count;
+2639
+2640        let in_use_count = self
+2641            .local_max_path_id
+2642            .next()
+2643            .saturating_sub(self.abandoned_paths.len() as u32)
+2644            .as_u32();
+2645        let extra_needed = count.get().saturating_sub(in_use_count);
+2646        let new_max_path_id = self.local_max_path_id.saturating_add(extra_needed);
+2647
+2648        self.set_max_path_id(now, new_max_path_id);
+2649
+2650        Ok(())
+2651    }
+2652
+2653    /// If needed, issues a new MAX_PATH_ID frame and new CIDs for any newly allowed paths
+2654    fn set_max_path_id(&mut self, now: Instant, max_path_id: PathId) {
+2655        if max_path_id <= self.local_max_path_id {
+2656            return;
+2657        }
+2658
+2659        self.local_max_path_id = max_path_id;
+2660        self.spaces[SpaceId::Data].pending.max_path_id = true;
+2661
+2662        self.issue_first_path_cids(now);
+2663    }
+2664
+2665    /// Current number of remotely initiated streams that may be concurrently open
+2666    ///
+2667    /// If the target for this limit is reduced using [`set_max_concurrent_streams`](Self::set_max_concurrent_streams),
+2668    /// it will not change immediately, even if fewer streams are open. Instead, it will
+2669    /// decrement by one for each time a remotely initiated stream of matching directionality is closed.
+2670    pub fn max_concurrent_streams(&self, dir: Dir) -> u64 {
+2671        self.streams.max_concurrent(dir)
+2672    }
+2673
+2674    /// See [`TransportConfig::send_window()`]
+2675    pub fn set_send_window(&mut self, send_window: u64) {
+2676        self.streams.set_send_window(send_window);
+2677    }
+2678
+2679    /// See [`TransportConfig::receive_window()`]
+2680    pub fn set_receive_window(&mut self, receive_window: VarInt) {
+2681        if self.streams.set_receive_window(receive_window) {
+2682            self.spaces[SpaceId::Data].pending.max_data = true;
+2683        }
+2684    }
+2685
+2686    /// Whether the Multipath for QUIC extension is enabled.
+2687    ///
+2688    /// Multipath is only enabled after the handshake is completed and if it was enabled by both
+2689    /// peers.
+2690    pub fn is_multipath_negotiated(&self) -> bool {
+2691        !self.is_handshaking()
+2692            && self.config.max_concurrent_multipath_paths.is_some()
+2693            && self.peer_params.initial_max_path_id.is_some()
+2694    }
+2695
+2696    fn on_ack_received(
+2697        &mut self,
+2698        now: Instant,
+2699        space: SpaceId,
+2700        ack: frame::Ack,
+2701    ) -> Result<(), TransportError> {
+2702        // All ACKs are referencing path 0
+2703        let path = PathId::ZERO;
+2704        self.inner_on_ack_received(now, space, path, ack)
+2705    }
+2706
+2707    fn on_path_ack_received(
+2708        &mut self,
+2709        now: Instant,
+2710        space: SpaceId,
+2711        path_ack: frame::PathAck,
+2712    ) -> Result<(), TransportError> {
+2713        let (ack, path) = path_ack.into_ack();
+2714        self.inner_on_ack_received(now, space, path, ack)
+2715    }
+2716
+2717    /// Handles an ACK frame acknowledging packets sent on *path*.
+2718    fn inner_on_ack_received(
+2719        &mut self,
+2720        now: Instant,
+2721        space: SpaceId,
+2722        path: PathId,
+2723        ack: frame::Ack,
+2724    ) -> Result<(), TransportError> {
+2725        if self.abandoned_paths.contains(&path) {
+2726            // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.4.3-3
+2727            // > PATH_ACK frames received with an abandoned path ID are silently ignored, as specified in Section 4.
+2728            trace!("silently ignoring PATH_ACK on abandoned path");
+2729            return Ok(());
+2730        }
+2731        if ack.largest >= self.spaces[space].for_path(path).next_packet_number {
+2732            return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked"));
+2733        }
+2734        let new_largest = {
+2735            let space = &mut self.spaces[space].for_path(path);
+2736            if space.largest_acked_packet.is_none_or(|pn| ack.largest > pn) {
+2737                space.largest_acked_packet = Some(ack.largest);
+2738                if let Some(info) = space.sent_packets.get(ack.largest) {
+2739                    // This should always succeed, but a misbehaving peer might ACK a packet we
+2740                    // haven't sent. At worst, that will result in us spuriously reducing the
+2741                    // congestion window.
+2742                    space.largest_acked_packet_sent = info.time_sent;
+2743                }
+2744                true
+2745            } else {
+2746                false
+2747            }
+2748        };
+2749
+2750        if self.detect_spurious_loss(&ack, space, path) {
+2751            self.path_data_mut(path)
+2752                .congestion
+2753                .on_spurious_congestion_event();
+2754        }
+2755
+2756        // Avoid DoS from unreasonably huge ack ranges by filtering out just the new acks.
+2757        let mut newly_acked = ArrayRangeSet::new();
+2758        for range in ack.iter() {
+2759            self.spaces[space].for_path(path).check_ack(range.clone())?;
+2760            for (pn, _) in self.spaces[space]
+2761                .for_path(path)
+2762                .sent_packets
+2763                .iter_range(range)
+2764            {
+2765                newly_acked.insert_one(pn);
+2766            }
+2767        }
+2768
+2769        if newly_acked.is_empty() {
+2770            return Ok(());
+2771        }
+2772
+2773        let mut ack_eliciting_acked = false;
+2774        for packet in newly_acked.elts() {
+2775            if let Some(info) = self.spaces[space].for_path(path).take(packet) {
+2776                for (acked_path_id, acked_pn) in info.largest_acked.iter() {
+2777                    // Assume ACKs for all packets below the largest acknowledged in
+2778                    // `packet` have been received. This can cause the peer to spuriously
+2779                    // retransmit if some of our earlier ACKs were lost, but allows for
+2780                    // simpler state tracking. See discussion at
+2781                    // https://www.rfc-editor.org/rfc/rfc9000.html#name-limiting-ranges-by-tracking
+2782                    if let Some(pns) = self.spaces[space].path_space_mut(*acked_path_id) {
+2783                        pns.pending_acks.subtract_below(*acked_pn);
+2784                    }
+2785                }
+2786                ack_eliciting_acked |= info.ack_eliciting;
+2787
+2788                // Notify MTU discovery that a packet was acked, because it might be an MTU probe
+2789                let path_data = self.path_data_mut(path);
+2790                let mtu_updated = path_data.mtud.on_acked(space.kind(), packet, info.size);
+2791                if mtu_updated {
+2792                    path_data
+2793                        .congestion
+2794                        .on_mtu_update(path_data.mtud.current_mtu());
+2795                }
+2796
+2797                // Notify ack frequency that a packet was acked, because it might contain an ACK_FREQUENCY frame
+2798                self.ack_frequency.on_acked(path, packet);
+2799
+2800                self.on_packet_acked(now, path, info);
+2801            }
+2802        }
+2803
+2804        let largest_ackd = self.spaces[space].for_path(path).largest_acked_packet;
+2805        let app_limited = self.app_limited;
+2806        let path_data = self.path_data_mut(path);
+2807        let in_flight = path_data.in_flight.bytes;
+2808
+2809        path_data
+2810            .congestion
+2811            .on_end_acks(now, in_flight, app_limited, largest_ackd);
+2812
+2813        if new_largest && ack_eliciting_acked {
+2814            let ack_delay = if space != SpaceId::Data {
+2815                Duration::from_micros(0)
+2816            } else {
+2817                cmp::min(
+2818                    self.ack_frequency.peer_max_ack_delay,
+2819                    Duration::from_micros(ack.delay << self.peer_params.ack_delay_exponent.0),
+2820                )
+2821            };
+2822            let rtt = now.saturating_duration_since(
+2823                self.spaces[space].for_path(path).largest_acked_packet_sent,
+2824            );
+2825
+2826            let next_pn = self.spaces[space].for_path(path).next_packet_number;
+2827            let path_data = self.path_data_mut(path);
+2828            // TODO(@divma): should be a method of path, should be contained in a single place
+2829            path_data.rtt.update(ack_delay, rtt);
+2830            if path_data.first_packet_after_rtt_sample.is_none() {
+2831                path_data.first_packet_after_rtt_sample = Some((space.kind(), next_pn));
+2832            }
+2833        }
+2834
+2835        // Must be called before crypto/pto_count are clobbered
+2836        self.detect_lost_packets(now, space, path, true);
+2837
+2838        if self.peer_completed_address_validation(path) {
+2839            self.path_data_mut(path).pto_count = 0;
+2840        }
+2841
+2842        // Explicit congestion notification
+2843        // TODO(@divma): this code is a good example of logic that should be contained in a single
+2844        // place but it's split between the path data and the packet number space data, we should
+2845        // find a way to make this work without two lookups
+2846        if self.path_data(path).sending_ecn {
+2847            if let Some(ecn) = ack.ecn {
+2848                // We only examine ECN counters from ACKs that we are certain we received in transmit
+2849                // order, allowing us to compute an increase in ECN counts to compare against the number
+2850                // of newly acked packets that remains well-defined in the presence of arbitrary packet
+2851                // reordering.
+2852                if new_largest {
+2853                    let sent = self.spaces[space].for_path(path).largest_acked_packet_sent;
+2854                    self.process_ecn(now, space, path, newly_acked.len() as u64, ecn, sent);
+2855                }
+2856            } else {
+2857                // We always start out sending ECN, so any ack that doesn't acknowledge it disables it.
+2858                debug!("ECN not acknowledged by peer");
+2859                self.path_data_mut(path).sending_ecn = false;
+2860            }
+2861        }
+2862
+2863        self.set_loss_detection_timer(now, path);
+2864        Ok(())
+2865    }
+2866
+2867    fn detect_spurious_loss(&mut self, ack: &frame::Ack, space: SpaceId, path: PathId) -> bool {
+2868        let lost_packets = &mut self.spaces[space].for_path(path).lost_packets;
+2869
+2870        if lost_packets.is_empty() {
+2871            return false;
+2872        }
+2873
+2874        for range in ack.iter() {
+2875            let spurious_losses: Vec<u64> = lost_packets
+2876                .iter_range(range.clone())
+2877                .map(|(pn, _info)| pn)
+2878                .collect();
+2879
+2880            for pn in spurious_losses {
+2881                lost_packets.remove(pn);
+2882            }
+2883        }
+2884
+2885        // If this ACK frame acknowledged all deemed lost packets,
+2886        // then we have raised a spurious congestion event in the past.
+2887        // We cannot conclude when there are remaining packets,
+2888        // but future ACK frames might indicate a spurious loss detection.
+2889        lost_packets.is_empty()
+2890    }
+2891
+2892    /// Drain lost packets that we reasonably think will never arrive
+2893    ///
+2894    /// The current criterion is copied from `msquic`:
+2895    /// discard packets that were sent earlier than 2 probe timeouts ago.
+2896    fn drain_lost_packets(&mut self, now: Instant, space: SpaceId, path: PathId) {
+2897        let two_pto = 2 * self.path_data(path).rtt.pto_base();
+2898
+2899        let lost_packets = &mut self.spaces[space].for_path(path).lost_packets;
+2900        lost_packets.retain(|_pn, info| now.saturating_duration_since(info.time_sent) <= two_pto);
+2901    }
+2902
+2903    /// Process a new ECN block from an in-order ACK
+2904    fn process_ecn(
+2905        &mut self,
+2906        now: Instant,
+2907        space: SpaceId,
+2908        path: PathId,
+2909        newly_acked: u64,
+2910        ecn: frame::EcnCounts,
+2911        largest_sent_time: Instant,
+2912    ) {
+2913        match self.spaces[space]
+2914            .for_path(path)
+2915            .detect_ecn(newly_acked, ecn)
+2916        {
+2917            Err(e) => {
+2918                debug!("halting ECN due to verification failure: {}", e);
+2919
+2920                self.path_data_mut(path).sending_ecn = false;
+2921                // Wipe out the existing value because it might be garbage and could interfere with
+2922                // future attempts to use ECN on new paths.
+2923                self.spaces[space].for_path(path).ecn_feedback = frame::EcnCounts::ZERO;
+2924            }
+2925            Ok(false) => {}
+2926            Ok(true) => {
+2927                self.path_stats.entry(path).or_default().congestion_events += 1;
+2928                self.path_data_mut(path).congestion.on_congestion_event(
+2929                    now,
+2930                    largest_sent_time,
+2931                    false,
+2932                    true,
+2933                    0,
+2934                );
+2935            }
+2936        }
+2937    }
+2938
+2939    // Not timing-aware, so it's safe to call this for inferred acks, such as arise from
+2940    // high-latency handshakes
+2941    fn on_packet_acked(&mut self, now: Instant, path_id: PathId, info: SentPacket) {
+2942        self.paths
+2943            .get_mut(&path_id)
+2944            .expect("known path")
+2945            .remove_in_flight(&info);
+2946        let app_limited = self.app_limited;
+2947        let path = self.path_data_mut(path_id);
+2948        if info.ack_eliciting && !path.is_validating_path() {
+2949            // Only pass ACKs to the congestion controller if we are not validating the current
+2950            // path, so as to ignore any ACKs from older paths still coming in.
+2951            let rtt = path.rtt;
+2952            path.congestion
+2953                .on_ack(now, info.time_sent, info.size.into(), app_limited, &rtt);
+2954        }
+2955
+2956        // Update state for confirmed delivery of frames
+2957        if let Some(retransmits) = info.retransmits.get() {
+2958            for (id, _) in retransmits.reset_stream.iter() {
+2959                self.streams.reset_acked(*id);
+2960            }
+2961        }
+2962
+2963        for frame in info.stream_frames {
+2964            self.streams.received_ack_of(frame);
+2965        }
+2966    }
+2967
+2968    fn set_key_discard_timer(&mut self, now: Instant, space: SpaceKind) {
+2969        let start = if self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) {
+2970            now
+2971        } else {
+2972            self.crypto_state
+2973                .prev_crypto
+2974                .as_ref()
+2975                .expect("no previous keys")
+2976                .end_packet
+2977                .as_ref()
+2978                .expect("update not acknowledged yet")
+2979                .1
+2980        };
+2981
+2982        // QUIC-MULTIPATH § 2.5 Key Phase Update Process: use largest PTO of all paths.
+2983        self.timers.set(
+2984            Timer::Conn(ConnTimer::KeyDiscard),
+2985            start + self.max_pto_for_space(space) * 3,
+2986            self.qlog.with_time(now),
+2987        );
+2988    }
+2989
+2990    /// Handle a [`PathTimer::LossDetection`] timeout.
+2991    ///
+2992    /// This timer expires for two reasons:
+2993    /// - An ACK-eliciting packet we sent should be considered lost.
+2994    /// - The PTO may have expired and a tail-loss probe needs to be scheduled.
+2995    ///
+2996    /// The former needs us to schedule re-transmission of the lost data.
+2997    ///
+2998    /// The latter means we have not received an ACK for an ack-eliciting packet we sent
+2999    /// within the PTO time-window. We need to schedule a tail-loss probe, an ack-eliciting
+3000    /// packet, to try and elicit new acknowledgements. These new acknowledgements will
+3001    /// indicate whether the previously sent packets were lost or not.
+3002    fn on_loss_detection_timeout(&mut self, now: Instant, path_id: PathId) {
+3003        if let Some((_, pn_space)) = self.loss_time_and_space(path_id) {
+3004            // Time threshold loss Detection
+3005            self.detect_lost_packets(now, pn_space, path_id, false);
+3006            self.set_loss_detection_timer(now, path_id);
+3007            return;
+3008        }
+3009
+3010        let Some((_, space)) = self.pto_time_and_space(now, path_id) else {
+3011            error!(%path_id, "PTO expired while unset");
+3012            return;
+3013        };
+3014        trace!(
+3015            in_flight = self.path_data(path_id).in_flight.bytes,
+3016            count = self.path_data(path_id).pto_count,
+3017            ?space,
+3018            %path_id,
+3019            "PTO fired"
+3020        );
+3021
+3022        let count = match self.path_data(path_id).in_flight.ack_eliciting {
+3023            // A PTO when we're not expecting any ACKs must be due to handshake anti-amplification
+3024            // deadlock preventions
+3025            0 => {
+3026                debug_assert!(!self.peer_completed_address_validation(path_id));
+3027                1
+3028            }
+3029            // Conventional loss probe
+3030            _ => 2,
+3031        };
+3032        let pns = self.spaces[space].for_path(path_id);
+3033        pns.loss_probes = pns.loss_probes.saturating_add(count);
+3034        let path_data = self.path_data_mut(path_id);
+3035        path_data.pto_count = path_data.pto_count.saturating_add(1);
+3036        self.set_loss_detection_timer(now, path_id);
+3037    }
+3038
+3039    /// Detect any lost packets
+3040    ///
+3041    /// There are two cases in which we detects lost packets:
+3042    ///
+3043    /// - We received an ACK packet.
+3044    /// - The [`PathTimer::LossDetection`] timer expired. So there is an un-acknowledged packet
+3045    ///   that was followed by an acknowledged packet. The loss timer for this
+3046    ///   un-acknowledged packet expired and we need to detect that packet as lost.
+3047    ///
+3048    /// Packets are lost if they are both (See RFC9002 §6.1):
+3049    ///
+3050    /// - Unacknowledged, in flight and sent prior to an acknowledged packet.
+3051    /// - Old enough by either:
+3052    ///   - Having a packet number [`TransportConfig::packet_threshold`] lower then the last
+3053    ///     acknowledged packet.
+3054    ///   - Being sent [`TransportConfig::time_threshold`] * RTT in the past.
+3055    fn detect_lost_packets(
+3056        &mut self,
+3057        now: Instant,
+3058        pn_space: SpaceId,
+3059        path_id: PathId,
+3060        due_to_ack: bool,
+3061    ) {
+3062        let mut lost_packets = Vec::<u64>::new();
+3063        let mut lost_mtu_probe = None;
+3064        let mut in_persistent_congestion = false;
+3065        let mut size_of_lost_packets = 0u64;
+3066        self.spaces[pn_space].for_path(path_id).loss_time = None;
+3067
+3068        // Find all the lost packets, populating all variables initialised above.
+3069
+3070        let path = self.path_data(path_id);
+3071        let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe();
+3072        let loss_delay = path
+3073            .rtt
+3074            .conservative()
+3075            .mul_f32(self.config.time_threshold)
+3076            .max(TIMER_GRANULARITY);
+3077        let first_packet_after_rtt_sample = path.first_packet_after_rtt_sample;
+3078
+3079        let largest_acked_packet = self.spaces[pn_space]
+3080            .for_path(path_id)
+3081            .largest_acked_packet
+3082            .expect("detect_lost_packets only to be called if path received at least one ACK");
+3083        let packet_threshold = self.config.packet_threshold as u64;
+3084
+3085        // InPersistentCongestion: Determine if all packets in the time period before the newest
+3086        // lost packet, including the edges, are marked lost. PTO computation must always
+3087        // include max ACK delay, i.e. operate as if in Data space (see RFC9001 §7.6.1).
+3088        let congestion_period = self
+3089            .pto(SpaceKind::Data, path_id)
+3090            .saturating_mul(self.config.persistent_congestion_threshold);
+3091        let mut persistent_congestion_start: Option<Instant> = None;
+3092        let mut prev_packet = None;
+3093        let space = self.spaces[pn_space].for_path(path_id);
+3094
+3095        for (packet, info) in space.sent_packets.iter_range(0..largest_acked_packet) {
+3096            if prev_packet != Some(packet.wrapping_sub(1)) {
+3097                // An intervening packet was acknowledged
+3098                persistent_congestion_start = None;
+3099            }
+3100
+3101            // Packets sent before now - loss_delay are deemed lost.
+3102            // However, we avoid subtraction as it can panic and there's no
+3103            // saturating equivalent of this subtraction operation with a Duration.
+3104            let packet_too_old = now.saturating_duration_since(info.time_sent) >= loss_delay;
+3105            if packet_too_old || largest_acked_packet >= packet + packet_threshold {
+3106                // The packet should be declared lost.
+3107                if Some(packet) == in_flight_mtu_probe {
+3108                    // Lost MTU probes are not included in `lost_packets`, because they
+3109                    // should not trigger a congestion control response
+3110                    lost_mtu_probe = in_flight_mtu_probe;
+3111                } else {
+3112                    lost_packets.push(packet);
+3113                    size_of_lost_packets += info.size as u64;
+3114                    if info.ack_eliciting && due_to_ack {
+3115                        match persistent_congestion_start {
+3116                            // Two ACK-eliciting packets lost more than
+3117                            // congestion_period apart, with no ACKed packets in between
+3118                            Some(start) if info.time_sent - start > congestion_period => {
+3119                                in_persistent_congestion = true;
+3120                            }
+3121                            // Persistent congestion must start after the first RTT sample
+3122                            None if first_packet_after_rtt_sample
+3123                                .is_some_and(|x| x < (pn_space.kind(), packet)) =>
+3124                            {
+3125                                persistent_congestion_start = Some(info.time_sent);
+3126                            }
+3127                            _ => {}
+3128                        }
+3129                    }
+3130                }
+3131            } else {
+3132                // The packet should not yet be declared lost.
+3133                if space.loss_time.is_none() {
+3134                    // Since we iterate in order the lowest packet number's loss time will
+3135                    // always be the earliest.
+3136                    space.loss_time = Some(info.time_sent + loss_delay);
+3137                }
+3138                persistent_congestion_start = None;
+3139            }
+3140
+3141            prev_packet = Some(packet);
+3142        }
+3143
+3144        self.handle_lost_packets(
+3145            pn_space,
+3146            path_id,
+3147            now,
+3148            lost_packets,
+3149            lost_mtu_probe,
+3150            loss_delay,
+3151            in_persistent_congestion,
+3152            size_of_lost_packets,
+3153        );
+3154    }
+3155
+3156    /// Drops the path state, declaring any remaining in-flight packets as lost
+3157    fn discard_path(&mut self, path_id: PathId, now: Instant) {
+3158        trace!(%path_id, "dropping path state");
+3159        let path = self.path_data(path_id);
+3160        let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe();
+3161
+3162        let mut size_of_lost_packets = 0u64; // add to path_stats.lost_bytes;
+3163        let lost_pns: Vec<_> = self.spaces[SpaceId::Data]
+3164            .for_path(path_id)
+3165            .sent_packets
+3166            .iter()
+3167            .filter(|(pn, _info)| Some(*pn) != in_flight_mtu_probe)
+3168            .map(|(pn, info)| {
+3169                size_of_lost_packets += info.size as u64;
+3170                pn
+3171            })
+3172            .collect();
+3173
+3174        if !lost_pns.is_empty() {
+3175            trace!(
+3176                %path_id,
+3177                count = lost_pns.len(),
+3178                lost_bytes = size_of_lost_packets,
+3179                "packets lost on path abandon"
+3180            );
+3181            self.handle_lost_packets(
+3182                SpaceId::Data,
+3183                path_id,
+3184                now,
+3185                lost_pns,
+3186                in_flight_mtu_probe,
+3187                Duration::ZERO,
+3188                false,
+3189                size_of_lost_packets,
+3190            );
+3191        }
+3192        // Before removing the path, we fetch the final path stats via `Self::path_stats`.
+3193        // This updates some values for the last time.
+3194        let path_stats = self.path_stats(path_id).unwrap_or_default();
+3195        self.path_stats.remove(&path_id);
+3196        self.paths.remove(&path_id);
+3197        self.spaces[SpaceId::Data].number_spaces.remove(&path_id);
+3198
+3199        self.events.push_back(
+3200            PathEvent::Discarded {
+3201                id: path_id,
+3202                path_stats,
+3203            }
+3204            .into(),
+3205        );
+3206    }
+3207
+3208    fn handle_lost_packets(
+3209        &mut self,
+3210        pn_space: SpaceId,
+3211        path_id: PathId,
+3212        now: Instant,
+3213        lost_packets: Vec<u64>,
+3214        lost_mtu_probe: Option<u64>,
+3215        loss_delay: Duration,
+3216        in_persistent_congestion: bool,
+3217        size_of_lost_packets: u64,
+3218    ) {
+3219        debug_assert!(
+3220            {
+3221                let mut sorted = lost_packets.clone();
+3222                sorted.sort();
+3223                sorted == lost_packets
+3224            },
+3225            "lost_packets must be sorted"
+3226        );
+3227
+3228        self.drain_lost_packets(now, pn_space, path_id);
+3229
+3230        // OnPacketsLost
+3231        if let Some(largest_lost) = lost_packets.last().cloned() {
+3232            let old_bytes_in_flight = self.path_data_mut(path_id).in_flight.bytes;
+3233            let largest_lost_sent = self.spaces[pn_space]
+3234                .for_path(path_id)
+3235                .sent_packets
+3236                .get(largest_lost)
+3237                .unwrap()
+3238                .time_sent;
+3239            let path_stats = self.path_stats.entry(path_id).or_default();
+3240            path_stats.lost_packets += lost_packets.len() as u64;
+3241            path_stats.lost_bytes += size_of_lost_packets;
+3242            trace!(
+3243                %path_id,
+3244                count = lost_packets.len(),
+3245                lost_bytes = size_of_lost_packets,
+3246                "packets lost",
+3247            );
+3248
+3249            for &packet in &lost_packets {
+3250                let Some(info) = self.spaces[pn_space].for_path(path_id).take(packet) else {
+3251                    continue;
+3252                };
+3253                self.qlog
+3254                    .emit_packet_lost(packet, &info, loss_delay, pn_space.kind(), now);
+3255                self.paths
+3256                    .get_mut(&path_id)
+3257                    .unwrap()
+3258                    .remove_in_flight(&info);
+3259
+3260                for frame in info.stream_frames {
+3261                    self.streams.retransmit(frame);
+3262                }
+3263                self.spaces[pn_space].pending |= info.retransmits;
+3264                self.path_data_mut(path_id)
+3265                    .mtud
+3266                    .on_non_probe_lost(packet, info.size);
+3267
+3268                self.spaces[pn_space].for_path(path_id).lost_packets.insert(
+3269                    packet,
+3270                    LostPacket {
+3271                        time_sent: info.time_sent,
+3272                    },
+3273                );
+3274            }
+3275
+3276            let path = self.path_data_mut(path_id);
+3277            if path.mtud.black_hole_detected(now) {
+3278                path.congestion.on_mtu_update(path.mtud.current_mtu());
+3279                if let Some(max_datagram_size) = self.datagrams().max_size()
+3280                    && self.datagrams.drop_oversized(max_datagram_size)
+3281                    && self.datagrams.send_blocked
+3282                {
+3283                    self.datagrams.send_blocked = false;
+3284                    self.events.push_back(Event::DatagramsUnblocked);
+3285                }
+3286                self.path_stats
+3287                    .entry(path_id)
+3288                    .or_default()
+3289                    .black_holes_detected += 1;
+3290            }
+3291
+3292            // Don't apply congestion penalty for lost ack-only packets
+3293            let lost_ack_eliciting =
+3294                old_bytes_in_flight != self.path_data_mut(path_id).in_flight.bytes;
+3295
+3296            if lost_ack_eliciting {
+3297                self.path_stats
+3298                    .entry(path_id)
+3299                    .or_default()
+3300                    .congestion_events += 1;
+3301                self.path_data_mut(path_id).congestion.on_congestion_event(
+3302                    now,
+3303                    largest_lost_sent,
+3304                    in_persistent_congestion,
+3305                    false,
+3306                    size_of_lost_packets,
+3307                );
+3308            }
+3309        }
+3310
+3311        // Handle a lost MTU probe
+3312        if let Some(packet) = lost_mtu_probe {
+3313            let info = self.spaces[SpaceId::Data]
+3314                .for_path(path_id)
+3315                .take(packet)
+3316                .unwrap(); // safe: lost_mtu_probe is omitted from lost_packets, and
+3317            // therefore must not have been removed yet
+3318            self.paths
+3319                .get_mut(&path_id)
+3320                .unwrap()
+3321                .remove_in_flight(&info);
+3322            self.path_data_mut(path_id).mtud.on_probe_lost();
+3323            self.path_stats
+3324                .entry(path_id)
+3325                .or_default()
+3326                .lost_plpmtud_probes += 1;
+3327        }
+3328    }
+3329
+3330    /// Returns the earliest time packets should be declared lost for all spaces on a path.
+3331    ///
+3332    /// If a path has an acknowledged packet with any prior un-acknowledged packets, the
+3333    /// earliest un-acknowledged packet can be declared lost after a timeout has elapsed.
+3334    /// The time returned is when this packet should be declared lost.
+3335    fn loss_time_and_space(&self, path_id: PathId) -> Option<(Instant, SpaceId)> {
+3336        SpaceId::iter()
+3337            .filter_map(|id| {
+3338                self.spaces[id]
+3339                    .number_spaces
+3340                    .get(&path_id)
+3341                    .and_then(|pns| pns.loss_time)
+3342                    .map(|time| (time, id))
+3343            })
+3344            .min_by_key(|&(time, _)| time)
+3345    }
+3346
+3347    /// Returns the earliest next PTO should fire for all spaces on a path.
+3348    fn pto_time_and_space(&mut self, now: Instant, path_id: PathId) -> Option<(Instant, SpaceId)> {
+3349        let path = self.path(path_id)?;
+3350        let pto_count = path.pto_count;
+3351        let backoff = 2u32.pow(pto_count.min(MAX_BACKOFF_EXPONENT));
+3352        let mut duration = path.rtt.pto_base() * backoff;
+3353
+3354        if path_id == PathId::ZERO
+3355            && path.in_flight.ack_eliciting == 0
+3356            && !self.peer_completed_address_validation(PathId::ZERO)
+3357        {
+3358            // Address Validation during Connection Establishment:
+3359            // https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1. To prevent a
+3360            // deadlock if an Initial or Handshake packet from the server is lost and the
+3361            // server can not send more due to its anti-amplification limit the client must
+3362            // send another packet on PTO.
+3363            let space = match self.highest_space {
+3364                SpaceKind::Handshake => SpaceId::Handshake,
+3365                _ => SpaceId::Initial,
+3366            };
+3367
+3368            return Some((now + duration, space));
+3369        }
+3370
+3371        let mut result = None;
+3372        for space in SpaceId::iter() {
+3373            let Some(pns) = self.spaces[space].number_spaces.get(&path_id) else {
+3374                continue;
+3375            };
+3376
+3377            if !pns.has_in_flight() {
+3378                continue;
+3379            }
+3380            if space == SpaceId::Data {
+3381                // Skip ApplicationData until handshake completes.
+3382                if self.is_handshaking() {
+3383                    return result;
+3384                }
+3385                // Include max_ack_delay and backoff for ApplicationData.
+3386                duration += self.ack_frequency.max_ack_delay_for_pto() * backoff;
+3387            }
+3388            let Some(last_ack_eliciting) = pns.time_of_last_ack_eliciting_packet else {
+3389                continue;
+3390            };
+3391            let pto = last_ack_eliciting + duration;
+3392            if result.is_none_or(|(earliest_pto, _)| pto < earliest_pto) {
+3393                if path.anti_amplification_blocked(1) {
+3394                    // Nothing would be able to be sent.
+3395                    continue;
+3396                }
+3397                if path.in_flight.ack_eliciting == 0 {
+3398                    // Nothing ack-eliciting, no PTO to arm/fire.
+3399                    continue;
+3400                }
+3401                result = Some((pto, space));
+3402            }
+3403        }
+3404        result
+3405    }
+3406
+3407    fn peer_completed_address_validation(&self, path: PathId) -> bool {
+3408        // TODO(flub): This logic needs updating for multipath
+3409        if self.side.is_server() || self.state.is_closed() {
+3410            return true;
+3411        }
+3412        // The server is guaranteed to have validated our address if any of our handshake or 1-RTT
+3413        // packets are acknowledged or we've seen HANDSHAKE_DONE and discarded handshake keys.
+3414        self.spaces[SpaceId::Handshake]
+3415            .path_space(PathId::ZERO)
+3416            .and_then(|pns| pns.largest_acked_packet)
+3417            .is_some()
+3418            || self.spaces[SpaceId::Data]
+3419                .path_space(path)
+3420                .and_then(|pns| pns.largest_acked_packet)
+3421                .is_some()
+3422            || (self.crypto_state.has_keys(EncryptionLevel::OneRtt)
+3423                && !self.crypto_state.has_keys(EncryptionLevel::Handshake))
+3424    }
+3425
+3426    /// Resets the the [`PathTimer::LossDetection`] timer to the next instant it may be needed
+3427    ///
+3428    /// The timer must fire if either:
+3429    /// - An ack-eliciting packet we sent needs to be declared lost.
+3430    /// - A tail-loss probe needs to be sent.
+3431    ///
+3432    /// See [`Connection::on_loss_detection_timeout`] for details.
+3433    fn set_loss_detection_timer(&mut self, now: Instant, path_id: PathId) {
+3434        if self.state.is_closed() {
+3435            // No loss detection takes place on closed connections, and `close_common` already
+3436            // stopped time timer. Ensure we don't restart it inadvertently, e.g. in response to a
+3437            // reordered packet being handled by state-insensitive code.
+3438            return;
+3439        }
+3440
+3441        if let Some((loss_time, _)) = self.loss_time_and_space(path_id) {
+3442            // Time threshold loss detection.
+3443            self.timers.set(
+3444                Timer::PerPath(path_id, PathTimer::LossDetection),
+3445                loss_time,
+3446                self.qlog.with_time(now),
+3447            );
+3448            return;
+3449        }
+3450
+3451        // Determine which PN space to arm PTO for.
+3452        // Calculate PTO duration
+3453        if let Some((timeout, _)) = self.pto_time_and_space(now, path_id) {
+3454            self.timers.set(
+3455                Timer::PerPath(path_id, PathTimer::LossDetection),
+3456                timeout,
+3457                self.qlog.with_time(now),
+3458            );
+3459        } else {
+3460            self.timers.stop(
+3461                Timer::PerPath(path_id, PathTimer::LossDetection),
+3462                self.qlog.with_time(now),
+3463            );
+3464        }
+3465    }
+3466
+3467    /// The maximum probe timeout across all paths
+3468    ///
+3469    /// See [`Connection::pto`]
+3470    fn max_pto_for_space(&self, space: SpaceKind) -> Duration {
+3471        self.paths
+3472            .keys()
+3473            .map(|path_id| self.pto(space, *path_id))
+3474            .max()
+3475            .expect("there should be at least one path")
+3476    }
+3477
+3478    /// Probe Timeout
+3479    ///
+3480    /// The PTO is logically the time in which you'd expect to receive an acknowledgement
+3481    /// for a packet. So approximately RTT + max_ack_delay.
+3482    fn pto(&self, space: SpaceKind, path_id: PathId) -> Duration {
+3483        let max_ack_delay = match space {
+3484            SpaceKind::Initial | SpaceKind::Handshake => Duration::ZERO,
+3485            SpaceKind::Data => self.ack_frequency.max_ack_delay_for_pto(),
+3486        };
+3487        self.path_data(path_id).rtt.pto_base() + max_ack_delay
+3488    }
+3489
+3490    fn on_packet_authenticated(
+3491        &mut self,
+3492        now: Instant,
+3493        space_id: SpaceKind,
+3494        path_id: PathId,
+3495        ecn: Option<EcnCodepoint>,
+3496        packet: Option<u64>,
+3497        spin: bool,
+3498        is_1rtt: bool,
+3499    ) {
+3500        self.total_authed_packets += 1;
+3501        self.reset_keep_alive(path_id, now);
+3502        self.reset_idle_timeout(now, space_id, path_id);
+3503        self.permit_idle_reset = true;
+3504        self.receiving_ecn |= ecn.is_some();
+3505        if let Some(x) = ecn {
+3506            let space = &mut self.spaces[space_id];
+3507            space.for_path(path_id).ecn_counters += x;
+3508
+3509            if x.is_ce() {
+3510                space
+3511                    .for_path(path_id)
+3512                    .pending_acks
+3513                    .set_immediate_ack_required();
+3514            }
+3515        }
+3516
+3517        let Some(packet) = packet else {
+3518            return;
+3519        };
+3520        match &self.side {
+3521            ConnectionSide::Client { .. } => {
+3522                // If we received a handshake packet that authenticated, then we're talking to
+3523                // the real server.  From now on we should no longer allow the server to migrate
+3524                // its address.
+3525                if space_id == SpaceKind::Handshake
+3526                    && let Some(hs) = self.state.as_handshake_mut()
+3527                {
+3528                    hs.allow_server_migration = false;
+3529                }
+3530            }
+3531            ConnectionSide::Server { .. } => {
+3532                if self.crypto_state.has_keys(EncryptionLevel::Initial)
+3533                    && space_id == SpaceKind::Handshake
+3534                {
+3535                    // A server stops sending and processing Initial packets when it receives its first Handshake packet.
+3536                    self.discard_space(now, SpaceKind::Initial);
+3537                }
+3538                if self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) && is_1rtt {
+3539                    // Discard 0-RTT keys soon after receiving a 1-RTT packet
+3540                    self.set_key_discard_timer(now, space_id)
+3541                }
+3542            }
+3543        }
+3544        let space = self.spaces[space_id].for_path(path_id);
+3545        space.pending_acks.insert_one(packet, now);
+3546        if packet >= space.rx_packet.unwrap_or_default() {
+3547            space.rx_packet = Some(packet);
+3548            // Update outgoing spin bit, inverting iff we're the client
+3549            self.spin = self.side.is_client() ^ spin;
+3550        }
+3551    }
+3552
+3553    /// Resets the idle timeout timers
+3554    ///
+3555    /// Without multipath there is only the connection-wide idle timeout. When multipath is
+3556    /// enabled there is an additional per-path idle timeout.
+3557    fn reset_idle_timeout(&mut self, now: Instant, space: SpaceKind, path_id: PathId) {
+3558        // First reset the global idle timeout.
+3559        if let Some(timeout) = self.idle_timeout {
+3560            if self.state.is_closed() {
+3561                self.timers
+3562                    .stop(Timer::Conn(ConnTimer::Idle), self.qlog.with_time(now));
+3563            } else {
+3564                let dt = cmp::max(timeout, 3 * self.max_pto_for_space(space));
+3565                self.timers.set(
+3566                    Timer::Conn(ConnTimer::Idle),
+3567                    now + dt,
+3568                    self.qlog.with_time(now),
+3569                );
+3570            }
+3571        }
+3572
+3573        // Now handle the per-path state
+3574        if let Some(timeout) = self.path_data(path_id).idle_timeout {
+3575            if self.state.is_closed() {
+3576                self.timers.stop(
+3577                    Timer::PerPath(path_id, PathTimer::PathIdle),
+3578                    self.qlog.with_time(now),
+3579                );
+3580            } else {
+3581                let dt = cmp::max(timeout, 3 * self.pto(space, path_id));
+3582                self.timers.set(
+3583                    Timer::PerPath(path_id, PathTimer::PathIdle),
+3584                    now + dt,
+3585                    self.qlog.with_time(now),
+3586                );
+3587            }
+3588        }
+3589    }
+3590
+3591    /// Resets both the [`ConnTimer::KeepAlive`] and [`PathTimer::PathKeepAlive`] timers
+3592    fn reset_keep_alive(&mut self, path_id: PathId, now: Instant) {
+3593        if !self.state.is_established() {
+3594            return;
+3595        }
+3596
+3597        if let Some(interval) = self.config.keep_alive_interval {
+3598            self.timers.set(
+3599                Timer::Conn(ConnTimer::KeepAlive),
+3600                now + interval,
+3601                self.qlog.with_time(now),
+3602            );
+3603        }
+3604
+3605        if let Some(interval) = self.path_data(path_id).keep_alive {
+3606            self.timers.set(
+3607                Timer::PerPath(path_id, PathTimer::PathKeepAlive),
+3608                now + interval,
+3609                self.qlog.with_time(now),
+3610            );
+3611        }
+3612    }
+3613
+3614    /// Sets the timer for when a previously issued CID should be retired next
+3615    fn reset_cid_retirement(&mut self, now: Instant) {
+3616        if let Some((_path, t)) = self.next_cid_retirement() {
+3617            self.timers.set(
+3618                Timer::Conn(ConnTimer::PushNewCid),
+3619                t,
+3620                self.qlog.with_time(now),
+3621            );
+3622        }
+3623    }
+3624
+3625    /// The next time when a previously issued CID should be retired
+3626    fn next_cid_retirement(&self) -> Option<(PathId, Instant)> {
+3627        self.local_cid_state
+3628            .iter()
+3629            .filter_map(|(path_id, cid_state)| cid_state.next_timeout().map(|t| (*path_id, t)))
+3630            .min_by_key(|(_path_id, timeout)| *timeout)
+3631    }
+3632
+3633    /// Handle the already-decrypted first packet from the client
+3634    ///
+3635    /// Decrypting the first packet in the `Endpoint` allows stateless packet handling to be more
+3636    /// efficient.
+3637    pub(crate) fn handle_first_packet(
+3638        &mut self,
+3639        now: Instant,
+3640        network_path: FourTuple,
+3641        ecn: Option<EcnCodepoint>,
+3642        packet_number: u64,
+3643        packet: InitialPacket,
+3644        remaining: Option<BytesMut>,
+3645    ) -> Result<(), ConnectionError> {
+3646        let span = trace_span!("first recv");
+3647        let _guard = span.enter();
+3648        debug_assert!(self.side.is_server());
+3649        let len = packet.header_data.len() + packet.payload.len();
+3650        let path_id = PathId::ZERO;
+3651        self.path_data_mut(path_id).total_recvd = len as u64;
+3652
+3653        if let Some(hs) = self.state.as_handshake_mut() {
+3654            hs.expected_token = packet.header.token.clone();
+3655        } else {
+3656            unreachable!("first packet must be delivered in Handshake state");
+3657        }
+3658
+3659        // The first packet is always on PathId::ZERO
+3660        self.on_packet_authenticated(
+3661            now,
+3662            SpaceKind::Initial,
+3663            path_id,
+3664            ecn,
+3665            Some(packet_number),
+3666            false,
+3667            false,
+3668        );
+3669
+3670        let packet: Packet = packet.into();
+3671
+3672        let mut qlog = QlogRecvPacket::new(len);
+3673        qlog.header(&packet.header, Some(packet_number), path_id);
+3674
+3675        self.process_decrypted_packet(
+3676            now,
+3677            network_path,
+3678            path_id,
+3679            Some(packet_number),
+3680            packet,
+3681            &mut qlog,
+3682        )?;
+3683        self.qlog.emit_packet_received(qlog, now);
+3684        if let Some(data) = remaining {
+3685            self.handle_coalesced(now, network_path, path_id, ecn, data);
+3686        }
+3687
+3688        self.qlog.emit_recovery_metrics(
+3689            path_id,
+3690            &mut self.paths.get_mut(&path_id).unwrap().data,
+3691            now,
+3692        );
+3693
+3694        Ok(())
+3695    }
+3696
+3697    fn init_0rtt(&mut self, now: Instant) {
+3698        let Some((header, packet)) = self.crypto_state.session.early_crypto() else {
+3699            return;
+3700        };
+3701        if self.side.is_client() {
+3702            match self.crypto_state.session.transport_parameters() {
+3703                Ok(params) => {
+3704                    let params = params
+3705                        .expect("crypto layer didn't supply transport parameters with ticket");
+3706                    // Certain values must not be cached
+3707                    let params = TransportParameters {
+3708                        initial_src_cid: None,
+3709                        original_dst_cid: None,
+3710                        preferred_address: None,
+3711                        retry_src_cid: None,
+3712                        stateless_reset_token: None,
+3713                        min_ack_delay: None,
+3714                        ack_delay_exponent: TransportParameters::default().ack_delay_exponent,
+3715                        max_ack_delay: TransportParameters::default().max_ack_delay,
+3716                        initial_max_path_id: None,
+3717                        ..params
+3718                    };
+3719                    self.set_peer_params(params);
+3720                    self.qlog.emit_peer_transport_params_restored(self, now);
+3721                }
+3722                Err(e) => {
+3723                    error!("session ticket has malformed transport parameters: {}", e);
+3724                    return;
+3725                }
+3726            }
+3727        }
+3728        trace!("0-RTT enabled");
+3729        self.crypto_state.enable_zero_rtt(header, packet);
+3730    }
+3731
+3732    fn read_crypto(
+3733        &mut self,
+3734        space: SpaceId,
+3735        crypto: &frame::Crypto,
+3736        payload_len: usize,
+3737    ) -> Result<(), TransportError> {
+3738        let expected = if !self.state.is_handshake() {
+3739            SpaceId::Data
+3740        } else if self.highest_space == SpaceKind::Initial {
+3741            SpaceId::Initial
+3742        } else {
+3743            // On the server, self.highest_space can be Data after receiving the client's first
+3744            // flight, but we expect Handshake CRYPTO until the handshake is complete.
+3745            SpaceId::Handshake
+3746        };
+3747        // We can't decrypt Handshake packets when highest_space is Initial, CRYPTO frames in 0-RTT
+3748        // packets are illegal, and we don't process 1-RTT packets until the handshake is
+3749        // complete. Therefore, we will never see CRYPTO data from a later-than-expected space.
+3750        debug_assert!(space <= expected, "received out-of-order CRYPTO data");
+3751
+3752        let end = crypto.offset + crypto.data.len() as u64;
+3753        if space < expected
+3754            && end
+3755                > self.crypto_state.spaces[space.kind()]
+3756                    .crypto_stream
+3757                    .bytes_read()
+3758        {
+3759            warn!(
+3760                "received new {:?} CRYPTO data when expecting {:?}",
+3761                space, expected
+3762            );
+3763            return Err(TransportError::PROTOCOL_VIOLATION(
+3764                "new data at unexpected encryption level",
+3765            ));
+3766        }
+3767
+3768        let crypto_space = &mut self.crypto_state.spaces[space.kind()];
+3769        let max = end.saturating_sub(crypto_space.crypto_stream.bytes_read());
+3770        if max > self.config.crypto_buffer_size as u64 {
+3771            return Err(TransportError::CRYPTO_BUFFER_EXCEEDED(""));
+3772        }
+3773
+3774        crypto_space
+3775            .crypto_stream
+3776            .insert(crypto.offset, crypto.data.clone(), payload_len);
+3777        while let Some(chunk) = crypto_space.crypto_stream.read(usize::MAX, true) {
+3778            trace!("consumed {} CRYPTO bytes", chunk.bytes.len());
+3779            if self.crypto_state.session.read_handshake(&chunk.bytes)? {
+3780                self.events.push_back(Event::HandshakeDataReady);
+3781            }
+3782        }
+3783
+3784        Ok(())
+3785    }
+3786
+3787    fn write_crypto(&mut self) {
+3788        loop {
+3789            let space = self.highest_space;
+3790            let mut outgoing = Vec::new();
+3791            if let Some(crypto) = self.crypto_state.session.write_handshake(&mut outgoing) {
+3792                match space {
+3793                    SpaceKind::Initial => {
+3794                        self.upgrade_crypto(SpaceKind::Handshake, crypto);
+3795                    }
+3796                    SpaceKind::Handshake => {
+3797                        self.upgrade_crypto(SpaceKind::Data, crypto);
+3798                    }
+3799                    SpaceKind::Data => unreachable!("got updated secrets during 1-RTT"),
+3800                }
+3801            }
+3802            if outgoing.is_empty() {
+3803                if space == self.highest_space {
+3804                    break;
+3805                } else {
+3806                    // Keys updated, check for more data to send
+3807                    continue;
+3808                }
+3809            }
+3810            let offset = self.crypto_state.spaces[space].crypto_offset;
+3811            let outgoing = Bytes::from(outgoing);
+3812            if let Some(hs) = self.state.as_handshake_mut()
+3813                && space == SpaceKind::Initial
+3814                && offset == 0
+3815                && self.side.is_client()
+3816            {
+3817                hs.client_hello = Some(outgoing.clone());
+3818            }
+3819            self.crypto_state.spaces[space].crypto_offset += outgoing.len() as u64;
+3820            trace!("wrote {} {:?} CRYPTO bytes", outgoing.len(), space);
+3821            self.spaces[space].pending.crypto.push_back(frame::Crypto {
+3822                offset,
+3823                data: outgoing,
+3824            });
+3825        }
+3826    }
+3827
+3828    /// Switch to stronger cryptography during handshake
+3829    fn upgrade_crypto(&mut self, space: SpaceKind, crypto: Keys) {
+3830        debug_assert!(
+3831            !self.crypto_state.has_keys(space.encryption_level()),
+3832            "already reached packet space {space:?}"
+3833        );
+3834        trace!("{:?} keys ready", space);
+3835        if space == SpaceKind::Data {
+3836            // Precompute the first key update
+3837            self.crypto_state.next_crypto = Some(
+3838                self.crypto_state
+3839                    .session
+3840                    .next_1rtt_keys()
+3841                    .expect("handshake should be complete"),
+3842            );
+3843        }
+3844
+3845        self.crypto_state.spaces[space].keys = Some(crypto);
+3846        debug_assert!(space > self.highest_space);
+3847        self.highest_space = space;
+3848        if space == SpaceKind::Data && self.side.is_client() {
+3849            // Discard 0-RTT keys because 1-RTT keys are available.
+3850            self.crypto_state.discard_zero_rtt();
+3851        }
+3852    }
+3853
+3854    fn discard_space(&mut self, now: Instant, space: SpaceKind) {
+3855        debug_assert!(space != SpaceKind::Data);
+3856        trace!("discarding {:?} keys", space);
+3857        if space == SpaceKind::Initial {
+3858            // No longer needed
+3859            if let ConnectionSide::Client { token, .. } = &mut self.side {
+3860                *token = Bytes::new();
+3861            }
+3862        }
+3863        self.crypto_state.spaces[space].keys = None;
+3864        let space = &mut self.spaces[space];
+3865        let pns = space.for_path(PathId::ZERO);
+3866        pns.time_of_last_ack_eliciting_packet = None;
+3867        pns.loss_time = None;
+3868        pns.loss_probes = 0;
+3869        let sent_packets = mem::take(&mut pns.sent_packets);
+3870        let path = self.paths.get_mut(&PathId::ZERO).unwrap();
+3871        for (_, packet) in sent_packets.into_iter() {
+3872            path.data.remove_in_flight(&packet);
+3873        }
+3874
+3875        self.set_loss_detection_timer(now, PathId::ZERO)
+3876    }
+3877
+3878    fn handle_coalesced(
+3879        &mut self,
+3880        now: Instant,
+3881        network_path: FourTuple,
+3882        path_id: PathId,
+3883        ecn: Option<EcnCodepoint>,
+3884        data: BytesMut,
+3885    ) {
+3886        self.path_data_mut(path_id)
+3887            .inc_total_recvd(data.len() as u64);
+3888        let mut remaining = Some(data);
+3889        let cid_len = self
+3890            .local_cid_state
+3891            .values()
+3892            .map(|cid_state| cid_state.cid_len())
+3893            .next()
+3894            .expect("one cid_state must exist");
+3895        while let Some(data) = remaining {
+3896            match PartialDecode::new(
+3897                data,
+3898                &FixedLengthConnectionIdParser::new(cid_len),
+3899                &[self.version],
+3900                self.endpoint_config.grease_quic_bit,
+3901            ) {
+3902                Ok((partial_decode, rest)) => {
+3903                    remaining = rest;
+3904                    self.handle_decode(now, network_path, path_id, ecn, partial_decode);
+3905                }
+3906                Err(e) => {
+3907                    trace!("malformed header: {}", e);
+3908                    return;
+3909                }
+3910            }
+3911        }
+3912    }
+3913
+3914    fn handle_decode(
+3915        &mut self,
+3916        now: Instant,
+3917        network_path: FourTuple,
+3918        path_id: PathId,
+3919        ecn: Option<EcnCodepoint>,
+3920        partial_decode: PartialDecode,
+3921    ) {
+3922        let qlog = QlogRecvPacket::new(partial_decode.len());
+3923        if let Some(decoded) = self
+3924            .crypto_state
+3925            .unprotect_header(partial_decode, self.peer_params.stateless_reset_token)
+3926        {
+3927            self.handle_packet(
+3928                now,
+3929                network_path,
+3930                path_id,
+3931                ecn,
+3932                decoded.packet,
+3933                decoded.stateless_reset,
+3934                qlog,
+3935            );
+3936        }
+3937    }
+3938
+3939    fn handle_packet(
+3940        &mut self,
+3941        now: Instant,
+3942        network_path: FourTuple,
+3943        path_id: PathId,
+3944        ecn: Option<EcnCodepoint>,
+3945        packet: Option<Packet>,
+3946        stateless_reset: bool,
+3947        mut qlog: QlogRecvPacket,
+3948    ) {
+3949        self.stats.udp_rx.ios += 1;
+3950        self.path_stats.entry(path_id).or_default().udp_rx.ios += 1;
+3951
+3952        if let Some(ref packet) = packet {
+3953            trace!(
+3954                "got {:?} packet ({} bytes) from {} using id {}",
+3955                packet.header.space(),
+3956                packet.payload.len() + packet.header_data.len(),
+3957                network_path,
+3958                packet.header.dst_cid(),
+3959            );
+3960        }
+3961
+3962        if self.is_handshaking() {
+3963            if path_id != PathId::ZERO {
+3964                debug!(%network_path, %path_id, "discarding multipath packet during handshake");
+3965                return;
+3966            }
+3967            if network_path != self.path_data_mut(path_id).network_path {
+3968                if let Some(hs) = self.state.as_handshake() {
+3969                    if hs.allow_server_migration {
+3970                        trace!(%network_path, prev = %self.path_data(path_id).network_path, "server migrated to new remote");
+3971                        self.path_data_mut(path_id).network_path = network_path;
+3972                        self.qlog.emit_tuple_assigned(path_id, network_path, now);
+3973                    } else {
+3974                        debug!("discarding packet with unexpected remote during handshake");
+3975                        return;
+3976                    }
+3977                } else {
+3978                    debug!("discarding packet with unexpected remote during handshake");
+3979                    return;
+3980                }
+3981            }
+3982        }
+3983
+3984        let was_closed = self.state.is_closed();
+3985        let was_drained = self.state.is_drained();
+3986
+3987        let decrypted = match packet {
+3988            None => Err(None),
+3989            Some(mut packet) => self
+3990                .decrypt_packet(now, path_id, &mut packet)
+3991                .map(move |number| (packet, number)),
+3992        };
+3993        let result = match decrypted {
+3994            _ if stateless_reset => {
+3995                debug!("got stateless reset");
+3996                Err(ConnectionError::Reset)
+3997            }
+3998            Err(Some(e)) => {
+3999                warn!("illegal packet: {}", e);
+4000                Err(e.into())
+4001            }
+4002            Err(None) => {
+4003                debug!("failed to authenticate packet");
+4004                self.authentication_failures += 1;
+4005                let integrity_limit = self
+4006                    .crypto_state
+4007                    .integrity_limit(self.highest_space)
+4008                    .unwrap();
+4009                if self.authentication_failures > integrity_limit {
+4010                    Err(TransportError::AEAD_LIMIT_REACHED("integrity limit violated").into())
+4011                } else {
+4012                    return;
+4013                }
+4014            }
+4015            Ok((packet, number)) => {
+4016                qlog.header(&packet.header, number, path_id);
+4017                let span = match number {
+4018                    Some(pn) => trace_span!("recv", space = ?packet.header.space(), pn),
+4019                    None => trace_span!("recv", space = ?packet.header.space()),
+4020                };
+4021                let _guard = span.enter();
+4022
+4023                let dedup = self.spaces[packet.header.space()]
+4024                    .path_space_mut(path_id)
+4025                    .map(|pns| &mut pns.dedup);
+4026                if number.zip(dedup).is_some_and(|(n, d)| d.insert(n)) {
+4027                    debug!("discarding possible duplicate packet");
+4028                    self.qlog.emit_packet_received(qlog, now);
+4029                    return;
+4030                } else if self.state.is_handshake() && packet.header.is_short() {
+4031                    // TODO: SHOULD buffer these to improve reordering tolerance.
+4032                    trace!("dropping short packet during handshake");
+4033                    self.qlog.emit_packet_received(qlog, now);
+4034                    return;
+4035                } else {
+4036                    if let Header::Initial(InitialHeader { ref token, .. }) = packet.header
+4037                        && let Some(hs) = self.state.as_handshake()
+4038                        && self.side.is_server()
+4039                        && token != &hs.expected_token
+4040                    {
+4041                        // Clients must send the same retry token in every Initial. Initial
+4042                        // packets can be spoofed, so we discard rather than killing the
+4043                        // connection.
+4044                        warn!("discarding Initial with invalid retry token");
+4045                        self.qlog.emit_packet_received(qlog, now);
+4046                        return;
+4047                    }
+4048
+4049                    if !self.state.is_closed() {
+4050                        let spin = match packet.header {
+4051                            Header::Short { spin, .. } => spin,
+4052                            _ => false,
+4053                        };
+4054
+4055                        if self.side().is_server() && !self.abandoned_paths.contains(&path_id) {
+4056                            // Only the client is allowed to open paths
+4057                            self.ensure_path(path_id, network_path, now, number);
+4058                        }
+4059                        if self.paths.contains_key(&path_id) {
+4060                            self.on_packet_authenticated(
+4061                                now,
+4062                                packet.header.space(),
+4063                                path_id,
+4064                                ecn,
+4065                                number,
+4066                                spin,
+4067                                packet.header.is_1rtt(),
+4068                            );
+4069                        }
+4070                    }
+4071
+4072                    let res = self.process_decrypted_packet(
+4073                        now,
+4074                        network_path,
+4075                        path_id,
+4076                        number,
+4077                        packet,
+4078                        &mut qlog,
+4079                    );
+4080
+4081                    self.qlog.emit_packet_received(qlog, now);
+4082                    res
+4083                }
+4084            }
+4085        };
+4086
+4087        // State transitions for error cases
+4088        if let Err(conn_err) = result {
+4089            match conn_err {
+4090                ConnectionError::ApplicationClosed(reason) => self.state.move_to_closed(reason),
+4091                ConnectionError::ConnectionClosed(reason) => self.state.move_to_closed(reason),
+4092                ConnectionError::Reset
+4093                | ConnectionError::TransportError(TransportError {
+4094                    code: TransportErrorCode::AEAD_LIMIT_REACHED,
+4095                    ..
+4096                }) => {
+4097                    self.state.move_to_drained(Some(conn_err));
+4098                }
+4099                ConnectionError::TimedOut => {
+4100                    unreachable!("timeouts aren't generated by packet processing");
+4101                }
+4102                ConnectionError::TransportError(err) => {
+4103                    debug!("closing connection due to transport error: {}", err);
+4104                    self.state.move_to_closed(err);
+4105                }
+4106                ConnectionError::VersionMismatch => {
+4107                    self.state.move_to_draining(Some(conn_err));
+4108                }
+4109                ConnectionError::LocallyClosed => {
+4110                    unreachable!("LocallyClosed isn't generated by packet processing");
+4111                }
+4112                ConnectionError::CidsExhausted => {
+4113                    unreachable!("CidsExhausted isn't generated by packet processing");
+4114                }
+4115            };
+4116        }
+4117
+4118        if !was_closed && self.state.is_closed() {
+4119            self.close_common();
+4120            if !self.state.is_drained() {
+4121                self.set_close_timer(now);
+4122            }
+4123        }
+4124        if !was_drained && self.state.is_drained() {
+4125            self.endpoint_events.push_back(EndpointEventInner::Drained);
+4126            // Close timer may have been started previously, e.g. if we sent a close and got a
+4127            // stateless reset in response
+4128            self.timers
+4129                .stop(Timer::Conn(ConnTimer::Close), self.qlog.with_time(now));
+4130        }
+4131
+4132        // Transmit CONNECTION_CLOSE if necessary.
+4133        //
+4134        // If we received a valid packet and we are in the closed state we should respond
+4135        // with a CONNECTION_CLOSE frame.
+4136        // TODO: This SHOULD be rate-limited according to §10.2.1 of QUIC-TRANSPORT, but
+4137        //    that does not yet happen. This is triggered by each received packet.
+4138        if matches!(self.state.as_type(), StateType::Closed) {
+4139            // From https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1-7
+4140            //
+4141            // While in the closing state we must either:
+4142            // - discard packets coming from an un-validated remote OR
+4143            // - ensure we do not send more than 3 times the received data
+4144            //
+4145            // Doing the 2nd would mean we would be able to send CONNECTION_CLOSE to a peer
+4146            // who was (involuntary) migrated just at the time we initiated immediate
+4147            // close. It is a lot more work though. So while we would like to do this for
+4148            // now we only do 1.
+4149            //
+4150            // Another shortcoming of the current implementation is that when we have a
+4151            // previous PathData which is validated and the remote matches that path, we
+4152            // should schedule CONNECTION_CLOSE on that path. However currently we can not
+4153            // schedule such a packet. We should also fix this some day. This makes us
+4154            // vulnerable to an attacker faking a migration at the right time and then we'd
+4155            // be unable to send the CONNECTION_CLOSE to the real remote.
+4156            if self
+4157                .paths
+4158                .get(&path_id)
+4159                .map(|p| p.data.validated && p.data.network_path == network_path)
+4160                .unwrap_or(false)
+4161            {
+4162                self.connection_close_pending = true;
+4163            }
+4164        }
+4165    }
+4166
+4167    fn process_decrypted_packet(
+4168        &mut self,
+4169        now: Instant,
+4170        network_path: FourTuple,
+4171        path_id: PathId,
+4172        number: Option<u64>,
+4173        packet: Packet,
+4174        qlog: &mut QlogRecvPacket,
+4175    ) -> Result<(), ConnectionError> {
+4176        if !self.paths.contains_key(&path_id) {
+4177            // There is a chance this is a server side, first (for this path) packet, which would
+4178            // be a protocol violation. It's more likely, however, that this is a packet of a
+4179            // pruned path
+4180            trace!(%path_id, ?number, "discarding packet for unknown path");
+4181            return Ok(());
+4182        }
+4183        let state = match self.state.as_type() {
+4184            StateType::Established => {
+4185                match packet.header.space() {
+4186                    SpaceKind::Data => self.process_payload(
+4187                        now,
+4188                        network_path,
+4189                        path_id,
+4190                        number.unwrap(),
+4191                        packet,
+4192                        qlog,
+4193                    )?,
+4194                    _ if packet.header.has_frames() => {
+4195                        self.process_early_payload(now, path_id, packet, qlog)?
+4196                    }
+4197                    _ => {
+4198                        trace!("discarding unexpected pre-handshake packet");
+4199                    }
+4200                }
+4201                return Ok(());
+4202            }
+4203            StateType::Closed => {
+4204                for result in frame::Iter::new(packet.payload.freeze())? {
+4205                    let frame = match result {
+4206                        Ok(frame) => frame,
+4207                        Err(err) => {
+4208                            debug!("frame decoding error: {err:?}");
+4209                            continue;
+4210                        }
+4211                    };
+4212                    qlog.frame(&frame);
+4213
+4214                    if let Frame::Padding = frame {
+4215                        continue;
+4216                    };
+4217
+4218                    self.stats.frame_rx.record(frame.ty());
+4219
+4220                    if let Frame::Close(_error) = frame {
+4221                        self.state.move_to_draining(None);
+4222                        break;
+4223                    }
+4224                }
+4225                return Ok(());
+4226            }
+4227            StateType::Draining | StateType::Drained => return Ok(()),
+4228            StateType::Handshake => self.state.as_handshake_mut().expect("checked"),
+4229        };
+4230
+4231        match packet.header {
+4232            Header::Retry {
+4233                src_cid: remote_cid,
+4234                ..
+4235            } => {
+4236                debug_assert_eq!(path_id, PathId::ZERO);
+4237                if self.side.is_server() {
+4238                    return Err(TransportError::PROTOCOL_VIOLATION("client sent Retry").into());
+4239                }
+4240
+4241                let is_valid_retry = self
+4242                    .remote_cids
+4243                    .get(&path_id)
+4244                    .map(|cids| cids.active())
+4245                    .map(|orig_dst_cid| {
+4246                        self.crypto_state.session.is_valid_retry(
+4247                            orig_dst_cid,
+4248                            &packet.header_data,
+4249                            &packet.payload,
+4250                        )
+4251                    })
+4252                    .unwrap_or_default();
+4253                if self.total_authed_packets > 1
+4254                            || packet.payload.len() <= 16 // token + 16 byte tag
+4255                            || !is_valid_retry
+4256                {
+4257                    trace!("discarding invalid Retry");
+4258                    // - After the client has received and processed an Initial or Retry
+4259                    //   packet from the server, it MUST discard any subsequent Retry
+4260                    //   packets that it receives.
+4261                    // - A client MUST discard a Retry packet with a zero-length Retry Token
+4262                    //   field.
+4263                    // - Clients MUST discard Retry packets that have a Retry Integrity Tag
+4264                    //   that cannot be validated
+4265                    return Ok(());
+4266                }
+4267
+4268                trace!("retrying with CID {}", remote_cid);
+4269                let client_hello = state.client_hello.take().unwrap();
+4270                self.retry_src_cid = Some(remote_cid);
+4271                self.remote_cids
+4272                    .get_mut(&path_id)
+4273                    .expect("PathId::ZERO not yet abandoned, is_valid_retry would have been false")
+4274                    .update_initial_cid(remote_cid);
+4275                self.remote_handshake_cid = remote_cid;
+4276
+4277                let space = &mut self.spaces[SpaceId::Initial];
+4278                if let Some(info) = space.for_path(PathId::ZERO).take(0) {
+4279                    self.on_packet_acked(now, PathId::ZERO, info);
+4280                };
+4281
+4282                self.discard_space(now, SpaceKind::Initial); // Make sure we clean up after
+4283                // any retransmitted Initials
+4284                let crypto_space = &mut self.crypto_state.spaces[SpaceKind::Initial];
+4285                crypto_space.keys = Some(
+4286                    self.crypto_state
+4287                        .session
+4288                        .initial_keys(remote_cid, self.side.side()),
+4289                );
+4290                crypto_space.crypto_offset = client_hello.len() as u64;
+4291
+4292                let next_pn = self.spaces[SpaceId::Initial]
+4293                    .for_path(path_id)
+4294                    .next_packet_number;
+4295                self.spaces[SpaceId::Initial] = {
+4296                    let mut space = PacketSpace::new(now, SpaceId::Initial, &mut self.rng);
+4297                    space.for_path(path_id).next_packet_number = next_pn;
+4298                    space.pending.crypto.push_back(frame::Crypto {
+4299                        offset: 0,
+4300                        data: client_hello,
+4301                    });
+4302                    space
+4303                };
+4304
+4305                // Retransmit all 0-RTT data
+4306                let zero_rtt = mem::take(
+4307                    &mut self.spaces[SpaceId::Data]
+4308                        .for_path(PathId::ZERO)
+4309                        .sent_packets,
+4310                );
+4311                for (_, info) in zero_rtt.into_iter() {
+4312                    self.paths
+4313                        .get_mut(&PathId::ZERO)
+4314                        .unwrap()
+4315                        .remove_in_flight(&info);
+4316                    self.spaces[SpaceId::Data].pending |= info.retransmits;
+4317                }
+4318                self.streams.retransmit_all_for_0rtt();
+4319
+4320                let token_len = packet.payload.len() - 16;
+4321                let ConnectionSide::Client { ref mut token, .. } = self.side else {
+4322                    unreachable!("we already short-circuited if we're server");
+4323                };
+4324                *token = packet.payload.freeze().split_to(token_len);
+4325
+4326                self.state = State::handshake(state::Handshake {
+4327                    expected_token: Bytes::new(),
+4328                    remote_cid_set: false,
+4329                    client_hello: None,
+4330                    allow_server_migration: true,
+4331                });
+4332                Ok(())
+4333            }
+4334            Header::Long {
+4335                ty: LongType::Handshake,
+4336                src_cid: remote_cid,
+4337                dst_cid: local_cid,
+4338                ..
+4339            } => {
+4340                debug_assert_eq!(path_id, PathId::ZERO);
+4341                if remote_cid != self.remote_handshake_cid {
+4342                    debug!(
+4343                        "discarding packet with mismatched remote CID: {} != {}",
+4344                        self.remote_handshake_cid, remote_cid
+4345                    );
+4346                    return Ok(());
+4347                }
+4348                self.on_path_validated(path_id);
+4349
+4350                self.process_early_payload(now, path_id, packet, qlog)?;
+4351                if self.state.is_closed() {
+4352                    return Ok(());
+4353                }
+4354
+4355                if self.crypto_state.session.is_handshaking() {
+4356                    trace!("handshake ongoing");
+4357                    return Ok(());
+4358                }
+4359
+4360                if self.side.is_client() {
+4361                    // Client-only because server params were set from the client's Initial
+4362                    let params = self
+4363                        .crypto_state
+4364                        .session
+4365                        .transport_parameters()?
+4366                        .ok_or_else(|| {
+4367                            TransportError::new(
+4368                                TransportErrorCode::crypto(0x6d),
+4369                                "transport parameters missing".to_owned(),
+4370                            )
+4371                        })?;
+4372
+4373                    if self.has_0rtt() {
+4374                        if !self.crypto_state.session.early_data_accepted().unwrap() {
+4375                            debug_assert!(self.side.is_client());
+4376                            debug!("0-RTT rejected");
+4377                            self.crypto_state.accepted_0rtt = false;
+4378                            self.streams.zero_rtt_rejected();
+4379
+4380                            // Discard already-queued frames
+4381                            self.spaces[SpaceId::Data].pending = Retransmits::default();
+4382
+4383                            // Discard 0-RTT packets
+4384                            let sent_packets = mem::take(
+4385                                &mut self.spaces[SpaceId::Data].for_path(path_id).sent_packets,
+4386                            );
+4387                            for (_, packet) in sent_packets.into_iter() {
+4388                                self.paths
+4389                                    .get_mut(&path_id)
+4390                                    .unwrap()
+4391                                    .remove_in_flight(&packet);
+4392                            }
+4393                        } else {
+4394                            self.crypto_state.accepted_0rtt = true;
+4395                            params.validate_resumption_from(&self.peer_params)?;
+4396                        }
+4397                    }
+4398                    if let Some(token) = params.stateless_reset_token {
+4399                        let remote = self.path_data(path_id).network_path.remote;
+4400                        debug_assert!(!self.state.is_drained()); // requirement for endpoint events, checked above
+4401                        self.endpoint_events
+4402                            .push_back(EndpointEventInner::ResetToken(path_id, remote, token));
+4403                    }
+4404                    self.handle_peer_params(params, local_cid, remote_cid, now)?;
+4405                    self.issue_first_cids(now);
+4406                } else {
+4407                    // Server-only
+4408                    self.spaces[SpaceId::Data].pending.handshake_done = true;
+4409                    self.discard_space(now, SpaceKind::Handshake);
+4410                    self.events.push_back(Event::HandshakeConfirmed);
+4411                    trace!("handshake confirmed");
+4412                }
+4413
+4414                self.events.push_back(Event::Connected);
+4415                self.state.move_to_established();
+4416                trace!("established");
+4417
+4418                // Multipath can only be enabled after the state has reached Established.
+4419                // So this can not happen any earlier.
+4420                self.issue_first_path_cids(now);
+4421                Ok(())
+4422            }
+4423            Header::Initial(InitialHeader {
+4424                src_cid: remote_cid,
+4425                dst_cid: local_cid,
+4426                ..
+4427            }) => {
+4428                debug_assert_eq!(path_id, PathId::ZERO);
+4429                if !state.remote_cid_set {
+4430                    trace!("switching remote CID to {}", remote_cid);
+4431                    let mut state = state.clone();
+4432                    self.remote_cids
+4433                        .get_mut(&path_id)
+4434                        .expect("PathId::ZERO not yet abandoned")
+4435                        .update_initial_cid(remote_cid);
+4436                    self.remote_handshake_cid = remote_cid;
+4437                    self.original_remote_cid = remote_cid;
+4438                    state.remote_cid_set = true;
+4439                    self.state.move_to_handshake(state);
+4440                } else if remote_cid != self.remote_handshake_cid {
+4441                    debug!(
+4442                        "discarding packet with mismatched remote CID: {} != {}",
+4443                        self.remote_handshake_cid, remote_cid
+4444                    );
+4445                    return Ok(());
+4446                }
+4447
+4448                let starting_space = self.highest_space;
+4449                self.process_early_payload(now, path_id, packet, qlog)?;
+4450
+4451                if self.side.is_server()
+4452                    && starting_space == SpaceKind::Initial
+4453                    && self.highest_space != SpaceKind::Initial
+4454                {
+4455                    let params = self
+4456                        .crypto_state
+4457                        .session
+4458                        .transport_parameters()?
+4459                        .ok_or_else(|| {
+4460                            TransportError::new(
+4461                                TransportErrorCode::crypto(0x6d),
+4462                                "transport parameters missing".to_owned(),
+4463                            )
+4464                        })?;
+4465                    self.handle_peer_params(params, local_cid, remote_cid, now)?;
+4466                    self.issue_first_cids(now);
+4467                    self.init_0rtt(now);
+4468                }
+4469                Ok(())
+4470            }
+4471            Header::Long {
+4472                ty: LongType::ZeroRtt,
+4473                ..
+4474            } => {
+4475                self.process_payload(now, network_path, path_id, number.unwrap(), packet, qlog)?;
+4476                Ok(())
+4477            }
+4478            Header::VersionNegotiate { .. } => {
+4479                if self.total_authed_packets > 1 {
+4480                    return Ok(());
+4481                }
+4482                let supported = packet
+4483                    .payload
+4484                    .chunks(4)
+4485                    .any(|x| match <[u8; 4]>::try_from(x) {
+4486                        Ok(version) => self.version == u32::from_be_bytes(version),
+4487                        Err(_) => false,
+4488                    });
+4489                if supported {
+4490                    return Ok(());
+4491                }
+4492                debug!("remote doesn't support our version");
+4493                Err(ConnectionError::VersionMismatch)
+4494            }
+4495            Header::Short { .. } => unreachable!(
+4496                "short packets received during handshake are discarded in handle_packet"
+4497            ),
+4498        }
+4499    }
+4500
+4501    /// Process an Initial or Handshake packet payload
+4502    fn process_early_payload(
+4503        &mut self,
+4504        now: Instant,
+4505        path_id: PathId,
+4506        packet: Packet,
+4507        #[allow(unused)] qlog: &mut QlogRecvPacket,
+4508    ) -> Result<(), TransportError> {
+4509        debug_assert_ne!(packet.header.space(), SpaceKind::Data);
+4510        debug_assert_eq!(path_id, PathId::ZERO);
+4511        let payload_len = packet.payload.len();
+4512        let mut ack_eliciting = false;
+4513        for result in frame::Iter::new(packet.payload.freeze())? {
+4514            let frame = result?;
+4515            qlog.frame(&frame);
+4516            let span = match frame {
+4517                Frame::Padding => continue,
+4518                _ => Some(trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty)),
+4519            };
+4520
+4521            self.stats.frame_rx.record(frame.ty());
+4522
+4523            let _guard = span.as_ref().map(|x| x.enter());
+4524            ack_eliciting |= frame.is_ack_eliciting();
+4525
+4526            // Process frames
+4527            if frame.is_1rtt() && packet.header.space() != SpaceKind::Data {
+4528                return Err(TransportError::PROTOCOL_VIOLATION(
+4529                    "illegal frame type in handshake",
+4530                ));
+4531            }
+4532
+4533            match frame {
+4534                Frame::Padding | Frame::Ping => {}
+4535                Frame::Crypto(frame) => {
+4536                    self.read_crypto(packet.header.space().into(), &frame, payload_len)?;
+4537                }
+4538                Frame::Ack(ack) => {
+4539                    self.on_ack_received(now, packet.header.space().into(), ack)?;
+4540                }
+4541                Frame::PathAck(ack) => {
+4542                    span.as_ref()
+4543                        .map(|span| span.record("path", tracing::field::display(&ack.path_id)));
+4544                    self.on_path_ack_received(now, packet.header.space().into(), ack)?;
+4545                }
+4546                Frame::Close(reason) => {
+4547                    self.state.move_to_draining(Some(reason.into()));
+4548                    return Ok(());
+4549                }
+4550                _ => {
+4551                    let mut err =
+4552                        TransportError::PROTOCOL_VIOLATION("illegal frame type in handshake");
+4553                    err.frame = frame::MaybeFrame::Known(frame.ty());
+4554                    return Err(err);
+4555                }
+4556            }
+4557        }
+4558
+4559        if ack_eliciting {
+4560            // In the initial and handshake spaces, ACKs must be sent immediately
+4561            self.spaces[packet.header.space()]
+4562                .for_path(path_id)
+4563                .pending_acks
+4564                .set_immediate_ack_required();
+4565        }
+4566
+4567        self.write_crypto();
+4568        Ok(())
+4569    }
+4570
+4571    /// Processes the packet payload, always in the data space.
+4572    fn process_payload(
+4573        &mut self,
+4574        now: Instant,
+4575        network_path: FourTuple,
+4576        path_id: PathId,
+4577        number: u64,
+4578        packet: Packet,
+4579        #[allow(unused)] qlog: &mut QlogRecvPacket,
+4580    ) -> Result<(), TransportError> {
+4581        let is_multipath_negotiated = self.is_multipath_negotiated();
+4582        let payload = packet.payload.freeze();
+4583        let mut is_probing_packet = true;
+4584        let mut close = None;
+4585        let payload_len = payload.len();
+4586        let mut ack_eliciting = false;
+4587        // if this packet triggers a path migration and includes a observed address frame, it's
+4588        // stored here
+4589        let mut migration_observed_addr = None;
+4590        for result in frame::Iter::new(payload)? {
+4591            let frame = result?;
+4592            qlog.frame(&frame);
+4593            let span = match frame {
+4594                Frame::Padding => continue,
+4595                _ => trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty),
+4596            };
+4597
+4598            self.stats.frame_rx.record(frame.ty());
+4599            // Crypto, Stream and Datagram frames are special cased in order no pollute
+4600            // the log with payload data
+4601            match &frame {
+4602                Frame::Crypto(f) => {
+4603                    trace!(offset = f.offset, len = f.data.len(), "got frame CRYPTO");
+4604                }
+4605                Frame::Stream(f) => {
+4606                    trace!(id = %f.id, offset = f.offset, len = f.data.len(), fin = f.fin, "got frame STREAM");
+4607                }
+4608                Frame::Datagram(f) => {
+4609                    trace!(len = f.data.len(), "got frame DATAGRAM");
+4610                }
+4611                f => {
+4612                    trace!("got frame {f}");
+4613                }
+4614            }
+4615
+4616            let _guard = span.enter();
+4617            if packet.header.is_0rtt() {
+4618                match frame {
+4619                    Frame::Crypto(_) | Frame::Close(Close::Application(_)) => {
+4620                        return Err(TransportError::PROTOCOL_VIOLATION(
+4621                            "illegal frame type in 0-RTT",
+4622                        ));
+4623                    }
+4624                    _ => {
+4625                        if frame.is_1rtt() {
+4626                            return Err(TransportError::PROTOCOL_VIOLATION(
+4627                                "illegal frame type in 0-RTT",
+4628                            ));
+4629                        }
+4630                    }
+4631                }
+4632            }
+4633            ack_eliciting |= frame.is_ack_eliciting();
+4634
+4635            // Check whether this could be a probing packet
+4636            match frame {
+4637                Frame::Padding
+4638                | Frame::PathChallenge(_)
+4639                | Frame::PathResponse(_)
+4640                | Frame::NewConnectionId(_)
+4641                | Frame::ObservedAddr(_) => {}
+4642                _ => {
+4643                    is_probing_packet = false;
+4644                }
+4645            }
+4646
+4647            match frame {
+4648                Frame::Crypto(frame) => {
+4649                    self.read_crypto(SpaceId::Data, &frame, payload_len)?;
+4650                }
+4651                Frame::Stream(frame) => {
+4652                    if self.streams.received(frame, payload_len)?.should_transmit() {
+4653                        self.spaces[SpaceId::Data].pending.max_data = true;
+4654                    }
+4655                }
+4656                Frame::Ack(ack) => {
+4657                    self.on_ack_received(now, SpaceId::Data, ack)?;
+4658                }
+4659                Frame::PathAck(ack) => {
+4660                    span.record("path", tracing::field::display(&ack.path_id));
+4661                    self.on_path_ack_received(now, SpaceId::Data, ack)?;
+4662                }
+4663                Frame::Padding | Frame::Ping => {}
+4664                Frame::Close(reason) => {
+4665                    close = Some(reason);
+4666                }
+4667                Frame::PathChallenge(challenge) => {
+4668                    let path = &mut self
+4669                        .path_mut(path_id)
+4670                        .expect("payload is processed only after the path becomes known");
+4671                    path.path_responses.push(number, challenge.0, network_path);
+4672                    // At this point, update_network_path_or_discard was already called, so
+4673                    // we don't need to be lenient about `local_ip` possibly mis-matching.
+4674                    if network_path == path.network_path {
+4675                        // PATH_CHALLENGE on active path, possible off-path packet forwarding
+4676                        // attack. Send a non-probing packet to recover the active path.
+4677                        // TODO(flub): No longer true! We now path_challege also to validate
+4678                        //    the path if the path is new, without an RFC9000-style
+4679                        //    migration involved. This means we add in an extra
+4680                        //    IMMEDIATE_ACK on some challenges. It isn't really wrong to do
+4681                        //    so, but it still is something untidy. We should instead
+4682                        //    suppress this when we know the remote is still validating the
+4683                        //    path.
+4684                        match self.peer_supports_ack_frequency() {
+4685                            true => self.immediate_ack(path_id),
+4686                            false => {
+4687                                self.ping_path(path_id).ok();
+4688                            }
+4689                        }
+4690                    }
+4691                }
+4692                Frame::PathResponse(response) => {
+4693                    let path = self
+4694                        .paths
+4695                        .get_mut(&path_id)
+4696                        .expect("payload is processed only after the path becomes known");
+4697
+4698                    use PathTimer::*;
+4699                    use paths::OnPathResponseReceived::*;
+4700                    match path
+4701                        .data
+4702                        .on_path_response_received(now, response.0, network_path)
+4703                    {
+4704                        OnPath { was_open } => {
+4705                            let qlog = self.qlog.with_time(now);
+4706
+4707                            self.timers
+4708                                .stop(Timer::PerPath(path_id, PathValidation), qlog.clone());
+4709                            self.timers
+4710                                .stop(Timer::PerPath(path_id, PathOpen), qlog.clone());
+4711
+4712                            let next_challenge = path
+4713                                .data
+4714                                .earliest_on_path_expiring_challenge()
+4715                                .map(|time| time + self.ack_frequency.max_ack_delay_for_pto());
+4716                            self.timers.set_or_stop(
+4717                                Timer::PerPath(path_id, PathChallengeLost),
+4718                                next_challenge,
+4719                                qlog,
+4720                            );
+4721
+4722                            if !was_open {
+4723                                if is_multipath_negotiated {
+4724                                    self.events
+4725                                        .push_back(Event::Path(PathEvent::Opened { id: path_id }));
+4726                                }
+4727                                if let Some(observed) = path.data.last_observed_addr_report.as_ref()
+4728                                {
+4729                                    self.events.push_back(Event::Path(PathEvent::ObservedAddr {
+4730                                        id: path_id,
+4731                                        addr: observed.socket_addr(),
+4732                                    }));
+4733                                }
+4734                            }
+4735                            if let Some((_, ref mut prev)) = path.prev {
+4736                                prev.reset_on_path_challenges();
+4737                            }
+4738                        }
+4739                        OffPath => {
+4740                            debug!(%response, "Valid response to off-path PATH_CHALLENGE");
+4741                        }
+4742                        Ignored {
+4743                            sent_on,
+4744                            current_path,
+4745                        } => {
+4746                            debug!(%sent_on, %current_path, %response, "ignoring valid PATH_RESPONSE")
+4747                        }
+4748                        Unknown => debug!(%response, "ignoring invalid PATH_RESPONSE"),
+4749                    }
+4750                }
+4751                Frame::MaxData(frame::MaxData(bytes)) => {
+4752                    self.streams.received_max_data(bytes);
+4753                }
+4754                Frame::MaxStreamData(frame::MaxStreamData { id, offset }) => {
+4755                    self.streams.received_max_stream_data(id, offset)?;
+4756                }
+4757                Frame::MaxStreams(frame::MaxStreams { dir, count }) => {
+4758                    self.streams.received_max_streams(dir, count)?;
+4759                }
+4760                Frame::ResetStream(frame) => {
+4761                    if self.streams.received_reset(frame)?.should_transmit() {
+4762                        self.spaces[SpaceId::Data].pending.max_data = true;
+4763                    }
+4764                }
+4765                Frame::DataBlocked(DataBlocked(offset)) => {
+4766                    debug!(offset, "peer claims to be blocked at connection level");
+4767                }
+4768                Frame::StreamDataBlocked(StreamDataBlocked { id, offset }) => {
+4769                    if id.initiator() == self.side.side() && id.dir() == Dir::Uni {
+4770                        debug!("got STREAM_DATA_BLOCKED on send-only {}", id);
+4771                        return Err(TransportError::STREAM_STATE_ERROR(
+4772                            "STREAM_DATA_BLOCKED on send-only stream",
+4773                        ));
+4774                    }
+4775                    debug!(
+4776                        stream = %id,
+4777                        offset, "peer claims to be blocked at stream level"
+4778                    );
+4779                }
+4780                Frame::StreamsBlocked(StreamsBlocked { dir, limit }) => {
+4781                    if limit > MAX_STREAM_COUNT {
+4782                        return Err(TransportError::FRAME_ENCODING_ERROR(
+4783                            "unrepresentable stream limit",
+4784                        ));
+4785                    }
+4786                    debug!(
+4787                        "peer claims to be blocked opening more than {} {} streams",
+4788                        limit, dir
+4789                    );
+4790                }
+4791                Frame::StopSending(frame::StopSending { id, error_code }) => {
+4792                    if id.initiator() != self.side.side() {
+4793                        if id.dir() == Dir::Uni {
+4794                            debug!("got STOP_SENDING on recv-only {}", id);
+4795                            return Err(TransportError::STREAM_STATE_ERROR(
+4796                                "STOP_SENDING on recv-only stream",
+4797                            ));
+4798                        }
+4799                    } else if self.streams.is_local_unopened(id) {
+4800                        return Err(TransportError::STREAM_STATE_ERROR(
+4801                            "STOP_SENDING on unopened stream",
+4802                        ));
+4803                    }
+4804                    self.streams.received_stop_sending(id, error_code);
+4805                }
+4806                Frame::RetireConnectionId(frame::RetireConnectionId { path_id, sequence }) => {
+4807                    if let Some(ref path_id) = path_id {
+4808                        span.record("path", tracing::field::display(&path_id));
+4809                    }
+4810                    let path_id = path_id.unwrap_or_default();
+4811                    match self.local_cid_state.get_mut(&path_id) {
+4812                        None => error!(?path_id, "RETIRE_CONNECTION_ID for unknown path"),
+4813                        Some(cid_state) => {
+4814                            let allow_more_cids = cid_state
+4815                                .on_cid_retirement(sequence, self.peer_params.issue_cids_limit())?;
+4816
+4817                            // If the path has closed, we do not issue more CIDs for this path
+4818                            // For details see  https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.2.2
+4819                            // > an endpoint SHOULD provide new connection IDs for that path, if still open, using PATH_NEW_CONNECTION_ID frames.
+4820                            let has_path = !self.abandoned_paths.contains(&path_id);
+4821                            let allow_more_cids = allow_more_cids && has_path;
+4822
+4823                            debug_assert!(!self.state.is_drained()); // required for adding endpoint events, process_payload is never called for drained connections
+4824                            self.endpoint_events
+4825                                .push_back(EndpointEventInner::RetireConnectionId(
+4826                                    now,
+4827                                    path_id,
+4828                                    sequence,
+4829                                    allow_more_cids,
+4830                                ));
+4831                        }
+4832                    }
+4833                }
+4834                Frame::NewConnectionId(frame) => {
+4835                    let path_id = if let Some(path_id) = frame.path_id {
+4836                        if !self.is_multipath_negotiated() {
+4837                            return Err(TransportError::PROTOCOL_VIOLATION(
+4838                                "received PATH_NEW_CONNECTION_ID frame when multipath was not negotiated",
+4839                            ));
+4840                        }
+4841                        if path_id > self.local_max_path_id {
+4842                            return Err(TransportError::PROTOCOL_VIOLATION(
+4843                                "PATH_NEW_CONNECTION_ID contains path_id exceeding current max",
+4844                            ));
+4845                        }
+4846                        path_id
+4847                    } else {
+4848                        PathId::ZERO
+4849                    };
+4850
+4851                    if let Some(ref path_id) = frame.path_id {
+4852                        span.record("path", tracing::field::display(&path_id));
+4853                    }
+4854
+4855                    if self.abandoned_paths.contains(&path_id) {
+4856                        trace!("ignoring issued CID for abandoned path");
+4857                        continue;
+4858                    }
+4859                    let remote_cids = self
+4860                        .remote_cids
+4861                        .entry(path_id)
+4862                        .or_insert_with(|| CidQueue::new(frame.id));
+4863                    if remote_cids.active().is_empty() {
+4864                        return Err(TransportError::PROTOCOL_VIOLATION(
+4865                            "NEW_CONNECTION_ID when CIDs aren't in use",
+4866                        ));
+4867                    }
+4868                    if frame.retire_prior_to > frame.sequence {
+4869                        return Err(TransportError::PROTOCOL_VIOLATION(
+4870                            "NEW_CONNECTION_ID retiring unissued CIDs",
+4871                        ));
+4872                    }
+4873
+4874                    use crate::cid_queue::InsertError;
+4875                    match remote_cids.insert(frame) {
+4876                        Ok(None) if self.path(path_id).is_none() => {
+4877                            // if this gives us CIDs to open a new path and a nat traversal attempt
+4878                            // is underway we could try to probe a pending remote
+4879                            self.continue_nat_traversal_round(now);
+4880                        }
+4881                        Ok(None) => {}
+4882                        Ok(Some((retired, reset_token))) => {
+4883                            let pending_retired =
+4884                                &mut self.spaces[SpaceId::Data].pending.retire_cids;
+4885                            /// Ensure `pending_retired` cannot grow without bound. Limit is
+4886                            /// somewhat arbitrary but very permissive.
+4887                            const MAX_PENDING_RETIRED_CIDS: u64 = CidQueue::LEN as u64 * 10;
+4888                            // We don't bother counting in-flight frames because those are bounded
+4889                            // by congestion control.
+4890                            if (pending_retired.len() as u64)
+4891                                .saturating_add(retired.end.saturating_sub(retired.start))
+4892                                > MAX_PENDING_RETIRED_CIDS
+4893                            {
+4894                                return Err(TransportError::CONNECTION_ID_LIMIT_ERROR(
+4895                                    "queued too many retired CIDs",
+4896                                ));
+4897                            }
+4898                            pending_retired.extend(retired.map(|seq| (path_id, seq)));
+4899                            self.set_reset_token(path_id, network_path.remote, reset_token);
+4900                        }
+4901                        Err(InsertError::ExceedsLimit) => {
+4902                            return Err(TransportError::CONNECTION_ID_LIMIT_ERROR(""));
+4903                        }
+4904                        Err(InsertError::Retired) => {
+4905                            trace!("discarding already-retired");
+4906                            // RETIRE_CONNECTION_ID might not have been previously sent if e.g. a
+4907                            // range of connection IDs larger than the active connection ID limit
+4908                            // was retired all at once via retire_prior_to.
+4909                            self.spaces[SpaceId::Data]
+4910                                .pending
+4911                                .retire_cids
+4912                                .push((path_id, frame.sequence));
+4913                            continue;
+4914                        }
+4915                    };
+4916
+4917                    if self.side.is_server()
+4918                        && path_id == PathId::ZERO
+4919                        && self
+4920                            .remote_cids
+4921                            .get(&PathId::ZERO)
+4922                            .map(|cids| cids.active_seq() == 0)
+4923                            .unwrap_or_default()
+4924                    {
+4925                        // We're a server still using the initial remote CID for the client, so
+4926                        // let's switch immediately to enable clientside stateless resets.
+4927                        self.update_remote_cid(PathId::ZERO);
+4928                    }
+4929                }
+4930                Frame::NewToken(NewToken { token }) => {
+4931                    let ConnectionSide::Client {
+4932                        token_store,
+4933                        server_name,
+4934                        ..
+4935                    } = &self.side
+4936                    else {
+4937                        return Err(TransportError::PROTOCOL_VIOLATION("client sent NEW_TOKEN"));
+4938                    };
+4939                    if token.is_empty() {
+4940                        return Err(TransportError::FRAME_ENCODING_ERROR("empty token"));
+4941                    }
+4942                    trace!("got new token");
+4943                    token_store.insert(server_name, token);
+4944                }
+4945                Frame::Datagram(datagram) => {
+4946                    if self
+4947                        .datagrams
+4948                        .received(datagram, &self.config.datagram_receive_buffer_size)?
+4949                    {
+4950                        self.events.push_back(Event::DatagramReceived);
+4951                    }
+4952                }
+4953                Frame::AckFrequency(ack_frequency) => {
+4954                    // This frame can only be sent in the Data space
+4955
+4956                    if !self.ack_frequency.ack_frequency_received(&ack_frequency)? {
+4957                        // The AckFrequency frame is stale (we have already received a more
+4958                        // recent one)
+4959                        continue;
+4960                    }
+4961
+4962                    // Update the params for all of our paths
+4963                    for (path_id, space) in self.spaces[SpaceId::Data].number_spaces.iter_mut() {
+4964                        space.pending_acks.set_ack_frequency_params(&ack_frequency);
+4965
+4966                        // Our `max_ack_delay` has been updated, so we may need to adjust
+4967                        // its associated timeout
+4968                        if let Some(timeout) = space
+4969                            .pending_acks
+4970                            .max_ack_delay_timeout(self.ack_frequency.max_ack_delay)
+4971                        {
+4972                            self.timers.set(
+4973                                Timer::PerPath(*path_id, PathTimer::MaxAckDelay),
+4974                                timeout,
+4975                                self.qlog.with_time(now),
+4976                            );
+4977                        }
+4978                    }
+4979                }
+4980                Frame::ImmediateAck => {
+4981                    // This frame can only be sent in the Data space
+4982                    for pns in self.spaces[SpaceId::Data].iter_paths_mut() {
+4983                        pns.pending_acks.set_immediate_ack_required();
+4984                    }
+4985                }
+4986                Frame::HandshakeDone => {
+4987                    if self.side.is_server() {
+4988                        return Err(TransportError::PROTOCOL_VIOLATION(
+4989                            "client sent HANDSHAKE_DONE",
+4990                        ));
+4991                    }
+4992                    if self.crypto_state.has_keys(EncryptionLevel::Handshake) {
+4993                        self.discard_space(now, SpaceKind::Handshake);
+4994                    }
+4995                    self.events.push_back(Event::HandshakeConfirmed);
+4996                    trace!("handshake confirmed");
+4997                }
+4998                Frame::ObservedAddr(observed) => {
+4999                    // check if params allows the peer to send report and this node to receive it
+5000                    trace!(seq_no = %observed.seq_no, ip = %observed.ip, port = observed.port);
+5001                    if !self
+5002                        .peer_params
+5003                        .address_discovery_role
+5004                        .should_report(&self.config.address_discovery_role)
+5005                    {
+5006                        return Err(TransportError::PROTOCOL_VIOLATION(
+5007                            "received OBSERVED_ADDRESS frame when not negotiated",
+5008                        ));
+5009                    }
+5010                    // must only be sent in data space
+5011                    if packet.header.space() != SpaceKind::Data {
+5012                        return Err(TransportError::PROTOCOL_VIOLATION(
+5013                            "OBSERVED_ADDRESS frame outside data space",
+5014                        ));
+5015                    }
+5016
+5017                    let path = self.path_data_mut(path_id);
+5018                    if network_path == path.network_path {
+5019                        if let Some(updated) = path.update_observed_addr_report(observed)
+5020                            && path.open_status == paths::OpenStatus::Informed
+5021                        {
+5022                            self.events.push_back(Event::Path(PathEvent::ObservedAddr {
+5023                                id: path_id,
+5024                                addr: updated,
+5025                            }));
+5026                            // otherwise the event is reported when the path is deemed open
+5027                        }
+5028                    } else {
+5029                        // include in migration
+5030                        migration_observed_addr = Some(observed)
+5031                    }
+5032                }
+5033                Frame::PathAbandon(frame::PathAbandon {
+5034                    path_id,
+5035                    error_code,
+5036                }) => {
+5037                    span.record("path", tracing::field::display(&path_id));
+5038                    match self.close_path_inner(
+5039                        now,
+5040                        path_id,
+5041                        PathAbandonReason::RemoteAbandoned {
+5042                            error_code: error_code.into(),
+5043                        },
+5044                    ) {
+5045                        Ok(()) => {
+5046                            trace!("peer abandoned path");
+5047                        }
+5048                        Err(ClosePathError::LastOpenPath) => {
+5049                            trace!("peer abandoned last path, closing connection");
+5050                            return Err(TransportError::NO_VIABLE_PATH(
+5051                                "last path abandoned by peer",
+5052                            ));
+5053                        }
+5054                        Err(ClosePathError::ClosedPath) => {
+5055                            trace!("peer abandoned already closed path");
+5056                        }
+5057                        Err(ClosePathError::MultipathNotNegotiated) => {
+5058                            return Err(TransportError::PROTOCOL_VIOLATION(
+5059                                "received PATH_ABANDON frame when multipath was not negotiated",
+5060                            ));
+5061                        }
+5062                    };
+5063
+5064                    // Start draining the path if it still exists and hasn't started draining yet.
+5065                    if let Some(path) = self.paths.get_mut(&path_id)
+5066                        && !mem::replace(&mut path.data.draining, true)
+5067                    {
+5068                        let ack_delay = self.ack_frequency.max_ack_delay_for_pto();
+5069                        let pto = path.data.rtt.pto_base() + ack_delay;
+5070                        self.timers.set(
+5071                            Timer::PerPath(path_id, PathTimer::DiscardPath),
+5072                            now + 3 * pto,
+5073                            self.qlog.with_time(now),
+5074                        );
+5075
+5076                        self.set_max_path_id(now, self.local_max_path_id.saturating_add(1u8));
+5077                    }
+5078                }
+5079                Frame::PathStatusAvailable(info) => {
+5080                    span.record("path", tracing::field::display(&info.path_id));
+5081                    if self.is_multipath_negotiated() {
+5082                        self.on_path_status(
+5083                            info.path_id,
+5084                            PathStatus::Available,
+5085                            info.status_seq_no,
+5086                        );
+5087                    } else {
+5088                        return Err(TransportError::PROTOCOL_VIOLATION(
+5089                            "received PATH_STATUS_AVAILABLE frame when multipath was not negotiated",
+5090                        ));
+5091                    }
+5092                }
+5093                Frame::PathStatusBackup(info) => {
+5094                    span.record("path", tracing::field::display(&info.path_id));
+5095                    if self.is_multipath_negotiated() {
+5096                        self.on_path_status(info.path_id, PathStatus::Backup, info.status_seq_no);
+5097                    } else {
+5098                        return Err(TransportError::PROTOCOL_VIOLATION(
+5099                            "received PATH_STATUS_BACKUP frame when multipath was not negotiated",
+5100                        ));
+5101                    }
+5102                }
+5103                Frame::MaxPathId(frame::MaxPathId(path_id)) => {
+5104                    span.record("path", tracing::field::display(&path_id));
+5105                    if !self.is_multipath_negotiated() {
+5106                        return Err(TransportError::PROTOCOL_VIOLATION(
+5107                            "received MAX_PATH_ID frame when multipath was not negotiated",
+5108                        ));
+5109                    }
+5110                    // frames that do not increase the path id are ignored
+5111                    if path_id > self.remote_max_path_id {
+5112                        self.remote_max_path_id = path_id;
+5113                        self.issue_first_path_cids(now);
+5114                        while let Some(true) = self.continue_nat_traversal_round(now) {}
+5115                    }
+5116                }
+5117                Frame::PathsBlocked(frame::PathsBlocked(max_path_id)) => {
+5118                    // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST
+5119                    // be treated as a connection error of type PROTOCOL_VIOLATION.
+5120                    // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids>
+5121                    if self.is_multipath_negotiated() {
+5122                        if max_path_id > self.local_max_path_id {
+5123                            return Err(TransportError::PROTOCOL_VIOLATION(
+5124                                "PATHS_BLOCKED maximum path identifier was larger than local maximum",
+5125                            ));
+5126                        }
+5127                        debug!("received PATHS_BLOCKED({:?})", max_path_id);
+5128                        // TODO(@divma): ensure max concurrent paths
+5129                    } else {
+5130                        return Err(TransportError::PROTOCOL_VIOLATION(
+5131                            "received PATHS_BLOCKED frame when not multipath was not negotiated",
+5132                        ));
+5133                    }
+5134                }
+5135                Frame::PathCidsBlocked(frame::PathCidsBlocked { path_id, next_seq }) => {
+5136                    // Nothing to do.  This is recorded in the frame stats, but otherwise we
+5137                    // always issue all CIDs we're allowed to issue, so either this is an
+5138                    // impatient peer or a bug on our side.
+5139
+5140                    // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST
+5141                    // be treated as a connection error of type PROTOCOL_VIOLATION.
+5142                    // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids>
+5143                    if self.is_multipath_negotiated() {
+5144                        if path_id > self.local_max_path_id {
+5145                            return Err(TransportError::PROTOCOL_VIOLATION(
+5146                                "PATH_CIDS_BLOCKED path identifier was larger than local maximum",
+5147                            ));
+5148                        }
+5149                        if next_seq.0
+5150                            > self
+5151                                .local_cid_state
+5152                                .get(&path_id)
+5153                                .map(|cid_state| cid_state.active_seq().1 + 1)
+5154                                .unwrap_or_default()
+5155                        {
+5156                            return Err(TransportError::PROTOCOL_VIOLATION(
+5157                                "PATH_CIDS_BLOCKED next sequence number larger than in local state",
+5158                            ));
+5159                        }
+5160                        debug!(%path_id, %next_seq, "received PATH_CIDS_BLOCKED");
+5161                    } else {
+5162                        return Err(TransportError::PROTOCOL_VIOLATION(
+5163                            "received PATH_CIDS_BLOCKED frame when not multipath was not negotiated",
+5164                        ));
+5165                    }
+5166                }
+5167                Frame::AddAddress(addr) => {
+5168                    let client_state = match self.n0_nat_traversal.client_side_mut() {
+5169                        Ok(state) => state,
+5170                        Err(err) => {
+5171                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
+5172                                "Nat traversal(ADD_ADDRESS): {err}"
+5173                            )));
+5174                        }
+5175                    };
+5176
+5177                    if !client_state.check_remote_address(&addr) {
+5178                        // if the address is not valid we flag it, but update anyway
+5179                        warn!(?addr, "server sent illegal ADD_ADDRESS frame");
+5180                    }
+5181
+5182                    match client_state.add_remote_address(addr) {
+5183                        Ok(maybe_added) => {
+5184                            if let Some(added) = maybe_added {
+5185                                self.events.push_back(Event::NatTraversal(
+5186                                    n0_nat_traversal::Event::AddressAdded(added),
+5187                                ));
+5188                            }
+5189                        }
+5190                        Err(e) => {
+5191                            warn!(%e, "failed to add remote address")
+5192                        }
+5193                    }
+5194                }
+5195                Frame::RemoveAddress(addr) => {
+5196                    let client_state = match self.n0_nat_traversal.client_side_mut() {
+5197                        Ok(state) => state,
+5198                        Err(err) => {
+5199                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
+5200                                "Nat traversal(REMOVE_ADDRESS): {err}"
+5201                            )));
+5202                        }
+5203                    };
+5204                    if let Some(removed_addr) = client_state.remove_remote_address(addr) {
+5205                        self.events.push_back(Event::NatTraversal(
+5206                            n0_nat_traversal::Event::AddressRemoved(removed_addr),
+5207                        ));
+5208                    }
+5209                }
+5210                Frame::ReachOut(reach_out) => {
+5211                    let ipv6 = self.is_ipv6();
+5212                    let server_state = match self.n0_nat_traversal.server_side_mut() {
+5213                        Ok(state) => state,
+5214                        Err(err) => {
+5215                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
+5216                                "Nat traversal(REACH_OUT): {err}"
+5217                            )));
+5218                        }
+5219                    };
+5220
+5221                    if let Err(err) = server_state.handle_reach_out(reach_out, ipv6) {
+5222                        return Err(TransportError::PROTOCOL_VIOLATION(format!(
+5223                            "Nat traversal(REACH_OUT): {err}"
+5224                        )));
+5225                    }
+5226                }
+5227            }
+5228        }
+5229
+5230        let space = self.spaces[SpaceId::Data].for_path(path_id);
+5231        if space
+5232            .pending_acks
+5233            .packet_received(now, number, ack_eliciting, &space.dedup)
+5234        {
+5235            if self.abandoned_paths.contains(&path_id) {
+5236                // § 3.4.3 QUIC-MULTIPATH: promptly send ACKs for packets received from
+5237                // abandoned paths.
+5238                space.pending_acks.set_immediate_ack_required();
+5239            } else {
+5240                self.timers.set(
+5241                    Timer::PerPath(path_id, PathTimer::MaxAckDelay),
+5242                    now + self.ack_frequency.max_ack_delay,
+5243                    self.qlog.with_time(now),
+5244                );
+5245            }
+5246        }
+5247
+5248        // Issue stream ID credit due to ACKs of outgoing finish/resets and incoming finish/resets
+5249        // on stopped streams. Incoming finishes/resets on open streams are not handled here as they
+5250        // are only freed, and hence only issue credit, once the application has been notified
+5251        // during a read on the stream.
+5252        let pending = &mut self.spaces[SpaceId::Data].pending;
+5253        self.streams.queue_max_stream_id(pending);
+5254
+5255        if let Some(reason) = close {
+5256            self.state.move_to_draining(Some(reason.into()));
+5257            self.connection_close_pending = true;
+5258        }
+5259
+5260        if Some(number) == self.spaces[SpaceId::Data].for_path(path_id).rx_packet
+5261            && !is_probing_packet
+5262            && network_path != self.path_data(path_id).network_path
+5263        {
+5264            let ConnectionSide::Server { ref server_config } = self.side else {
+5265                panic!("packets from unknown remote should be dropped by clients");
+5266            };
+5267            debug_assert!(
+5268                server_config.migration,
+5269                "migration-initiating packets should have been dropped immediately"
+5270            );
+5271            self.migrate(path_id, now, network_path, migration_observed_addr);
+5272            // Break linkability, if possible
+5273            self.update_remote_cid(path_id);
+5274            self.spin = false;
+5275        }
+5276
+5277        Ok(())
+5278    }
+5279
+5280    fn migrate(
+5281        &mut self,
+5282        path_id: PathId,
+5283        now: Instant,
+5284        network_path: FourTuple,
+5285        observed_addr: Option<ObservedAddr>,
+5286    ) {
+5287        trace!(%network_path, %path_id, "migration initiated");
+5288        self.path_generation_counter = self.path_generation_counter.wrapping_add(1);
+5289        // TODO(@divma): conditions for path migration in multipath are very specific, check them
+5290        // again to prevent path migrations that should actually create a new path
+5291
+5292        // Reset rtt/congestion state for new path unless it looks like a NAT rebinding.
+5293        // Note that the congestion window will not grow until validation terminates. Helps mitigate
+5294        // amplification attacks performed by spoofing source addresses.
+5295        let prev_pto = self.pto(SpaceKind::Data, path_id);
+5296        let path = self.paths.get_mut(&path_id).expect("known path");
+5297        let mut new_path_data = if network_path.remote.is_ipv4()
+5298            && network_path.remote.ip() == path.data.network_path.remote.ip()
+5299        {
+5300            PathData::from_previous(network_path, &path.data, self.path_generation_counter, now)
+5301        } else {
+5302            let peer_max_udp_payload_size =
+5303                u16::try_from(self.peer_params.max_udp_payload_size.into_inner())
+5304                    .unwrap_or(u16::MAX);
+5305            PathData::new(
+5306                network_path,
+5307                self.allow_mtud,
+5308                Some(peer_max_udp_payload_size),
+5309                self.path_generation_counter,
+5310                now,
+5311                &self.config,
+5312            )
+5313        };
+5314        new_path_data.last_observed_addr_report = path.data.last_observed_addr_report.clone();
+5315        if let Some(report) = observed_addr
+5316            && let Some(updated) = new_path_data.update_observed_addr_report(report)
+5317        {
+5318            tracing::info!("adding observed addr event from migration");
+5319            self.events.push_back(Event::Path(PathEvent::ObservedAddr {
+5320                id: path_id,
+5321                addr: updated,
+5322            }));
+5323        }
+5324        new_path_data.pending_on_path_challenge = true;
+5325
+5326        let mut prev_path_data = mem::replace(&mut path.data, new_path_data);
+5327
+5328        // Only store this as previous path if it was validated. For all we know there could
+5329        // already be a previous path stored which might have been validated in the past,
+5330        // which is more valuable than one that's not yet validated.
+5331        //
+5332        // With multipath it is possible that there are no remote CIDs for the path ID
+5333        // yet. In this case we would never have sent on this path yet and would not be able
+5334        // to send a PATH_CHALLENGE either, which is currently a fire-and-forget affair
+5335        // anyway. So don't store such a path either.
+5336        if !prev_path_data.validated
+5337            && let Some(cid) = self.remote_cids.get(&path_id).map(CidQueue::active)
+5338        {
+5339            prev_path_data.pending_on_path_challenge = true;
+5340            // We haven't updated the remote CID yet, this captures the remote CID we were using on
+5341            // the previous path.
+5342            path.prev = Some((cid, prev_path_data));
+5343        }
+5344
+5345        // We need to re-assign the correct remote to this path in qlog
+5346        self.qlog.emit_tuple_assigned(path_id, network_path, now);
+5347
+5348        self.timers.set(
+5349            Timer::PerPath(path_id, PathTimer::PathValidation),
+5350            now + 3 * cmp::max(self.pto(SpaceKind::Data, path_id), prev_pto),
+5351            self.qlog.with_time(now),
+5352        );
+5353    }
+5354
+5355    /// Handle a change in the local address, i.e. an active migration
+5356    ///
+5357    /// In the general (non-multipath) case, paths will perform a RFC9000 migration and be pinged
+5358    /// for a liveness check. This is the behaviour of a path assumed to be recoverable, even if
+5359    /// this is not the case.
+5360    ///
+5361    /// Clients in a connection in which multipath has been negotiated should migrate paths to new
+5362    /// [`PathId`]s. For paths that are known to be non-recoverable can be migrated to a new
+5363    /// [`PathId`] by closing the current path, and opening a new one to the same remote. Treating
+5364    /// paths as non recoverable when necessary accelerates connectivity re-establishment, or might
+5365    /// allow it altogether.
+5366    ///
+5367    /// The optional `hint` allows callers to indicate when paths are non-recoverable and should be
+5368    /// migrated to new a [`PathId`].
+5369    // NOTE: only clients are allowed to migrate, but generally dealing with RFC9000 migrations is
+5370    // lacking <https://github.com/n0-computer/noq/issues/364>
+5371    pub fn handle_network_change(&mut self, hint: Option<&dyn NetworkChangeHint>, now: Instant) {
+5372        debug!("network changed");
+5373        if self.state.is_drained() {
+5374            return;
+5375        }
+5376        if self.highest_space < SpaceKind::Data {
+5377            for path in self.paths.values_mut() {
+5378                // Clear the local address for it to be obtained from the socket again.
+5379                path.data.network_path.local_ip = None;
+5380            }
+5381
+5382            self.update_remote_cid(PathId::ZERO);
+5383            self.ping();
+5384
+5385            return;
+5386        }
+5387
+5388        // Paths that can't recover so a new path should be open instead. If multipath is not
+5389        // negotiated, this will be empty.
+5390        let mut non_recoverable_paths = Vec::default();
+5391        let mut recoverable_paths = Vec::default();
+5392        let mut open_paths = 0;
+5393
+5394        let is_multipath_negotiated = self.is_multipath_negotiated();
+5395        let is_client = self.side().is_client();
+5396        let immediate_ack_allowed = self.peer_supports_ack_frequency();
+5397
+5398        for (path_id, path) in self.paths.iter_mut() {
+5399            if self.abandoned_paths.contains(path_id) {
+5400                continue;
+5401            }
+5402            open_paths += 1;
+5403
+5404            // Clear the local address for it to be obtained from the socket again. This applies to
+5405            // all paths, regardless of being considered recoverable or not
+5406            path.data.network_path.local_ip = None;
+5407
+5408            let network_path = path.data.network_path;
+5409            let remote = network_path.remote;
+5410
+5411            // Without multipath, the connection tries to recover the single path, whereas with
+5412            // multipath, even in a single-path scenario, we attempt to migrate the path to a new
+5413            // PathId.
+5414            let attempt_to_recover = if is_multipath_negotiated {
+5415                if is_client {
+5416                    hint.map(|h| h.is_path_recoverable(*path_id, network_path))
+5417                        .unwrap_or(false)
+5418                } else {
+5419                    // Servers should have stable addresses so this scenario is generally discouraged.
+5420                    // There is no way to prevent this, so the best hope is to attempt to recover the
+5421                    // path
+5422                    true
+5423                }
+5424            } else {
+5425                // In the non multipath case, we try to recover the single active path
+5426                true
+5427            };
+5428
+5429            if attempt_to_recover {
+5430                recoverable_paths.push((*path_id, remote));
+5431            } else {
+5432                non_recoverable_paths.push((*path_id, remote, path.data.local_status()))
+5433            }
+5434        }
+5435
+5436        /* NON RECOVERABLE PATHS */
+5437        // This are handled first, so that in case the treatment intended for these fails, we can
+5438        // go the recoverable route instead.
+5439
+5440        // Decide if we need to close first or open first in the multipath case.
+5441        // - Opening first has a higher risk of getting limited by the negotiated MAX_PATH_ID.
+5442        // - Closing first risks this being the only open path.
+5443        // We prefer closing paths first unless we identify this is the last open path.
+5444        let open_first = open_paths == non_recoverable_paths.len();
+5445
+5446        for (path_id, remote, status) in non_recoverable_paths.into_iter() {
+5447            let network_path = FourTuple {
+5448                remote,
+5449                local_ip: None, /* allow the local ip to be discovered */
+5450            };
+5451
+5452            if open_first && let Err(e) = self.open_path(network_path, status, now) {
+5453                debug!(%e,"Failed to open new path for network change");
+5454                // if this fails, let the path try to recover itself
+5455                recoverable_paths.push((path_id, remote));
+5456                continue;
+5457            }
+5458
+5459            if let Err(e) =
+5460                self.close_path_inner(now, path_id, PathAbandonReason::UnusableAfterNetworkChange)
+5461            {
+5462                debug!(%e,"Failed to close unrecoverable path after network change");
+5463                recoverable_paths.push((path_id, remote));
+5464                continue;
+5465            }
+5466
+5467            if !open_first && let Err(e) = self.open_path(network_path, status, now) {
+5468                // Path has already been closed if we got here. Since the path was not recoverable,
+5469                // this might be desirable in any case, because other paths exist (!open_first) and
+5470                // this was is considered non recoverable
+5471                debug!(%e,"Failed to open new path for network change");
+5472            }
+5473        }
+5474
+5475        /* RECOVERABLE PATHS */
+5476
+5477        for (path_id, remote) in recoverable_paths.into_iter() {
+5478            let space = &mut self.spaces[SpaceId::Data];
+5479
+5480            // Schedule a Ping for a liveness check.
+5481            if let Some(path_space) = space.number_spaces.get_mut(&path_id) {
+5482                path_space.ping_pending = true;
+5483
+5484                if immediate_ack_allowed {
+5485                    path_space.immediate_ack_pending = true;
+5486                }
+5487            }
+5488
+5489            let Some((reset_token, retired)) =
+5490                self.remote_cids.get_mut(&path_id).and_then(CidQueue::next)
+5491            else {
+5492                continue;
+5493            };
+5494
+5495            // Retire the current remote CID and any CIDs we had to skip.
+5496            space
+5497                .pending
+5498                .retire_cids
+5499                .extend(retired.map(|seq| (path_id, seq)));
+5500
+5501            debug_assert!(!self.state.is_drained()); // required for endpoint_events, checked above
+5502            self.endpoint_events
+5503                .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token));
+5504        }
+5505    }
+5506
+5507    /// Switch to a previously unused remote connection ID, if possible
+5508    fn update_remote_cid(&mut self, path_id: PathId) {
+5509        let Some((reset_token, retired)) = self
+5510            .remote_cids
+5511            .get_mut(&path_id)
+5512            .and_then(|cids| cids.next())
+5513        else {
+5514            return;
+5515        };
+5516
+5517        // Retire the current remote CID and any CIDs we had to skip.
+5518        self.spaces[SpaceId::Data]
+5519            .pending
+5520            .retire_cids
+5521            .extend(retired.map(|seq| (path_id, seq)));
+5522        let remote = self.path_data(path_id).network_path.remote;
+5523        self.set_reset_token(path_id, remote, reset_token);
+5524    }
+5525
+5526    /// Sends this reset token to the endpoint
+5527    ///
+5528    /// The endpoint needs to know the reset tokens issued by the peer, so that if the peer
+5529    /// sends a reset token it knows to route it to this connection. See RFC 9000 section
+5530    /// 10.3. Stateless Reset.
+5531    ///
+5532    /// Reset tokens are different for each path, the endpoint identifies paths by peer
+5533    /// socket address however, not by path ID.
+5534    fn set_reset_token(&mut self, path_id: PathId, remote: SocketAddr, reset_token: ResetToken) {
+5535        debug_assert!(!self.state.is_drained()); // required for endpoint events, set_reset_token is never called for drained connections
+5536        self.endpoint_events
+5537            .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token));
+5538
+5539        // During the handshake the server sends a reset token in the transport
+5540        // parameters. When we are the client and we receive the reset token during the
+5541        // handshake we want this to affect our peer transport parameters.
+5542        // TODO(flub): Pretty sure this is pointless, the entire params is overwritten
+5543        //    shortly after this was called.  And then the params don't have this anymore.
+5544        if path_id == PathId::ZERO {
+5545            self.peer_params.stateless_reset_token = Some(reset_token);
+5546        }
+5547    }
+5548
+5549    /// Issue an initial set of connection IDs to the peer upon connection
+5550    fn issue_first_cids(&mut self, now: Instant) {
+5551        if self
+5552            .local_cid_state
+5553            .get(&PathId::ZERO)
+5554            .expect("PathId::ZERO exists when the connection is created")
+5555            .cid_len()
+5556            == 0
+5557        {
+5558            return;
+5559        }
+5560
+5561        // Subtract 1 to account for the CID we supplied while handshaking
+5562        let mut n = self.peer_params.issue_cids_limit() - 1;
+5563        if let ConnectionSide::Server { server_config } = &self.side
+5564            && server_config.has_preferred_address()
+5565        {
+5566            // We also sent a CID in the transport parameters
+5567            n -= 1;
+5568        }
+5569        debug_assert!(!self.state.is_drained()); // requirement for endpoint_events
+5570        self.endpoint_events
+5571            .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n));
+5572    }
+5573
+5574    /// Issues an initial set of CIDs for paths that have not yet had any CIDs issued
+5575    ///
+5576    /// Later CIDs are issued when CIDs expire or are retired by the peer.
+5577    fn issue_first_path_cids(&mut self, now: Instant) {
+5578        if let Some(max_path_id) = self.max_path_id() {
+5579            let mut path_id = self.max_path_id_with_cids.next();
+5580            while path_id <= max_path_id {
+5581                self.endpoint_events
+5582                    .push_back(EndpointEventInner::NeedIdentifiers(
+5583                        path_id,
+5584                        now,
+5585                        self.peer_params.issue_cids_limit(),
+5586                    ));
+5587                path_id = path_id.next();
+5588            }
+5589            self.max_path_id_with_cids = max_path_id;
+5590        }
+5591    }
+5592
+5593    /// Populates a packet with frames
+5594    ///
+5595    /// This tries to fit as many frames as possible into the packet.
+5596    ///
+5597    /// *path_exclusive_only* means to only build frames which can only be sent on this
+5598    /// *path.  This is used in multipath for backup paths while there is still an active
+5599    /// *path.
+5600    fn populate_packet<'a, 'b>(
+5601        &mut self,
+5602        now: Instant,
+5603        space_id: SpaceId,
+5604        path_id: PathId,
+5605        scheduling_info: &PathSchedulingInfo,
+5606        builder: &mut PacketBuilder<'a, 'b>,
+5607    ) {
+5608        let is_multipath_negotiated = self.is_multipath_negotiated();
+5609        let space_has_keys = self.crypto_state.has_keys(space_id.encryption_level());
+5610        let is_0rtt = space_id == SpaceId::Data && !space_has_keys;
+5611        let stats = &mut self.stats.frame_tx;
+5612        let space = &mut self.spaces[space_id];
+5613        let path = &mut self.paths.get_mut(&path_id).expect("known path").data;
+5614        space
+5615            .for_path(path_id)
+5616            .pending_acks
+5617            .maybe_ack_non_eliciting();
+5618
+5619        // HANDSHAKE_DONE
+5620        if !is_0rtt
+5621            && !scheduling_info.is_abandoned
+5622            && scheduling_info.may_send_data
+5623            && mem::replace(&mut space.pending.handshake_done, false)
+5624        {
+5625            builder.write_frame(frame::HandshakeDone, stats);
+5626        }
+5627
+5628        // PING
+5629        if !scheduling_info.is_abandoned
+5630            && mem::replace(&mut space.for_path(path_id).ping_pending, false)
+5631        {
+5632            builder.write_frame(frame::Ping, stats);
+5633        }
+5634
+5635        // IMMEDIATE_ACK
+5636        if !scheduling_info.is_abandoned
+5637            && mem::replace(&mut space.for_path(path_id).immediate_ack_pending, false)
+5638        {
+5639            debug_assert_eq!(
+5640                space_id,
+5641                SpaceId::Data,
+5642                "immediate acks must be sent in the data space"
+5643            );
+5644            builder.write_frame(frame::ImmediateAck, stats);
+5645        }
+5646
+5647        // ACK
+5648        if !scheduling_info.is_abandoned && scheduling_info.may_send_data {
+5649            for path_id in space
+5650                .number_spaces
+5651                .iter_mut()
+5652                .filter(|(_, pns)| pns.pending_acks.can_send())
+5653                .map(|(&path_id, _)| path_id)
+5654                .collect::<Vec<_>>()
+5655            {
+5656                Self::populate_acks(
+5657                    now,
+5658                    self.receiving_ecn,
+5659                    path_id,
+5660                    space_id,
+5661                    space,
+5662                    is_multipath_negotiated,
+5663                    builder,
+5664                    stats,
+5665                    space_has_keys,
+5666                );
+5667            }
+5668        }
+5669
+5670        // ACK_FREQUENCY
+5671        if !scheduling_info.is_abandoned
+5672            && scheduling_info.may_send_data
+5673            && mem::replace(&mut space.pending.ack_frequency, false)
+5674        {
+5675            let sequence_number = self.ack_frequency.next_sequence_number();
+5676
+5677            // Safe to unwrap because this is always provided when ACK frequency is enabled
+5678            let config = self.config.ack_frequency_config.as_ref().unwrap();
+5679
+5680            // Ensure the delay is within bounds to avoid a PROTOCOL_VIOLATION error
+5681            let max_ack_delay = self.ack_frequency.candidate_max_ack_delay(
+5682                path.rtt.get(),
+5683                config,
+5684                &self.peer_params,
+5685            );
+5686
+5687            let frame = frame::AckFrequency {
+5688                sequence: sequence_number,
+5689                ack_eliciting_threshold: config.ack_eliciting_threshold,
+5690                request_max_ack_delay: max_ack_delay.as_micros().try_into().unwrap_or(VarInt::MAX),
+5691                reordering_threshold: config.reordering_threshold,
+5692            };
+5693            builder.write_frame(frame, stats);
+5694
+5695            self.ack_frequency
+5696                .ack_frequency_sent(path_id, builder.packet_number, max_ack_delay);
+5697        }
+5698
+5699        // PATH_CHALLENGE
+5700        if !scheduling_info.is_abandoned
+5701            && space_id == SpaceId::Data
+5702            && path.pending_on_path_challenge
+5703            && !self.state.is_closed()
+5704            && builder.frame_space_remaining() > frame::PathChallenge::SIZE_BOUND
+5705        // we don't want to send new challenges if we are already closing
+5706        {
+5707            path.pending_on_path_challenge = false;
+5708
+5709            let token = self.rng.random();
+5710            path.record_path_challenge_sent(now, token, path.network_path);
+5711            // Generate a new challenge every time we send a new PATH_CHALLENGE
+5712            let challenge = frame::PathChallenge(token);
+5713            builder.write_frame(challenge, stats);
+5714            builder.require_padding();
+5715            let pto = self.ack_frequency.max_ack_delay_for_pto() + path.rtt.pto_base();
+5716            if path.open_status == paths::OpenStatus::Pending {
+5717                path.open_status = paths::OpenStatus::Sent;
+5718                self.timers.set(
+5719                    Timer::PerPath(path_id, PathTimer::PathOpen),
+5720                    now + 3 * pto,
+5721                    self.qlog.with_time(now),
+5722                );
+5723            }
+5724
+5725            self.timers.set(
+5726                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
+5727                now + pto,
+5728                self.qlog.with_time(now),
+5729            );
+5730
+5731            if is_multipath_negotiated && !path.validated && path.pending_on_path_challenge {
+5732                // queue informing the path status along with the challenge
+5733                space.pending.path_status.insert(path_id);
+5734            }
+5735
+5736            // Always include an OBSERVED_ADDR frame with a PATH_CHALLENGE, regardless
+5737            // of whether one has already been sent on this path.
+5738            if space_id == SpaceId::Data
+5739                && self
+5740                    .config
+5741                    .address_discovery_role
+5742                    .should_report(&self.peer_params.address_discovery_role)
+5743            {
+5744                let frame = frame::ObservedAddr::new(
+5745                    path.network_path.remote,
+5746                    self.next_observed_addr_seq_no,
+5747                );
+5748                if builder.frame_space_remaining() > frame.size() {
+5749                    builder.write_frame(frame, stats);
+5750
+5751                    self.next_observed_addr_seq_no =
+5752                        self.next_observed_addr_seq_no.saturating_add(1u8);
+5753                    path.observed_addr_sent = true;
+5754
+5755                    space.pending.observed_addr = false;
+5756                }
+5757            }
+5758        }
+5759
+5760        // PATH_RESPONSE
+5761        if !scheduling_info.is_abandoned
+5762            && space_id == SpaceId::Data
+5763            && builder.frame_space_remaining() > frame::PathResponse::SIZE_BOUND
+5764            && let Some(token) = path.path_responses.pop_on_path(path.network_path)
+5765        {
+5766            let response = frame::PathResponse(token);
+5767            builder.write_frame(response, stats);
+5768            builder.require_padding();
+5769
+5770            // NOTE: this is technically not required but might be useful to ride the
+5771            // request/response nature of path challenges to refresh an observation
+5772            // Since PATH_RESPONSE is a probing frame, this is allowed by the spec.
+5773            if space_id == SpaceId::Data
+5774                && self
+5775                    .config
+5776                    .address_discovery_role
+5777                    .should_report(&self.peer_params.address_discovery_role)
+5778            {
+5779                let frame = frame::ObservedAddr::new(
+5780                    path.network_path.remote,
+5781                    self.next_observed_addr_seq_no,
+5782                );
+5783                if builder.frame_space_remaining() > frame.size() {
+5784                    builder.write_frame(frame, stats);
+5785
+5786                    self.next_observed_addr_seq_no =
+5787                        self.next_observed_addr_seq_no.saturating_add(1u8);
+5788                    path.observed_addr_sent = true;
+5789
+5790                    space.pending.observed_addr = false;
+5791                }
+5792            }
+5793        }
+5794
+5795        // REACH_OUT
+5796        if !scheduling_info.is_abandoned
+5797            && scheduling_info.may_send_data
+5798            && let Some((round, addresses)) = space.pending.reach_out.as_mut()
+5799        {
+5800            while let Some(local_addr) = addresses.iter().next().copied() {
+5801                let local_addr = addresses.take(&local_addr).expect("found from iter");
+5802                let reach_out = frame::ReachOut::new(*round, local_addr);
+5803                if builder.frame_space_remaining() > reach_out.size() {
+5804                    builder.write_frame(reach_out, stats);
+5805                } else {
+5806                    addresses.insert(local_addr);
+5807                    break;
+5808                }
+5809            }
+5810            if addresses.is_empty() {
+5811                space.pending.reach_out = None;
+5812            }
+5813        }
+5814
+5815        // PATH_ABANDON
+5816        if space_id == SpaceId::Data
+5817            && scheduling_info.is_abandoned
+5818            && scheduling_info.may_self_abandon
+5819            && frame::PathAbandon::SIZE_BOUND <= builder.frame_space_remaining()
+5820            && let Some(error_code) = space.pending.path_abandon.remove(&path_id)
+5821        {
+5822            let frame = frame::PathAbandon {
+5823                path_id,
+5824                error_code,
+5825            };
+5826            builder.write_frame(frame, stats);
+5827
+5828            // Consider remotely issued CIDs as retired now that we have sent this frame at
+5829            // least once.
+5830            self.remote_cids.remove(&path_id);
+5831        }
+5832        while space_id == SpaceId::Data
+5833            && scheduling_info.may_send_data
+5834            && frame::PathAbandon::SIZE_BOUND <= builder.frame_space_remaining()
+5835            && let Some((abandoned_path_id, error_code)) = space.pending.path_abandon.pop_first()
+5836        {
+5837            let frame = frame::PathAbandon {
+5838                path_id: abandoned_path_id,
+5839                error_code,
+5840            };
+5841            builder.write_frame(frame, stats);
+5842
+5843            // Consider remotely issued CIDs as retired now that we have sent this frame at
+5844            // least once.
+5845            self.remote_cids.remove(&abandoned_path_id);
+5846        }
+5847
+5848        // OBSERVED_ADDR
+5849        if !scheduling_info.is_abandoned
+5850            && scheduling_info.may_send_data
+5851            && space_id == SpaceId::Data
+5852            && self
+5853                .config
+5854                .address_discovery_role
+5855                .should_report(&self.peer_params.address_discovery_role)
+5856            && (!path.observed_addr_sent || space.pending.observed_addr)
+5857        {
+5858            let frame =
+5859                frame::ObservedAddr::new(path.network_path.remote, self.next_observed_addr_seq_no);
+5860            if builder.frame_space_remaining() > frame.size() {
+5861                builder.write_frame(frame, stats);
+5862
+5863                self.next_observed_addr_seq_no = self.next_observed_addr_seq_no.saturating_add(1u8);
+5864                path.observed_addr_sent = true;
+5865
+5866                space.pending.observed_addr = false;
+5867            }
+5868        }
+5869
+5870        // CRYPTO
+5871        while !is_0rtt
+5872            && !scheduling_info.is_abandoned
+5873            && scheduling_info.may_send_data
+5874            && builder.frame_space_remaining() > frame::Crypto::SIZE_BOUND
+5875        {
+5876            let Some(mut frame) = space.pending.crypto.pop_front() else {
+5877                break;
+5878            };
+5879
+5880            // Calculate the maximum amount of crypto data we can store in the buffer.
+5881            // Since the offset is known, we can reserve the exact size required to encode it.
+5882            // For length we reserve 2bytes which allows to encode up to 2^14,
+5883            // which is more than what fits into normally sized QUIC frames.
+5884            let max_crypto_data_size = builder.frame_space_remaining()
+5885                - 1 // Frame Type
+5886                - VarInt::size(unsafe { VarInt::from_u64_unchecked(frame.offset) })
+5887                - 2; // Maximum encoded length for frame size, given we send less than 2^14 bytes
+5888
+5889            let len = frame
+5890                .data
+5891                .len()
+5892                .min(2usize.pow(14) - 1)
+5893                .min(max_crypto_data_size);
+5894
+5895            let data = frame.data.split_to(len);
+5896            let offset = frame.offset;
+5897            let truncated = frame::Crypto { offset, data };
+5898            builder.write_frame(truncated, stats);
+5899
+5900            if !frame.data.is_empty() {
+5901                frame.offset += len as u64;
+5902                space.pending.crypto.push_front(frame);
+5903            }
+5904        }
+5905
+5906        // PATH_STATUS_AVAILABLE & PATH_STATUS_BACKUP
+5907        while space_id == SpaceId::Data
+5908            && !scheduling_info.is_abandoned
+5909            && scheduling_info.may_send_data
+5910            && frame::PathStatusAvailable::SIZE_BOUND <= builder.frame_space_remaining()
+5911        {
+5912            let Some(path_id) = space.pending.path_status.pop_first() else {
+5913                break;
+5914            };
+5915            let Some(path) = self.paths.get(&path_id).map(|path_state| &path_state.data) else {
+5916                trace!(%path_id, "discarding queued path status for unknown path");
+5917                continue;
+5918            };
+5919
+5920            let seq = path.status.seq();
+5921            match path.local_status() {
+5922                PathStatus::Available => {
+5923                    let frame = frame::PathStatusAvailable {
+5924                        path_id,
+5925                        status_seq_no: seq,
+5926                    };
+5927                    builder.write_frame(frame, stats);
+5928                }
+5929                PathStatus::Backup => {
+5930                    let frame = frame::PathStatusBackup {
+5931                        path_id,
+5932                        status_seq_no: seq,
+5933                    };
+5934                    builder.write_frame(frame, stats);
+5935                }
+5936            }
+5937        }
+5938
+5939        // MAX_PATH_ID
+5940        if space_id == SpaceId::Data
+5941            && !scheduling_info.is_abandoned
+5942            && scheduling_info.may_send_data
+5943            && space.pending.max_path_id
+5944            && frame::MaxPathId::SIZE_BOUND <= builder.frame_space_remaining()
+5945        {
+5946            let frame = frame::MaxPathId(self.local_max_path_id);
+5947            builder.write_frame(frame, stats);
+5948            space.pending.max_path_id = false;
+5949        }
+5950
+5951        // PATHS_BLOCKED
+5952        if space_id == SpaceId::Data
+5953            && !scheduling_info.is_abandoned
+5954            && scheduling_info.may_send_data
+5955            && space.pending.paths_blocked
+5956            && frame::PathsBlocked::SIZE_BOUND <= builder.frame_space_remaining()
+5957        {
+5958            let frame = frame::PathsBlocked(self.remote_max_path_id);
+5959            builder.write_frame(frame, stats);
+5960            space.pending.paths_blocked = false;
+5961        }
+5962
+5963        // PATH_CIDS_BLOCKED
+5964        while space_id == SpaceId::Data
+5965            && !scheduling_info.is_abandoned
+5966            && scheduling_info.may_send_data
+5967            && frame::PathCidsBlocked::SIZE_BOUND <= builder.frame_space_remaining()
+5968        {
+5969            let Some(path_id) = space.pending.path_cids_blocked.pop_first() else {
+5970                break;
+5971            };
+5972            let next_seq = match self.remote_cids.get(&path_id) {
+5973                Some(cid_queue) => VarInt(cid_queue.active_seq() + 1),
+5974                None => VarInt(0),
+5975            };
+5976            let frame = frame::PathCidsBlocked { path_id, next_seq };
+5977            builder.write_frame(frame, stats);
+5978        }
+5979
+5980        // RESET_STREAM, STOP_SENDING, MAX_DATA, MAX_STREAM_DATA, MAX_STREAMS
+5981        if space_id == SpaceId::Data
+5982            && !scheduling_info.is_abandoned
+5983            && scheduling_info.may_send_data
+5984        {
+5985            self.streams
+5986                .write_control_frames(builder, &mut space.pending, stats);
+5987        }
+5988
+5989        // NEW_CONNECTION_ID
+5990        let cid_len = self
+5991            .local_cid_state
+5992            .values()
+5993            .map(|cid_state| cid_state.cid_len())
+5994            .max()
+5995            .expect("some local CID state must exist");
+5996        let new_cid_size_bound =
+5997            frame::NewConnectionId::size_bound(is_multipath_negotiated, cid_len);
+5998        while !scheduling_info.is_abandoned
+5999            && scheduling_info.may_send_data
+6000            && builder.frame_space_remaining() > new_cid_size_bound
+6001        {
+6002            let Some(issued) = space.pending.new_cids.pop() else {
+6003                break;
+6004            };
+6005            let retire_prior_to = self
+6006                .local_cid_state
+6007                .get(&issued.path_id)
+6008                .map(|cid_state| cid_state.retire_prior_to())
+6009                .unwrap_or_else(|| panic!("missing local CID state for path={}", issued.path_id));
+6010
+6011            let cid_path_id = match is_multipath_negotiated {
+6012                true => Some(issued.path_id),
+6013                false => {
+6014                    debug_assert_eq!(issued.path_id, PathId::ZERO);
+6015                    None
+6016                }
+6017            };
+6018            let frame = frame::NewConnectionId {
+6019                path_id: cid_path_id,
+6020                sequence: issued.sequence,
+6021                retire_prior_to,
+6022                id: issued.id,
+6023                reset_token: issued.reset_token,
+6024            };
+6025            builder.write_frame(frame, stats);
+6026        }
+6027
+6028        // RETIRE_CONNECTION_ID
+6029        let retire_cid_bound = frame::RetireConnectionId::size_bound(is_multipath_negotiated);
+6030        while !scheduling_info.is_abandoned
+6031            && scheduling_info.may_send_data
+6032            && builder.frame_space_remaining() > retire_cid_bound
+6033        {
+6034            let (path_id, sequence) = match space.pending.retire_cids.pop() {
+6035                Some((PathId::ZERO, seq)) if !is_multipath_negotiated => (None, seq),
+6036                Some((path_id, seq)) => (Some(path_id), seq),
+6037                None => break,
+6038            };
+6039            let frame = frame::RetireConnectionId { path_id, sequence };
+6040            builder.write_frame(frame, stats);
+6041        }
+6042
+6043        // DATAGRAM
+6044        let mut sent_datagrams = false;
+6045        while !scheduling_info.is_abandoned
+6046            && scheduling_info.may_send_data
+6047            && builder.frame_space_remaining() > Datagram::SIZE_BOUND
+6048            && space_id == SpaceId::Data
+6049        {
+6050            match self.datagrams.write(builder, stats) {
+6051                true => {
+6052                    sent_datagrams = true;
+6053                }
+6054                false => break,
+6055            }
+6056        }
+6057        if self.datagrams.send_blocked && sent_datagrams {
+6058            self.events.push_back(Event::DatagramsUnblocked);
+6059            self.datagrams.send_blocked = false;
+6060        }
+6061
+6062        let path = &mut self.paths.get_mut(&path_id).expect("known path").data;
+6063
+6064        // NEW_TOKEN
+6065        if !scheduling_info.is_abandoned && scheduling_info.may_send_data {
+6066            while let Some(network_path) = space.pending.new_tokens.pop() {
+6067                debug_assert_eq!(space_id, SpaceId::Data);
+6068                let ConnectionSide::Server { server_config } = &self.side else {
+6069                    panic!("NEW_TOKEN frames should not be enqueued by clients");
+6070                };
+6071
+6072                if !network_path.is_probably_same_path(&path.network_path) {
+6073                    // NEW_TOKEN frames contain tokens bound to a client's IP address, and are only
+6074                    // useful if used from the same IP address.  Thus, we abandon enqueued NEW_TOKEN
+6075                    // frames upon an path change. Instead, when the new path becomes validated,
+6076                    // NEW_TOKEN frames may be enqueued for the new path instead.
+6077                    continue;
+6078                }
+6079
+6080                let token = Token::new(
+6081                    TokenPayload::Validation {
+6082                        ip: network_path.remote.ip(),
+6083                        issued: server_config.time_source.now(),
+6084                    },
+6085                    &mut self.rng,
+6086                );
+6087                let new_token = NewToken {
+6088                    token: token.encode(&*server_config.token_key).into(),
+6089                };
+6090
+6091                if builder.frame_space_remaining() < new_token.size() {
+6092                    space.pending.new_tokens.push(network_path);
+6093                    break;
+6094                }
+6095
+6096                builder.write_frame(new_token, stats);
+6097                builder.retransmits_mut().new_tokens.push(network_path);
+6098            }
+6099        }
+6100
+6101        // STREAM
+6102        if !scheduling_info.is_abandoned
+6103            && scheduling_info.may_send_data
+6104            && space_id == SpaceId::Data
+6105        {
+6106            self.streams
+6107                .write_stream_frames(builder, self.config.send_fairness, stats);
+6108        }
+6109
+6110        // ADD_ADDRESS
+6111        while space_id == SpaceId::Data
+6112            && !scheduling_info.is_abandoned
+6113            && scheduling_info.may_send_data
+6114            && frame::AddAddress::SIZE_BOUND <= builder.frame_space_remaining()
+6115        {
+6116            if let Some(added_address) = space.pending.add_address.pop_last() {
+6117                builder.write_frame(added_address, stats);
+6118            } else {
+6119                break;
+6120            }
+6121        }
+6122
+6123        // REMOVE_ADDRESS
+6124        while space_id == SpaceId::Data
+6125            && !scheduling_info.is_abandoned
+6126            && scheduling_info.may_send_data
+6127            && frame::RemoveAddress::SIZE_BOUND <= builder.frame_space_remaining()
+6128        {
+6129            if let Some(removed_address) = space.pending.remove_address.pop_last() {
+6130                builder.write_frame(removed_address, stats);
+6131            } else {
+6132                break;
+6133            }
+6134        }
+6135    }
+6136
+6137    /// Write pending ACKs into a buffer
+6138    fn populate_acks<'a, 'b>(
+6139        now: Instant,
+6140        receiving_ecn: bool,
+6141        path_id: PathId,
+6142        space_id: SpaceId,
+6143        space: &mut PacketSpace,
+6144        is_multipath_negotiated: bool,
+6145        builder: &mut PacketBuilder<'a, 'b>,
+6146        stats: &mut FrameStats,
+6147        space_has_keys: bool,
+6148    ) {
+6149        // 0-RTT packets must never carry acks (which would have to be of handshake packets)
+6150        debug_assert!(space_has_keys, "tried to send ACK in 0-RTT");
+6151
+6152        debug_assert!(
+6153            is_multipath_negotiated || path_id == PathId::ZERO,
+6154            "Only PathId::ZERO allowed without multipath (have {path_id:?})"
+6155        );
+6156        if is_multipath_negotiated {
+6157            debug_assert!(
+6158                space_id == SpaceId::Data || path_id == PathId::ZERO,
+6159                "path acks must be sent in 1RTT space (have {space_id:?})"
+6160            );
+6161        }
+6162
+6163        let pns = space.for_path(path_id);
+6164        let ranges = pns.pending_acks.ranges();
+6165        debug_assert!(!ranges.is_empty(), "can not send empty ACK range");
+6166        let ecn = if receiving_ecn {
+6167            Some(&pns.ecn_counters)
+6168        } else {
+6169            None
+6170        };
+6171
+6172        let delay_micros = pns.pending_acks.ack_delay(now).as_micros() as u64;
+6173        // TODO: This should come from `TransportConfig` if that gets configurable.
+6174        let ack_delay_exp = TransportParameters::default().ack_delay_exponent;
+6175        let delay = delay_micros >> ack_delay_exp.into_inner();
+6176
+6177        if is_multipath_negotiated && space_id == SpaceId::Data {
+6178            if !ranges.is_empty() {
+6179                let frame = frame::PathAck::encoder(path_id, delay, ranges, ecn);
+6180                builder.write_frame(frame, stats);
+6181            }
+6182        } else {
+6183            builder.write_frame(frame::Ack::encoder(delay, ranges, ecn), stats);
+6184        }
+6185    }
+6186
+6187    fn close_common(&mut self) {
+6188        trace!("connection closed");
+6189        self.timers.reset();
+6190    }
+6191
+6192    fn set_close_timer(&mut self, now: Instant) {
+6193        // QUIC-MULTIPATH § 2.6 Connection Closure: draining for 3*PTO using the max PTO of
+6194        // all paths.
+6195        let pto_max = self.max_pto_for_space(self.highest_space);
+6196        self.timers.set(
+6197            Timer::Conn(ConnTimer::Close),
+6198            now + 3 * pto_max,
+6199            self.qlog.with_time(now),
+6200        );
+6201    }
+6202
+6203    /// Handle transport parameters received from the peer
+6204    ///
+6205    /// *remote_cid* and *local_cid* are the source and destination CIDs respectively of the
+6206    /// *packet into which the transport parameters arrived.
+6207    fn handle_peer_params(
+6208        &mut self,
+6209        params: TransportParameters,
+6210        local_cid: ConnectionId,
+6211        remote_cid: ConnectionId,
+6212        now: Instant,
+6213    ) -> Result<(), TransportError> {
+6214        if Some(self.original_remote_cid) != params.initial_src_cid
+6215            || (self.side.is_client()
+6216                && (Some(self.initial_dst_cid) != params.original_dst_cid
+6217                    || self.retry_src_cid != params.retry_src_cid))
+6218        {
+6219            return Err(TransportError::TRANSPORT_PARAMETER_ERROR(
+6220                "CID authentication failure",
+6221            ));
+6222        }
+6223        if params.initial_max_path_id.is_some() && (local_cid.is_empty() || remote_cid.is_empty()) {
+6224            return Err(TransportError::PROTOCOL_VIOLATION(
+6225                "multipath must not use zero-length CIDs",
+6226            ));
+6227        }
+6228
+6229        self.set_peer_params(params);
+6230        self.qlog.emit_peer_transport_params_received(self, now);
+6231
+6232        Ok(())
+6233    }
+6234
+6235    fn set_peer_params(&mut self, params: TransportParameters) {
+6236        self.streams.set_params(&params);
+6237        self.idle_timeout =
+6238            negotiate_max_idle_timeout(self.config.max_idle_timeout, Some(params.max_idle_timeout));
+6239        trace!("negotiated max idle timeout {:?}", self.idle_timeout);
+6240
+6241        if let Some(ref info) = params.preferred_address {
+6242            // During the handshake PathId::ZERO exists.
+6243            self.remote_cids.get_mut(&PathId::ZERO).expect("not yet abandoned").insert(frame::NewConnectionId {
+6244                path_id: None,
+6245                sequence: 1,
+6246                id: info.connection_id,
+6247                reset_token: info.stateless_reset_token,
+6248                retire_prior_to: 0,
+6249            })
+6250            .expect(
+6251                "preferred address CID is the first received, and hence is guaranteed to be legal",
+6252            );
+6253            let remote = self.path_data(PathId::ZERO).network_path.remote;
+6254            self.set_reset_token(PathId::ZERO, remote, info.stateless_reset_token);
+6255        }
+6256        self.ack_frequency.peer_max_ack_delay = get_max_ack_delay(&params);
+6257
+6258        let mut multipath_enabled = None;
+6259        if let (Some(local_max_path_id), Some(remote_max_path_id)) = (
+6260            self.config.get_initial_max_path_id(),
+6261            params.initial_max_path_id,
+6262        ) {
+6263            // multipath is enabled, register the local and remote maximums
+6264            self.local_max_path_id = local_max_path_id;
+6265            self.remote_max_path_id = remote_max_path_id;
+6266            let initial_max_path_id = local_max_path_id.min(remote_max_path_id);
+6267            debug!(%initial_max_path_id, "multipath negotiated");
+6268            multipath_enabled = Some(initial_max_path_id);
+6269        }
+6270
+6271        if let Some((max_locally_allowed_remote_addresses, max_remotely_allowed_remote_addresses)) =
+6272            self.config
+6273                .max_remote_nat_traversal_addresses
+6274                .zip(params.max_remote_nat_traversal_addresses)
+6275        {
+6276            if let Some(max_initial_paths) =
+6277                multipath_enabled.map(|path_id| path_id.saturating_add(1u8))
+6278            {
+6279                let max_local_addresses = max_remotely_allowed_remote_addresses.get();
+6280                let max_remote_addresses = max_locally_allowed_remote_addresses.get();
+6281                self.n0_nat_traversal = n0_nat_traversal::State::new(
+6282                    max_remote_addresses,
+6283                    max_local_addresses,
+6284                    self.side(),
+6285                );
+6286                debug!(
+6287                    %max_remote_addresses, %max_local_addresses,
+6288                    "n0's nat traversal negotiated"
+6289                );
+6290
+6291                match self.side() {
+6292                    Side::Client => {
+6293                        if max_initial_paths.as_u32() < max_remote_addresses as u32 + 1 {
+6294                            // in this case the client might try to open `max_remote_addresses` new
+6295                            // paths, but the current multipath configuration will not allow it
+6296                            debug!(%max_initial_paths, %max_remote_addresses, "local client configuration might cause nat traversal issues")
+6297                        } else if max_local_addresses as u64
+6298                            > params.active_connection_id_limit.into_inner()
+6299                        {
+6300                            // the server allows us to send at most `params.active_connection_id_limit`
+6301                            // but they might need at least `max_local_addresses` to effectively send
+6302                            // `PATH_CHALLENGE` frames to each advertised local address
+6303                            debug!(%max_local_addresses, remote_cid_limit=%params.active_connection_id_limit.into_inner(), "remote server configuration might cause nat traversal issues")
+6304                        }
+6305                    }
+6306                    Side::Server => {
+6307                        if (max_initial_paths.as_u32() as u64) < crate::LOCAL_CID_COUNT {
+6308                            debug!(%max_initial_paths, local_cid_limit=%crate::LOCAL_CID_COUNT, "local server configuration might cause nat traversal issues")
+6309                        }
+6310                    }
+6311                }
+6312            } else {
+6313                debug!("n0 nat traversal enabled for both endpoints, but multipath is missing")
+6314            }
+6315        }
+6316
+6317        self.peer_params = params;
+6318        let peer_max_udp_payload_size =
+6319            u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX);
+6320        self.path_data_mut(PathId::ZERO)
+6321            .mtud
+6322            .on_peer_max_udp_payload_size_received(peer_max_udp_payload_size);
+6323    }
+6324
+6325    /// Decrypts a packet, returning the packet number on success
+6326    fn decrypt_packet(
+6327        &mut self,
+6328        now: Instant,
+6329        path_id: PathId,
+6330        packet: &mut Packet,
+6331    ) -> Result<Option<u64>, Option<TransportError>> {
+6332        let result = self
+6333            .crypto_state
+6334            .decrypt_packet_body(packet, path_id, &self.spaces)?;
+6335
+6336        let Some(result) = result else {
+6337            return Ok(None);
+6338        };
+6339
+6340        if result.outgoing_key_update_acked
+6341            && let Some(prev) = self.crypto_state.prev_crypto.as_mut()
+6342        {
+6343            prev.end_packet = Some((result.number, now));
+6344            self.set_key_discard_timer(now, packet.header.space());
+6345        }
+6346
+6347        if result.incoming_key_update {
+6348            trace!("key update authenticated");
+6349            self.crypto_state
+6350                .update_keys(Some((result.number, now)), true);
+6351            self.set_key_discard_timer(now, packet.header.space());
+6352        }
+6353
+6354        Ok(Some(result.number))
+6355    }
+6356
+6357    fn peer_supports_ack_frequency(&self) -> bool {
+6358        self.peer_params.min_ack_delay.is_some()
+6359    }
+6360
+6361    /// Send an IMMEDIATE_ACK frame to the remote endpoint
+6362    ///
+6363    /// According to the spec, this will result in an error if the remote endpoint does not support
+6364    /// the Acknowledgement Frequency extension
+6365    pub(crate) fn immediate_ack(&mut self, path_id: PathId) {
+6366        debug_assert_eq!(
+6367            self.highest_space,
+6368            SpaceKind::Data,
+6369            "immediate ack must be written in the data space"
+6370        );
+6371        self.spaces[SpaceId::Data]
+6372            .for_path(path_id)
+6373            .immediate_ack_pending = true;
+6374    }
+6375
+6376    /// Decodes a packet, returning its decrypted payload, so it can be inspected in tests
+6377    #[cfg(test)]
+6378    pub(crate) fn decode_packet(&self, event: &ConnectionEvent) -> Option<Vec<u8>> {
+6379        let ConnectionEventInner::Datagram(DatagramConnectionEvent {
+6380            path_id,
+6381            first_decode,
+6382            remaining,
+6383            ..
+6384        }) = &event.0
+6385        else {
+6386            return None;
+6387        };
+6388
+6389        if remaining.is_some() {
+6390            panic!("Packets should never be coalesced in tests");
+6391        }
+6392
+6393        let decrypted_header = self
+6394            .crypto_state
+6395            .unprotect_header(first_decode.clone(), self.peer_params.stateless_reset_token)?;
+6396
+6397        let mut packet = decrypted_header.packet?;
+6398        self.crypto_state
+6399            .decrypt_packet_body(&mut packet, *path_id, &self.spaces)
+6400            .ok()?;
+6401
+6402        Some(packet.payload.to_vec())
+6403    }
+6404
+6405    /// The number of bytes of packets containing retransmittable frames that have not been
+6406    /// acknowledged or declared lost.
+6407    #[cfg(test)]
+6408    pub(crate) fn bytes_in_flight(&self) -> u64 {
+6409        // TODO(@divma): consider including for multipath?
+6410        self.path_data(PathId::ZERO).in_flight.bytes
+6411    }
+6412
+6413    /// Number of bytes worth of non-ack-only packets that may be sent
+6414    #[cfg(test)]
+6415    pub(crate) fn congestion_window(&self) -> u64 {
+6416        let path = self.path_data(PathId::ZERO);
+6417        path.congestion
+6418            .window()
+6419            .saturating_sub(path.in_flight.bytes)
+6420    }
+6421
+6422    /// Whether no timers but keepalive, idle, rtt, pushnewcid, and key discard are running
+6423    #[cfg(test)]
+6424    pub(crate) fn is_idle(&self) -> bool {
+6425        let current_timers = self.timers.values();
+6426        current_timers
+6427            .into_iter()
+6428            .filter(|(timer, _)| {
+6429                !matches!(
+6430                    timer,
+6431                    Timer::Conn(ConnTimer::KeepAlive)
+6432                        | Timer::PerPath(_, PathTimer::PathKeepAlive)
+6433                        | Timer::Conn(ConnTimer::PushNewCid)
+6434                        | Timer::Conn(ConnTimer::KeyDiscard)
+6435                )
+6436            })
+6437            .min_by_key(|(_, time)| *time)
+6438            .is_none_or(|(timer, _)| timer == Timer::Conn(ConnTimer::Idle))
+6439    }
+6440
+6441    /// Whether explicit congestion notification is in use on outgoing packets.
+6442    #[cfg(test)]
+6443    pub(crate) fn using_ecn(&self) -> bool {
+6444        self.path_data(PathId::ZERO).sending_ecn
+6445    }
+6446
+6447    /// The number of received bytes in the current path
+6448    #[cfg(test)]
+6449    pub(crate) fn total_recvd(&self) -> u64 {
+6450        self.path_data(PathId::ZERO).total_recvd
+6451    }
+6452
+6453    #[cfg(test)]
+6454    pub(crate) fn active_local_cid_seq(&self) -> (u64, u64) {
+6455        self.local_cid_state
+6456            .get(&PathId::ZERO)
+6457            .unwrap()
+6458            .active_seq()
+6459    }
+6460
+6461    #[cfg(test)]
+6462    #[track_caller]
+6463    pub(crate) fn active_local_path_cid_seq(&self, path_id: u32) -> (u64, u64) {
+6464        self.local_cid_state
+6465            .get(&PathId(path_id))
+6466            .unwrap()
+6467            .active_seq()
+6468    }
+6469
+6470    /// Instruct the peer to replace previously issued CIDs by sending a NEW_CONNECTION_ID frame
+6471    /// with updated `retire_prior_to` field set to `v`
+6472    #[cfg(test)]
+6473    pub(crate) fn rotate_local_cid(&mut self, v: u64, now: Instant) {
+6474        let n = self
+6475            .local_cid_state
+6476            .get_mut(&PathId::ZERO)
+6477            .unwrap()
+6478            .assign_retire_seq(v);
+6479        debug_assert!(!self.state.is_drained()); // requirement for endpoint_events
+6480        self.endpoint_events
+6481            .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n));
+6482    }
+6483
+6484    /// Check the current active remote CID sequence for `PathId::ZERO`
+6485    #[cfg(test)]
+6486    pub(crate) fn active_remote_cid_seq(&self) -> u64 {
+6487        self.remote_cids.get(&PathId::ZERO).unwrap().active_seq()
+6488    }
+6489
+6490    /// Returns the detected maximum udp payload size for the current path
+6491    #[cfg(test)]
+6492    pub(crate) fn path_mtu(&self, path_id: PathId) -> u16 {
+6493        self.path_data(path_id).current_mtu()
+6494    }
+6495
+6496    /// Triggers path validation on all paths
+6497    #[cfg(test)]
+6498    pub(crate) fn trigger_path_validation(&mut self) {
+6499        for path in self.paths.values_mut() {
+6500            path.data.pending_on_path_challenge = true;
+6501        }
+6502    }
+6503
+6504    /// Simulates a protocol violation error for test purposes.
+6505    #[cfg(test)]
+6506    pub fn simulate_protocol_violation(&mut self, now: Instant) {
+6507        if !self.state.is_closed() {
+6508            self.state
+6509                .move_to_closed(TransportError::PROTOCOL_VIOLATION("simulated violation"));
+6510            self.close_common();
+6511            if !self.state.is_drained() {
+6512                self.set_close_timer(now);
+6513            }
+6514            self.connection_close_pending = true;
+6515        }
+6516    }
+6517
+6518    /// Whether we have on-path 1-RTT data to send.
+6519    ///
+6520    /// This checks for frames that can only be sent in the data space (1-RTT):
+6521    /// - Pending PATH_CHALLENGE frames on the active and previous path if just migrated.
+6522    /// - Pending PATH_RESPONSE frames.
+6523    /// - Pending data to send in STREAM frames.
+6524    /// - Pending DATAGRAM frames to send.
+6525    ///
+6526    /// See also [`PacketSpace::can_send`] which keeps track of all other frame types that
+6527    /// may need to be sent.
+6528    fn can_send_1rtt(&self, path_id: PathId, max_size: usize) -> SendableFrames {
+6529        let space_only = self.paths.get(&path_id).is_some_and(|path| {
+6530            path.data.pending_on_path_challenge || !path.data.path_responses.is_empty()
+6531        });
+6532
+6533        // Stream control frames are checked in PacketSpace::can_send, only check data here.
+6534        let other = self.streams.can_send_stream_data()
+6535            || self
+6536                .datagrams
+6537                .outgoing
+6538                .front()
+6539                .is_some_and(|x| x.size(true) <= max_size);
+6540
+6541        // All `false` fields are set in PacketSpace::can_send.
+6542        SendableFrames {
+6543            acks: false,
+6544            close: false,
+6545            space_only,
+6546            other,
+6547        }
+6548    }
+6549
+6550    /// Terminate the connection instantly, without sending a close packet
+6551    fn kill(&mut self, reason: ConnectionError) {
+6552        self.close_common();
+6553        self.state.move_to_drained(Some(reason));
+6554        // move_to_drained checks that we were never in drained before, so we
+6555        // never sent a `Drained` event before (it's illegal to send more events after drained).
+6556        self.endpoint_events.push_back(EndpointEventInner::Drained);
+6557    }
+6558
+6559    /// Storage size required for the largest packet that can be transmitted on all currently
+6560    /// available paths
+6561    ///
+6562    /// Buffers passed to [`Connection::poll_transmit`] should be at least this large.
+6563    ///
+6564    /// When multipath is enabled, this value is the minimum MTU across all available paths.
+6565    pub fn current_mtu(&self) -> u16 {
+6566        self.paths
+6567            .iter()
+6568            .filter(|&(path_id, _path_state)| !self.abandoned_paths.contains(path_id))
+6569            .map(|(_path_id, path_state)| path_state.data.current_mtu())
+6570            .min()
+6571            .expect("There is always at least one available path")
+6572    }
+6573
+6574    /// Size of non-frame data for a 1-RTT packet
+6575    ///
+6576    /// Quantifies space consumed by the QUIC header and AEAD tag. All other bytes in a packet are
+6577    /// frames. Changes if the length of the remote connection ID changes, which is expected to be
+6578    /// rare. If `pn` is specified, may additionally change unpredictably due to variations in
+6579    /// latency and packet loss.
+6580    fn predict_1rtt_overhead(&mut self, pn: u64, path: PathId) -> usize {
+6581        let pn_len = PacketNumber::new(
+6582            pn,
+6583            self.spaces[SpaceId::Data]
+6584                .for_path(path)
+6585                .largest_acked_packet
+6586                .unwrap_or(0),
+6587        )
+6588        .len();
+6589
+6590        // 1 byte for flags
+6591        1 + self
+6592            .remote_cids
+6593            .get(&path)
+6594            .map(|cids| cids.active().len())
+6595            .unwrap_or(20)      // Max CID len in QUIC v1
+6596            + pn_len
+6597            + self.tag_len_1rtt()
+6598    }
+6599
+6600    fn predict_1rtt_overhead_no_pn(&self) -> usize {
+6601        let pn_len = 4;
+6602
+6603        let cid_len = self
+6604            .remote_cids
+6605            .values()
+6606            .map(|cids| cids.active().len())
+6607            .max()
+6608            .unwrap_or(20); // Max CID len in QUIC v1
+6609
+6610        // 1 byte for flags
+6611        1 + cid_len + pn_len + self.tag_len_1rtt()
+6612    }
+6613
+6614    fn tag_len_1rtt(&self) -> usize {
+6615        // encryption_keys for Data space returns 1-RTT keys if available, otherwise 0-RTT keys
+6616        let packet_crypto = self
+6617            .crypto_state
+6618            .encryption_keys(SpaceKind::Data, self.side.side())
+6619            .map(|(_header, packet, _level)| packet);
+6620        // If neither Data nor 0-RTT keys are available, make a reasonable tag length guess. As of
+6621        // this writing, all QUIC cipher suites use 16-byte tags. We could return `None` instead,
+6622        // but that would needlessly prevent sending datagrams during 0-RTT.
+6623        packet_crypto.map_or(16, |x| x.tag_len())
+6624    }
+6625
+6626    /// Mark the path as validated, and enqueue NEW_TOKEN frames to be sent as appropriate
+6627    fn on_path_validated(&mut self, path_id: PathId) {
+6628        self.path_data_mut(path_id).validated = true;
+6629        let ConnectionSide::Server { server_config } = &self.side else {
+6630            return;
+6631        };
+6632        let network_path = self.path_data(path_id).network_path;
+6633        let new_tokens = &mut self.spaces[SpaceId::Data as usize].pending.new_tokens;
+6634        new_tokens.clear();
+6635        for _ in 0..server_config.validation_token.sent {
+6636            new_tokens.push(network_path);
+6637        }
+6638    }
+6639
+6640    /// Handle new path status information: PATH_STATUS_AVAILABLE, PATH_STATUS_BACKUP
+6641    fn on_path_status(&mut self, path_id: PathId, status: PathStatus, status_seq_no: VarInt) {
+6642        if let Some(path) = self.paths.get_mut(&path_id) {
+6643            path.data.status.remote_update(status, status_seq_no);
+6644        } else {
+6645            debug!("PATH_STATUS_AVAILABLE received unknown path {:?}", path_id);
+6646        }
+6647        self.events.push_back(
+6648            PathEvent::RemoteStatus {
+6649                id: path_id,
+6650                status,
+6651            }
+6652            .into(),
+6653        );
+6654    }
+6655
+6656    /// Returns the maximum [`PathId`] to be used for sending in this connection.
+6657    ///
+6658    /// This is calculated as minimum between the local and remote's maximums when multipath is
+6659    /// enabled, or `None` when disabled.
+6660    ///
+6661    /// For data that's received, we should use [`Self::local_max_path_id`] instead.
+6662    /// The reasoning is that the remote might already have updated to its own newer
+6663    /// [`Self::max_path_id`] after sending out a `MAX_PATH_ID` frame, but it got re-ordered.
+6664    fn max_path_id(&self) -> Option<PathId> {
+6665        if self.is_multipath_negotiated() {
+6666            Some(self.remote_max_path_id.min(self.local_max_path_id))
+6667        } else {
+6668            None
+6669        }
+6670    }
+6671
+6672    /// Returns whether this connection has a socket that supports IPv6.
+6673    ///
+6674    /// TODO(matheus23): This is related to noq endpoint state's `ipv6` bool. We should move that info
+6675    /// here instead of trying to hack around not knowing it exactly.
+6676    fn is_ipv6(&self) -> bool {
+6677        self.paths
+6678            .values()
+6679            .any(|p| p.data.network_path.remote.is_ipv6())
+6680    }
+6681
+6682    /// Add addresses the local endpoint considers are reachable for nat traversal.
+6683    pub fn add_nat_traversal_address(
+6684        &mut self,
+6685        address: SocketAddr,
+6686    ) -> Result<(), n0_nat_traversal::Error> {
+6687        if let Some(added) = self.n0_nat_traversal.add_local_address(address)? {
+6688            self.spaces[SpaceId::Data].pending.add_address.insert(added);
+6689        };
+6690        Ok(())
+6691    }
+6692
+6693    /// Removes an address the endpoing no longer considers reachable for nat traversal
+6694    ///
+6695    /// Addresses not present in the set will be silently ignored.
+6696    pub fn remove_nat_traversal_address(
+6697        &mut self,
+6698        address: SocketAddr,
+6699    ) -> Result<(), n0_nat_traversal::Error> {
+6700        if let Some(removed) = self.n0_nat_traversal.remove_local_address(address)? {
+6701            self.spaces[SpaceId::Data]
+6702                .pending
+6703                .remove_address
+6704                .insert(removed);
+6705        }
+6706        Ok(())
+6707    }
+6708
+6709    /// Get the current local nat traversal addresses
+6710    pub fn get_local_nat_traversal_addresses(
+6711        &self,
+6712    ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> {
+6713        self.n0_nat_traversal.get_local_nat_traversal_addresses()
+6714    }
+6715
+6716    /// Get the currently advertised nat traversal addresses by the server
+6717    pub fn get_remote_nat_traversal_addresses(
+6718        &self,
+6719    ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> {
+6720        Ok(self
+6721            .n0_nat_traversal
+6722            .client_side()?
+6723            .get_remote_nat_traversal_addresses())
+6724    }
+6725
+6726    /// Attempts to open a path for nat traversal.
+6727    ///
+6728    /// On success returns the [`PathId`] and remote address of the path.
+6729    fn open_nat_traversal_path(
+6730        &mut self,
+6731        now: Instant,
+6732        ip_port: (IpAddr, u16),
+6733    ) -> Result<Option<(PathId, SocketAddr)>, PathError> {
+6734        let remote = ip_port.into();
+6735        // TODO(matheus23): Probe the correct 4-tuple, instead of only a remote address?
+6736        // By specifying None for `local_ip`, we do two things: 1. open_path_ensure won't
+6737        // generate two paths to the same remote and 2. we let the OS choose which
+6738        // interface to use for sending on that path.
+6739        let network_path = FourTuple {
+6740            remote,
+6741            local_ip: None,
+6742        };
+6743        match self.open_path_ensure(network_path, PathStatus::Backup, now) {
+6744            Ok((path_id, path_was_known)) => {
+6745                if path_was_known {
+6746                    trace!(%path_id, %remote, "nat traversal: path existed for remote");
+6747                }
+6748                Ok(Some((path_id, remote)))
+6749            }
+6750            Err(e) => {
+6751                debug!(%remote, %e, "nat traversal: failed to probe remote");
+6752                Err(e)
+6753            }
+6754        }
+6755    }
+6756
+6757    /// Initiates a new nat traversal round
+6758    ///
+6759    /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT`
+6760    /// frames, and initiating probing of the known remote addresses. When a new round is
+6761    /// initiated, the previous one is cancelled, and paths that have not been opened are closed.
+6762    ///
+6763    /// Returns the server addresses that are now being probed.
+6764    /// If addresses fail due to spurious errors, these might succeed later and not be returned in
+6765    /// this set.
+6766    pub fn initiate_nat_traversal_round(
+6767        &mut self,
+6768        now: Instant,
+6769    ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> {
+6770        if self.state.is_closed() {
+6771            return Err(n0_nat_traversal::Error::Closed);
+6772        }
+6773
+6774        let ipv6 = self.is_ipv6();
+6775        let client_state = self.n0_nat_traversal.client_side_mut()?;
+6776        let n0_nat_traversal::NatTraversalRound {
+6777            new_round,
+6778            reach_out_at,
+6779            addresses_to_probe,
+6780            prev_round_path_ids,
+6781        } = client_state.initiate_nat_traversal_round(ipv6)?;
+6782
+6783        trace!(%new_round, reach_out=reach_out_at.len(), to_probe=addresses_to_probe.len(),
+6784            "initiating nat traversal round");
+6785
+6786        self.spaces[SpaceId::Data].pending.reach_out = Some((new_round, reach_out_at));
+6787
+6788        for path_id in prev_round_path_ids {
+6789            let Some(path) = self.path(path_id) else {
+6790                continue;
+6791            };
+6792            let ip = path.network_path.remote.ip();
+6793            let port = path.network_path.remote.port();
+6794
+6795            // We only close paths that aren't validated (thus are working) that we opened
+6796            // in a previous round.
+6797            // And we only close paths that we don't want to probe anyways.
+6798            if !addresses_to_probe
+6799                .iter()
+6800                .any(|(_, probe)| *probe == (ip, port))
+6801                && !path.validated
+6802                && !self.abandoned_paths.contains(&path_id)
+6803            {
+6804                trace!(%path_id, "closing path from previous round");
+6805                let _ =
+6806                    self.close_path_inner(now, path_id, PathAbandonReason::NatTraversalRoundEnded);
+6807            }
+6808        }
+6809
+6810        let mut err = None;
+6811
+6812        let mut path_ids = Vec::with_capacity(addresses_to_probe.len());
+6813        let mut probed_addresses = Vec::with_capacity(addresses_to_probe.len());
+6814
+6815        for (id, address) in addresses_to_probe {
+6816            match self.open_nat_traversal_path(now, address) {
+6817                Ok(None) => {}
+6818                Ok(Some((path_id, remote))) => {
+6819                    path_ids.push(path_id);
+6820                    probed_addresses.push(remote);
+6821                }
+6822                Err(e) => {
+6823                    self.n0_nat_traversal
+6824                        .client_side_mut()
+6825                        .expect("validated")
+6826                        .report_in_continuation(id, e);
+6827                    err.get_or_insert(e);
+6828                }
+6829            }
+6830        }
+6831
+6832        if let Some(err) = err {
+6833            // We failed to probe any addresses, bail out
+6834            if probed_addresses.is_empty() {
+6835                return Err(n0_nat_traversal::Error::Multipath(err));
+6836            }
+6837        }
+6838
+6839        self.n0_nat_traversal
+6840            .client_side_mut()
+6841            .expect("connection side validated")
+6842            .set_round_path_ids(path_ids);
+6843
+6844        Ok(probed_addresses)
+6845    }
+6846
+6847    /// Attempts to continue a nat traversal round by trying to open paths for pending client probes.
+6848    ///
+6849    /// If there was nothing to do, it returns `None`. Otherwise it returns whether the path was
+6850    /// successfully open.
+6851    fn continue_nat_traversal_round(&mut self, now: Instant) -> Option<bool> {
+6852        let ipv6 = self.is_ipv6();
+6853        let client_state = self.n0_nat_traversal.client_side_mut().ok()?;
+6854        let (id, address) = client_state.continue_nat_traversal_round(ipv6)?;
+6855        let open_result = self.open_nat_traversal_path(now, address);
+6856        let client_state = self.n0_nat_traversal.client_side_mut().expect("validated");
+6857        match open_result {
+6858            Ok(None) => Some(true),
+6859            Ok(Some((path_id, _remote))) => {
+6860                client_state.add_round_path_id(path_id);
+6861                Some(true)
+6862            }
+6863            Err(e) => {
+6864                client_state.report_in_continuation(id, e);
+6865                Some(false)
+6866            }
+6867        }
+6868    }
+6869}
+6870
+6871impl fmt::Debug for Connection {
+6872    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+6873        f.debug_struct("Connection")
+6874            .field("handshake_cid", &self.handshake_cid)
+6875            .finish()
+6876    }
+6877}
+6878
+6879/// Hints when the caller identifies a network change.
+6880pub trait NetworkChangeHint: std::fmt::Debug + 'static {
+6881    /// Inform the connection if a path may recover after a network change.
+6882    ///
+6883    /// After network changes, paths may not be recoverable. In this case, waiting for the path to
+6884    /// become idle may take longer than what is desirable. If [`Self::is_path_recoverable`]
+6885    /// returns `false`, a multipath-enabled, client-side connection will establish a new path to
+6886    /// the same remote, closing the current one, instead of migrating the path.
+6887    ///
+6888    /// Paths that are deemed recoverable will simply be sent a PING for a liveness check.
+6889    fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool;
+6890}
+6891
+6892/// Return value for [`Connection::poll_transmit_on_path`].
+6893#[derive(Debug)]
+6894enum PollPathStatus {
+6895    /// Nothing to send on the path, nothing was written into the [`TransmitBuf`].
+6896    NothingToSend {
+6897        /// If true there was data to send but congestion control did not allow so.
+6898        congestion_blocked: bool,
+6899    },
+6900    /// The transmit is ready to be sent.
+6901    Send(Transmit),
+6902}
+6903
+6904/// Return value for [`Connection::poll_transmit_path_space`].
+6905#[derive(Debug)]
+6906enum PollPathSpaceStatus {
+6907    /// Nothing to send in the space, nothing was written into the [`TransmitBuf`].
+6908    NothingToSend {
+6909        /// If true there was data to send but congestion control did not allow so.
+6910        congestion_blocked: bool,
+6911    },
+6912    /// One or more packets have been written into the [`TransmitBuf`].
+6913    WrotePacket {
+6914        /// The highest packet number.
+6915        last_packet_number: u64,
+6916        /// Whether to pad an already started datagram in the next packet.
+6917        ///
+6918        /// When packets in Initial, 0-RTT or Handshake packet do not fill the entire
+6919        /// datagram they may decide to coalesce with the next packet from a higher
+6920        /// encryption level on the same path. But the earlier packet may require specific
+6921        /// size requirements for the datagram they are sent in.
+6922        ///
+6923        /// If a space did not complete the datagram, they use this to request the correct
+6924        /// padding in the final packet of the datagram so that the final datagram will have
+6925        /// the correct size.
+6926        ///
+6927        /// If a space did fill an entire datagram, it leaves this to the default of
+6928        /// [`PadDatagram::No`].
+6929        pad_datagram: PadDatagram,
+6930    },
+6931    /// Send the contents of the transmit immediately.
+6932    ///
+6933    /// Packets were written and the GSO batch must end now, regardless from whether higher
+6934    /// spaces still have frames to write. This is used when the last datagram written would
+6935    /// require too much padding to continue a GSO batch, which would waste space on the
+6936    /// wire.
+6937    Send {
+6938        /// The highest packet number written into the transmit.
+6939        last_packet_number: u64,
+6940    },
+6941}
+6942
+6943/// Information used to decide what frames to schedule into which packets.
+6944///
+6945/// Primarily used by [`Connection::poll_transmit_on_path`] and the functions that help
+6946/// building packets for it: [`Connection::poll_transmit_path_space`] and
+6947/// [`Connection::populate_packet`].
+6948#[derive(Debug, Copy, Clone)]
+6949struct PathSchedulingInfo {
+6950    /// Whether the path is abandoned.
+6951    ///
+6952    /// Note that a path that is abandoned but still has CIDs can still send a packet. After
+6953    /// sending that packet the CIDs issued by the remote have to be considered retired as
+6954    /// well.
+6955    is_abandoned: bool,
+6956    /// Whether the path may send [`SpaceKind::Data`] frames.
+6957    ///
+6958    /// Some paths should only send frames from [`SendableFrames::space_only`]. All other
+6959    /// frames are essentially frames that can be sent on any [`SpaceKind::Data`] space. For
+6960    /// those we want to respect packet scheduling rules however.
+6961    ///
+6962    /// Roughly speaking data frames are only sent on spaces that have CIDs, are not
+6963    /// abandoned and have no *better* spaces. However see to comments where this is
+6964    /// populated for the exact packet scheduling implementation.
+6965    ///
+6966    /// This essentially marks this paths as the best validated space ID. Except during
+6967    /// the handshake in which case it does not need to be validated. Several paths could be
+6968    /// equally good and all have this set to `true`, in that case packet scheduling can
+6969    /// choose which path to use. Currently it chooses the lowest path that is not
+6970    /// congestion blocked.
+6971    ///
+6972    /// Note that once in the closed or draining states this will never be true.
+6973    may_send_data: bool,
+6974    /// Whether the path may send a CONNECTION_CLOSE frame.
+6975    ///
+6976    /// This essentially marks this path as the best validated space ID with a fallback
+6977    /// to unvalidated spaces if there are no validated spaces. Like for
+6978    /// [`Self::may_send_data`] other paths could be equally good.
+6979    may_send_close: bool,
+6980    may_self_abandon: bool,
+6981}
+6982
+6983#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+6984enum PathBlocked {
+6985    No,
+6986    AntiAmplification,
+6987    Congestion,
+6988    Pacing,
+6989}
+6990
+6991/// Fields of `Connection` specific to it being client-side or server-side
+6992enum ConnectionSide {
+6993    Client {
+6994        /// Sent in every outgoing Initial packet. Always empty after Initial keys are discarded
+6995        token: Bytes,
+6996        token_store: Arc<dyn TokenStore>,
+6997        server_name: String,
+6998    },
+6999    Server {
+7000        server_config: Arc<ServerConfig>,
+7001    },
+7002}
+7003
+7004impl ConnectionSide {
+7005    fn remote_may_migrate(&self, state: &State) -> bool {
+7006        match self {
+7007            Self::Server { server_config } => server_config.migration,
+7008            Self::Client { .. } => {
+7009                if let Some(hs) = state.as_handshake() {
+7010                    hs.allow_server_migration
+7011                } else {
+7012                    false
+7013                }
+7014            }
+7015        }
+7016    }
+7017
+7018    fn is_client(&self) -> bool {
+7019        self.side().is_client()
+7020    }
+7021
+7022    fn is_server(&self) -> bool {
+7023        self.side().is_server()
+7024    }
+7025
+7026    fn side(&self) -> Side {
+7027        match *self {
+7028            Self::Client { .. } => Side::Client,
+7029            Self::Server { .. } => Side::Server,
+7030        }
+7031    }
+7032}
+7033
+7034impl From<SideArgs> for ConnectionSide {
+7035    fn from(side: SideArgs) -> Self {
+7036        match side {
+7037            SideArgs::Client {
+7038                token_store,
+7039                server_name,
+7040            } => Self::Client {
+7041                token: token_store.take(&server_name).unwrap_or_default(),
+7042                token_store,
+7043                server_name,
+7044            },
+7045            SideArgs::Server {
+7046                server_config,
+7047                pref_addr_cid: _,
+7048                path_validated: _,
+7049            } => Self::Server { server_config },
+7050        }
+7051    }
+7052}
+7053
+7054/// Parameters to `Connection::new` specific to it being client-side or server-side
+7055pub(crate) enum SideArgs {
+7056    Client {
+7057        token_store: Arc<dyn TokenStore>,
+7058        server_name: String,
+7059    },
+7060    Server {
+7061        server_config: Arc<ServerConfig>,
+7062        pref_addr_cid: Option<ConnectionId>,
+7063        path_validated: bool,
+7064    },
+7065}
+7066
+7067impl SideArgs {
+7068    pub(crate) fn pref_addr_cid(&self) -> Option<ConnectionId> {
+7069        match *self {
+7070            Self::Client { .. } => None,
+7071            Self::Server { pref_addr_cid, .. } => pref_addr_cid,
+7072        }
+7073    }
+7074
+7075    pub(crate) fn path_validated(&self) -> bool {
+7076        match *self {
+7077            Self::Client { .. } => true,
+7078            Self::Server { path_validated, .. } => path_validated,
+7079        }
+7080    }
+7081
+7082    pub(crate) fn side(&self) -> Side {
+7083        match *self {
+7084            Self::Client { .. } => Side::Client,
+7085            Self::Server { .. } => Side::Server,
+7086        }
+7087    }
+7088}
+7089
+7090/// Reasons why a connection might be lost
+7091#[derive(Debug, Error, Clone, PartialEq, Eq)]
+7092pub enum ConnectionError {
+7093    /// The peer doesn't implement any supported version
+7094    #[error("peer doesn't implement any supported version")]
+7095    VersionMismatch,
+7096    /// The peer violated the QUIC specification as understood by this implementation
+7097    #[error(transparent)]
+7098    TransportError(#[from] TransportError),
+7099    /// The peer's QUIC stack aborted the connection automatically
+7100    #[error("aborted by peer: {0}")]
+7101    ConnectionClosed(frame::ConnectionClose),
+7102    /// The peer closed the connection
+7103    #[error("closed by peer: {0}")]
+7104    ApplicationClosed(frame::ApplicationClose),
+7105    /// The peer is unable to continue processing this connection, usually due to having restarted
+7106    #[error("reset by peer")]
+7107    Reset,
+7108    /// Communication with the peer has lapsed for longer than the negotiated idle timeout
+7109    ///
+7110    /// If neither side is sending keep-alives, a connection will time out after a long enough idle
+7111    /// period even if the peer is still reachable. See also [`TransportConfig::max_idle_timeout()`]
+7112    /// and [`TransportConfig::keep_alive_interval()`].
+7113    #[error("timed out")]
+7114    TimedOut,
+7115    /// The local application closed the connection
+7116    #[error("closed")]
+7117    LocallyClosed,
+7118    /// The connection could not be created because not enough of the CID space is available
+7119    ///
+7120    /// Try using longer connection IDs.
+7121    #[error("CIDs exhausted")]
+7122    CidsExhausted,
+7123}
+7124
+7125impl From<Close> for ConnectionError {
+7126    fn from(x: Close) -> Self {
+7127        match x {
+7128            Close::Connection(reason) => Self::ConnectionClosed(reason),
+7129            Close::Application(reason) => Self::ApplicationClosed(reason),
+7130        }
+7131    }
+7132}
+7133
+7134// For compatibility with API consumers
+7135impl From<ConnectionError> for io::Error {
+7136    fn from(x: ConnectionError) -> Self {
+7137        use ConnectionError::*;
+7138        let kind = match x {
+7139            TimedOut => io::ErrorKind::TimedOut,
+7140            Reset => io::ErrorKind::ConnectionReset,
+7141            ApplicationClosed(_) | ConnectionClosed(_) => io::ErrorKind::ConnectionAborted,
+7142            TransportError(_) | VersionMismatch | LocallyClosed | CidsExhausted => {
+7143                io::ErrorKind::Other
+7144            }
+7145        };
+7146        Self::new(kind, x)
+7147    }
+7148}
+7149
+7150/// Errors that might trigger a path being closed
+7151// TODO(@divma): maybe needs to be reworked based on what we want to do with the public API
+7152#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)]
+7153pub enum PathError {
+7154    /// The extension was not negotiated with the peer
+7155    #[error("multipath extension not negotiated")]
+7156    MultipathNotNegotiated,
+7157    /// Paths can only be opened client-side
+7158    #[error("the server side may not open a path")]
+7159    ServerSideNotAllowed,
+7160    /// Current limits do not allow us to open more paths
+7161    #[error("maximum number of concurrent paths reached")]
+7162    MaxPathIdReached,
+7163    /// No remote CIDs available to open a new path
+7164    #[error("remoted CIDs exhausted")]
+7165    RemoteCidsExhausted,
+7166    /// Path could not be validated and will be abandoned
+7167    #[error("path validation failed")]
+7168    ValidationFailed,
+7169    /// The remote address for the path is not supported by the endpoint
+7170    #[error("invalid remote address")]
+7171    InvalidRemoteAddress(SocketAddr),
+7172}
+7173
+7174/// Errors triggered when abandoning a path
+7175#[derive(Debug, Error, Clone, Eq, PartialEq)]
+7176pub enum ClosePathError {
+7177    /// Multipath is not negotiated
+7178    #[error("Multipath extension not negotiated")]
+7179    MultipathNotNegotiated,
+7180    /// The path is already closed or was never opened
+7181    #[error("closed path")]
+7182    ClosedPath,
+7183    /// This is the last path, which can not be abandoned
+7184    #[error("last open path")]
+7185    LastOpenPath,
+7186}
+7187
+7188/// Error when the multipath extension was not negotiated, but attempted to be used.
+7189#[derive(Debug, Error, Clone, Copy)]
+7190#[error("Multipath extension not negotiated")]
+7191pub struct MultipathNotNegotiated {
+7192    _private: (),
+7193}
+7194
+7195/// Events of interest to the application
+7196#[derive(Debug)]
+7197pub enum Event {
+7198    /// The connection's handshake data is ready
+7199    HandshakeDataReady,
+7200    /// The connection was successfully established
+7201    Connected,
+7202    /// The TLS handshake was confirmed
+7203    HandshakeConfirmed,
+7204    /// The connection was lost
+7205    ///
+7206    /// Emitted if the peer closes the connection or an error is encountered.
+7207    ConnectionLost {
+7208        /// Reason that the connection was closed
+7209        reason: ConnectionError,
+7210    },
+7211    /// Stream events
+7212    Stream(StreamEvent),
+7213    /// One or more application datagrams have been received
+7214    DatagramReceived,
+7215    /// One or more application datagrams have been sent after blocking
+7216    DatagramsUnblocked,
+7217    /// (Multi)Path events
+7218    Path(PathEvent),
+7219    /// n0's nat traversal events
+7220    NatTraversal(n0_nat_traversal::Event),
+7221}
+7222
+7223impl From<PathEvent> for Event {
+7224    fn from(source: PathEvent) -> Self {
+7225        Self::Path(source)
+7226    }
+7227}
+7228
+7229fn get_max_ack_delay(params: &TransportParameters) -> Duration {
+7230    Duration::from_micros(params.max_ack_delay.0 * 1000)
+7231}
+7232
+7233// Prevents overflow and improves behavior in extreme circumstances
+7234const MAX_BACKOFF_EXPONENT: u32 = 16;
+7235
+7236/// Minimal remaining size to allow packet coalescing, excluding cryptographic tag
+7237///
+7238/// This must be at least as large as the header for a well-formed empty packet to be coalesced,
+7239/// plus some space for frames. We only care about handshake headers because short header packets
+7240/// necessarily have smaller headers, and initial packets are only ever the first packet in a
+7241/// datagram (because we coalesce in ascending packet space order and the only reason to split a
+7242/// packet is when packet space changes).
+7243const MIN_PACKET_SPACE: usize = MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE + 32;
+7244
+7245/// Largest amount of space that could be occupied by a Handshake or 0-RTT packet's header
+7246///
+7247/// Excludes packet-type-specific fields such as packet number or Initial token
+7248// https://www.rfc-editor.org/rfc/rfc9000.html#name-0-rtt: flags + version + dcid len + dcid +
+7249// scid len + scid + length + pn
+7250const MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE: usize =
+7251    1 + 4 + 1 + MAX_CID_SIZE + 1 + MAX_CID_SIZE + VarInt::from_u32(u16::MAX as u32).size() + 4;
+7252
+7253#[derive(Default)]
+7254struct SentFrames {
+7255    retransmits: ThinRetransmits,
+7256    /// The packet number of the largest acknowledged packet for each path
+7257    largest_acked: FxHashMap<PathId, u64>,
+7258    stream_frames: StreamMetaVec,
+7259    /// Whether the packet contains non-retransmittable frames (like datagrams)
+7260    non_retransmits: bool,
+7261    /// If the datagram containing these frames should be padded to the min MTU
+7262    requires_padding: bool,
+7263}
+7264
+7265impl SentFrames {
+7266    /// Returns whether the packet contains only ACKs
+7267    fn is_ack_only(&self, streams: &StreamsState) -> bool {
+7268        !self.largest_acked.is_empty()
+7269            && !self.non_retransmits
+7270            && self.stream_frames.is_empty()
+7271            && self.retransmits.is_empty(streams)
+7272    }
+7273
+7274    fn retransmits_mut(&mut self) -> &mut Retransmits {
+7275        self.retransmits.get_or_create()
+7276    }
+7277
+7278    fn record_sent_frame(&mut self, frame: frame::EncodableFrame<'_>) {
+7279        use frame::EncodableFrame::*;
+7280        match frame {
+7281            PathAck(path_ack_encoder) => {
+7282                if let Some(max) = path_ack_encoder.ranges.max() {
+7283                    self.largest_acked.insert(path_ack_encoder.path_id, max);
+7284                }
+7285            }
+7286            Ack(ack_encoder) => {
+7287                if let Some(max) = ack_encoder.ranges.max() {
+7288                    self.largest_acked.insert(PathId::ZERO, max);
+7289                }
+7290            }
+7291            Close(_) => { /* non retransmittable, but after this we don't really care */ }
+7292            PathResponse(_) => self.non_retransmits = true,
+7293            HandshakeDone(_) => self.retransmits_mut().handshake_done = true,
+7294            ReachOut(frame::ReachOut { round, ip, port }) => {
+7295                let (recorded_round, reach_outs) = self
+7296                    .retransmits_mut()
+7297                    .reach_out
+7298                    .get_or_insert_with(|| (round, FxHashSet::default()));
+7299                // Only record reach outs for the current round or a newer than the recorded one.
+7300                if *recorded_round == round {
+7301                    // Same round, simply append.
+7302                    reach_outs.insert((ip, port));
+7303                } else if *recorded_round < round {
+7304                    // New round.
+7305                    *recorded_round = round;
+7306                    reach_outs.drain();
+7307                    reach_outs.insert((ip, port));
+7308                } else {
+7309                    // ignore old reach out that was sent
+7310                }
+7311            }
+7312
+7313            ObservedAddr(_) => self.retransmits_mut().observed_addr = true,
+7314            Ping(_) => self.non_retransmits = true,
+7315            ImmediateAck(_) => self.non_retransmits = true,
+7316            AckFrequency(_) => self.retransmits_mut().ack_frequency = true,
+7317            PathChallenge(_) => self.non_retransmits = true,
+7318            Crypto(crypto) => self.retransmits_mut().crypto.push_back(crypto),
+7319            PathAbandon(path_abandon) => {
+7320                self.retransmits_mut()
+7321                    .path_abandon
+7322                    .entry(path_abandon.path_id)
+7323                    .or_insert(path_abandon.error_code);
+7324            }
+7325            PathStatusAvailable(frame::PathStatusAvailable { path_id, .. })
+7326            | PathStatusBackup(frame::PathStatusBackup { path_id, .. }) => {
+7327                self.retransmits_mut().path_status.insert(path_id);
+7328            }
+7329            MaxPathId(_) => self.retransmits_mut().max_path_id = true,
+7330            PathsBlocked(_) => self.retransmits_mut().paths_blocked = true,
+7331            PathCidsBlocked(path_cids_blocked) => {
+7332                self.retransmits_mut()
+7333                    .path_cids_blocked
+7334                    .insert(path_cids_blocked.path_id);
+7335            }
+7336            ResetStream(reset) => self
+7337                .retransmits_mut()
+7338                .reset_stream
+7339                .push((reset.id, reset.error_code)),
+7340            StopSending(stop_sending) => self.retransmits_mut().stop_sending.push(stop_sending),
+7341            NewConnectionId(new_cid) => self.retransmits_mut().new_cids.push(new_cid.issued()),
+7342            RetireConnectionId(retire_cid) => self
+7343                .retransmits_mut()
+7344                .retire_cids
+7345                .push((retire_cid.path_id.unwrap_or_default(), retire_cid.sequence)),
+7346            Datagram(_) => self.non_retransmits = true,
+7347            NewToken(_) => {}
+7348            AddAddress(add_address) => {
+7349                self.retransmits_mut().add_address.insert(add_address);
+7350            }
+7351            RemoveAddress(remove_address) => {
+7352                self.retransmits_mut().remove_address.insert(remove_address);
+7353            }
+7354            StreamMeta(stream_meta_encoder) => self.stream_frames.push(stream_meta_encoder.meta),
+7355            MaxData(_) => self.retransmits_mut().max_data = true,
+7356            MaxStreamData(max) => {
+7357                self.retransmits_mut().max_stream_data.insert(max.id);
+7358            }
+7359            MaxStreams(max_streams) => {
+7360                self.retransmits_mut().max_stream_id[max_streams.dir as usize] = true
+7361            }
+7362        }
+7363    }
+7364}
+7365
+7366/// Compute the negotiated idle timeout based on local and remote max_idle_timeout transport parameters.
+7367///
+7368/// 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.>
+7369///
+7370/// 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.>
+7371///
+7372/// Returns the negotiated idle timeout as a `Duration`, or `None` when both endpoints have opted out of idle timeout.
+7373fn negotiate_max_idle_timeout(x: Option<VarInt>, y: Option<VarInt>) -> Option<Duration> {
+7374    match (x, y) {
+7375        (Some(VarInt(0)) | None, Some(VarInt(0)) | None) => None,
+7376        (Some(VarInt(0)) | None, Some(y)) => Some(Duration::from_millis(y.0)),
+7377        (Some(x), Some(VarInt(0)) | None) => Some(Duration::from_millis(x.0)),
+7378        (Some(x), Some(y)) => Some(Duration::from_millis(cmp::min(x, y).0)),
+7379    }
+7380}
+7381
+7382#[cfg(test)]
+7383mod tests {
+7384    use super::*;
+7385
+7386    #[test]
+7387    fn negotiate_max_idle_timeout_commutative() {
+7388        let test_params = [
+7389            (None, None, None),
+7390            (None, Some(VarInt(0)), None),
+7391            (None, Some(VarInt(2)), Some(Duration::from_millis(2))),
+7392            (Some(VarInt(0)), Some(VarInt(0)), None),
+7393            (
+7394                Some(VarInt(2)),
+7395                Some(VarInt(0)),
+7396                Some(Duration::from_millis(2)),
+7397            ),
+7398            (
+7399                Some(VarInt(1)),
+7400                Some(VarInt(4)),
+7401                Some(Duration::from_millis(1)),
+7402            ),
+7403        ];
+7404
+7405        for (left, right, result) in test_params {
+7406            assert_eq!(negotiate_max_idle_timeout(left, right), result);
+7407            assert_eq!(negotiate_max_idle_timeout(right, left), result);
+7408        }
+7409    }
+7410}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/connection/mtud.rs.html b/pr/509/docs/src/noq_proto/connection/mtud.rs.html new file mode 100644 index 000000000..c102ed67d --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/connection/pacing.rs.html b/pr/509/docs/src/noq_proto/connection/pacing.rs.html new file mode 100644 index 000000000..3dd43abfb --- /dev/null +++ b/pr/509/docs/src/noq_proto/connection/pacing.rs.html @@ -0,0 +1,325 @@ +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    prev: Instant,
+22}
+23
+24impl Pacer {
+25    /// Obtains a new [`Pacer`].
+26    pub(super) fn new(smoothed_rtt: Duration, window: u64, mtu: u16, now: Instant) -> Self {
+27        let capacity = optimal_capacity(smoothed_rtt, window, mtu);
+28        Self {
+29            capacity,
+30            last_window: window,
+31            last_mtu: mtu,
+32            tokens: capacity,
+33            prev: now,
+34        }
+35    }
+36
+37    /// Record that a packet has been transmitted.
+38    pub(super) fn on_transmit(&mut self, packet_length: u16) {
+39        self.tokens = self.tokens.saturating_sub(packet_length.into())
+40    }
+41
+42    /// Return how long we need to wait before sending `bytes_to_send`
+43    ///
+44    /// If we can send a packet right away, this returns `None`. Otherwise, returns `Some(d)`,
+45    /// where `d` is the time before this function should be called again.
+46    ///
+47    /// The 5/4 ratio used here comes from the suggestion that N = 1.25 in the draft IETF RFC for
+48    /// QUIC.
+49    pub(super) fn delay(
+50        &mut self,
+51        smoothed_rtt: Duration,
+52        bytes_to_send: u64,
+53        mtu: u16,
+54        window: u64,
+55        now: Instant,
+56    ) -> Option<Instant> {
+57        debug_assert_ne!(
+58            window, 0,
+59            "zero-sized congestion control window is nonsense"
+60        );
+61
+62        if window != self.last_window || mtu != self.last_mtu {
+63            self.capacity = optimal_capacity(smoothed_rtt, window, mtu);
+64
+65            // Clamp the tokens
+66            self.tokens = self.capacity.min(self.tokens);
+67            self.last_window = window;
+68            self.last_mtu = mtu;
+69        }
+70
+71        // if we can already send a packet, there is no need for delay
+72        if self.tokens >= bytes_to_send {
+73            return None;
+74        }
+75
+76        // we disable pacing for extremely large windows
+77        if window > u64::from(u32::MAX) {
+78            return None;
+79        }
+80
+81        let window = window as u32;
+82
+83        let time_elapsed = now.checked_duration_since(self.prev).unwrap_or_else(|| {
+84            warn!("received a timestamp early than a previous recorded time, ignoring");
+85            Default::default()
+86        });
+87
+88        if smoothed_rtt.as_nanos() == 0 {
+89            return None;
+90        }
+91
+92        let elapsed_rtts = time_elapsed.as_secs_f64() / smoothed_rtt.as_secs_f64();
+93        let new_tokens = (window as f64 * 1.25 * elapsed_rtts).round() as u64;
+94        self.tokens = self.tokens.saturating_add(new_tokens).min(self.capacity);
+95
+96        // In the unlikely event that we're getting polled faster than tokens are generated, ensure
+97        // that `elapsed_rtts` can grow until we make progress.
+98        if new_tokens > 0 {
+99            self.prev = now;
+100        }
+101
+102        // if we can already send a packet, there is no need for delay
+103        if self.tokens >= bytes_to_send {
+104            return None;
+105        }
+106
+107        let unscaled_delay = smoothed_rtt
+108            .checked_mul((bytes_to_send.max(self.capacity) - self.tokens) as _)
+109            .unwrap_or(Duration::MAX)
+110            / window;
+111
+112        // divisions come before multiplications to prevent overflow
+113        // this is the time at which the pacing window becomes empty
+114        Some(now + (unscaled_delay / 5) * 4)
+115    }
+116}
+117
+118/// Calculates a pacer capacity for a certain window and RTT
+119///
+120/// The goal is to emit a burst (of size `capacity`) in timer intervals
+121/// which compromise between
+122/// - ideally distributing datagrams over time
+123/// - constantly waking up the connection to produce additional datagrams
+124///
+125/// Too short burst intervals means we will never meet them since the timer
+126/// accuracy in user-space is not high enough. If we miss the interval by more
+127/// than 25%, we will lose that part of the congestion window since no additional
+128/// tokens for the extra-elapsed time can be stored.
+129///
+130/// Too long burst intervals make pacing less effective.
+131fn optimal_capacity(smoothed_rtt: Duration, window: u64, mtu: u16) -> u64 {
+132    let rtt = smoothed_rtt.as_nanos().max(1);
+133    let mtu = u64::from(mtu);
+134
+135    let target_capacity = ((window as u128 * TARGET_BURST_INTERVAL.as_nanos()) / rtt) as u64;
+136    // Never restrict capacity below one MTU.
+137    let max_capacity = Ord::max(
+138        ((window as u128 * MAX_BURST_INTERVAL.as_nanos()) / rtt) as u64,
+139        mtu,
+140    );
+141
+142    // Batch the greater of `TARGET_BURST_INTERVAL` or `MIN_BURST_SIZE` worth of traffic at a
+143    // time. To avoid inducing excessive latency, limit that result to at most `MAX_BURST_INTERVAL`
+144    // worth of traffic.
+145    Ord::min(
+146        max_capacity,
+147        target_capacity.clamp(MIN_BURST_SIZE * mtu, MAX_BURST_SIZE * mtu),
+148    )
+149}
+150
+151/// Period of traffic to batch together on a reasonably fast connection
+152const TARGET_BURST_INTERVAL: Duration = Duration::from_millis(2);
+153
+154/// Maximum period of traffic to batch together on a slow connection
+155///
+156/// Takes precedence over [`MIN_BURST_SIZE`].
+157const MAX_BURST_INTERVAL: Duration = Duration::from_millis(10);
+158
+159/// Minimum number of datagrams to batch together, so long as we won't have to wait for more than
+160/// [`MAX_BURST_INTERVAL`]
+161const MIN_BURST_SIZE: u64 = 10;
+162
+163/// Creating 256 packets took 1ms in a benchmark, so larger bursts don't make sense.
+164const MAX_BURST_SIZE: u64 = 256;
+165
+166#[cfg(test)]
+167mod tests {
+168    use super::*;
+169
+170    #[test]
+171    fn does_not_panic_on_bad_instant() {
+172        let old_instant = Instant::now();
+173        let new_instant = old_instant + Duration::from_micros(15);
+174        let rtt = Duration::from_micros(400);
+175
+176        assert!(
+177            Pacer::new(rtt, 30000, 1500, new_instant)
+178                .delay(Duration::from_micros(0), 0, 1500, 1, old_instant)
+179                .is_none()
+180        );
+181        assert!(
+182            Pacer::new(rtt, 30000, 1500, new_instant)
+183                .delay(Duration::from_micros(0), 1600, 1500, 1, old_instant)
+184                .is_none()
+185        );
+186        assert!(
+187            Pacer::new(rtt, 30000, 1500, new_instant)
+188                .delay(Duration::from_micros(0), 1500, 1500, 3000, old_instant)
+189                .is_none()
+190        );
+191    }
+192
+193    #[test]
+194    fn derives_initial_capacity() {
+195        let window = 2_000_000;
+196        let mtu = 1500;
+197        let rtt = Duration::from_millis(50);
+198        let now = Instant::now();
+199
+200        let pacer = Pacer::new(rtt, window, mtu, now);
+201        assert_eq!(
+202            pacer.capacity,
+203            (window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+204        );
+205        assert_eq!(pacer.tokens, pacer.capacity);
+206
+207        let pacer = Pacer::new(Duration::from_millis(0), window, mtu, now);
+208        assert_eq!(pacer.capacity, MAX_BURST_SIZE * mtu as u64);
+209        assert_eq!(pacer.tokens, pacer.capacity);
+210
+211        let pacer = Pacer::new(rtt, 1, mtu, now);
+212        assert_eq!(pacer.capacity, mtu as u64);
+213        assert_eq!(pacer.tokens, pacer.capacity);
+214    }
+215
+216    #[test]
+217    fn adjusts_capacity() {
+218        let window = 2_000_000;
+219        let mtu = 1500;
+220        let rtt = Duration::from_millis(50);
+221        let now = Instant::now();
+222
+223        let mut pacer = Pacer::new(rtt, window, mtu, now);
+224        assert_eq!(
+225            pacer.capacity,
+226            (window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+227        );
+228        assert_eq!(pacer.tokens, pacer.capacity);
+229        let initial_tokens = pacer.tokens;
+230
+231        pacer.delay(rtt, mtu as u64, mtu, window * 2, now);
+232        assert_eq!(
+233            pacer.capacity,
+234            (2 * window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+235        );
+236        assert_eq!(pacer.tokens, initial_tokens);
+237
+238        pacer.delay(rtt, mtu as u64, mtu, window / 2, now);
+239        assert_eq!(
+240            pacer.capacity,
+241            (window as u128 / 2 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+242        );
+243        assert_eq!(pacer.tokens, initial_tokens / 2);
+244
+245        pacer.delay(rtt, mtu as u64, mtu * 2, window, now);
+246        assert_eq!(
+247            pacer.capacity,
+248            (window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+249        );
+250
+251        pacer.delay(rtt, mtu as u64, 20_000, window, now);
+252        assert_eq!(pacer.capacity, 20_000_u64 * MIN_BURST_SIZE);
+253    }
+254
+255    #[test]
+256    fn computes_pause_correctly() {
+257        let window = 2_000_000u64;
+258        let mtu = 1000;
+259        let rtt = Duration::from_millis(50);
+260        let old_instant = Instant::now();
+261
+262        let mut pacer = Pacer::new(rtt, window, mtu, old_instant);
+263        let packet_capacity = pacer.capacity / mtu as u64;
+264
+265        for _ in 0..packet_capacity {
+266            assert_eq!(
+267                pacer.delay(rtt, mtu as u64, mtu, window, old_instant),
+268                None,
+269                "When capacity is available packets should be sent immediately"
+270            );
+271
+272            pacer.on_transmit(mtu);
+273        }
+274
+275        let pace_duration = Duration::from_nanos((TARGET_BURST_INTERVAL.as_nanos() * 4 / 5) as u64);
+276
+277        let actual_delay = pacer
+278            .delay(rtt, mtu as u64, mtu, window, old_instant)
+279            .expect("Send must be delayed")
+280            .duration_since(old_instant);
+281
+282        let diff = actual_delay.abs_diff(pace_duration);
+283
+284        // Allow up to 2ns difference due to rounding
+285        assert!(
+286            diff < Duration::from_nanos(2),
+287            "expected ≈ {pace_duration:?}, got {actual_delay:?} (diff {diff:?})"
+288        );
+289        // Refill half of the tokens
+290        assert_eq!(
+291            pacer.delay(
+292                rtt,
+293                mtu as u64,
+294                mtu,
+295                window,
+296                old_instant + pace_duration / 2
+297            ),
+298            None
+299        );
+300        assert_eq!(pacer.tokens, pacer.capacity / 2);
+301
+302        for _ in 0..packet_capacity / 2 {
+303            assert_eq!(
+304                pacer.delay(rtt, mtu as u64, mtu, window, old_instant),
+305                None,
+306                "When capacity is available packets should be sent immediately"
+307            );
+308
+309            pacer.on_transmit(mtu);
+310        }
+311
+312        // Refill all capacity by waiting more than the expected duration
+313        assert_eq!(
+314            pacer.delay(
+315                rtt,
+316                mtu as u64,
+317                mtu,
+318                window,
+319                old_instant + pace_duration * 3 / 2
+320            ),
+321            None
+322        );
+323        assert_eq!(pacer.tokens, pacer.capacity);
+324    }
+325}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/connection/packet_builder.rs.html b/pr/509/docs/src/noq_proto/connection/packet_builder.rs.html new file mode 100644 index 000000000..1e3734fcb --- /dev/null +++ b/pr/509/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::Rng;
+3use tracing::{debug, trace, trace_span};
+4
+5use super::{Connection, PathId, SentFrames, TransmitBuf, spaces::SentPacket};
+6use crate::{
+7    ConnectionId, FrameStats, Instant, MIN_INITIAL_SIZE, TransportError,
+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        ack_eliciting: bool,
+53        conn: &mut Connection,
+54    ) -> Option<Self>
+55    where
+56        'b: 'a,
+57    {
+58        let mut qlog = QlogSentPacket::default();
+59
+60        let version = conn.version;
+61        // Initiate key update if we're approaching the confidentiality limit
+62        let (_header_crypto, _packet_crypto, level) = conn
+63            .crypto_state
+64            .encryption_keys(space_id.kind(), conn.side.side())
+65            .expect("tried to build packet without encryption keys");
+66
+67        let remaining_packet_budget = conn
+68            .crypto_state
+69            .remaining_packet_budget(level)
+70            .expect("keys are installed");
+71
+72        if level == EncryptionLevel::OneRtt {
+73            // 1-RTT keys can be rotated, so exhaustion just triggers a key update
+74            if remaining_packet_budget == 0 {
+75                debug!("routine key update due to phase exhaustion");
+76                conn.force_key_update();
+77            }
+78        } else {
+79            // Other encryption levels have a fixed key budget
+80            if remaining_packet_budget == 0 {
+81                let close = TransportError::AEAD_LIMIT_REACHED("confidentiality limit reached");
+82                conn.kill(close.into());
+83                return None;
+84            } else if remaining_packet_budget == 1 {
+85                let close = TransportError::AEAD_LIMIT_REACHED("confidentiality limit reached");
+86                conn.close_inner(now, close.into());
+87            }
+88        }
+89
+90        let (header_crypto, packet_crypto, level) = conn
+91            .crypto_state
+92            .encryption_keys(space_id.kind(), conn.side.side())
+93            .expect("verified");
+94
+95        let space = &mut conn.spaces[space_id];
+96        let packet_number = space.for_path(path_id).get_tx_number(&mut conn.rng);
+97        let span = trace_span!("send", space = ?space_id, pn = packet_number, %path_id).entered();
+98
+99        let number = PacketNumber::new(
+100            packet_number,
+101            space.for_path(path_id).largest_acked_packet.unwrap_or(0),
+102        );
+103        let header = match level {
+104            EncryptionLevel::OneRtt => Header::Short {
+105                dst_cid,
+106                number,
+107                spin: if conn.spin_enabled {
+108                    conn.spin
+109                } else {
+110                    conn.rng.random()
+111                },
+112                key_phase: conn.crypto_state.key_phase,
+113            },
+114            EncryptionLevel::ZeroRtt => Header::Long {
+115                ty: LongType::ZeroRtt,
+116                src_cid: conn.handshake_cid,
+117                dst_cid,
+118                number,
+119                version,
+120            },
+121            EncryptionLevel::Handshake => Header::Long {
+122                ty: LongType::Handshake,
+123                src_cid: conn.handshake_cid,
+124                dst_cid,
+125                number,
+126                version,
+127            },
+128            EncryptionLevel::Initial => Header::Initial(InitialHeader {
+129                src_cid: conn.handshake_cid,
+130                dst_cid,
+131                token: match &conn.side {
+132                    ConnectionSide::Client { token, .. } => token.clone(),
+133                    ConnectionSide::Server { .. } => Bytes::new(),
+134                },
+135                number,
+136                version,
+137            }),
+138        };
+139
+140        let partial_encode = header.encode(buffer);
+141        if conn.peer_params.grease_quic_bit && conn.rng.random() {
+142            buffer.as_mut_slice()[partial_encode.start] ^= FIXED_BIT;
+143        }
+144
+145        let (sample_size, tag_len) = (header_crypto.sample_size(), packet_crypto.tag_len());
+146
+147        // Each packet must be large enough for header protection sampling, i.e. the combined
+148        // lengths of the encoded packet number and protected payload must be at least 4 bytes
+149        // longer than the sample required for header protection. Further, each packet should be at
+150        // least tag_len + 6 bytes larger than the destination CID on incoming packets so that the
+151        // peer may send stateless resets that are indistinguishable from regular traffic.
+152
+153        // pn_len + payload_len + tag_len >= sample_size + 4
+154        // payload_len >= sample_size + 4 - pn_len - tag_len
+155        let min_size = Ord::max(
+156            buffer.len() + (sample_size + 4).saturating_sub(number.len() + tag_len),
+157            partial_encode.start + dst_cid.len() + 6,
+158        );
+159        let max_size = buffer.datagram_max_offset() - tag_len;
+160        debug_assert!(max_size >= min_size);
+161
+162        qlog.header(&header, Some(packet_number), level, path_id);
+163
+164        Some(Self {
+165            buf: buffer,
+166            space: space_id,
+167            path: path_id,
+168            partial_encode,
+169            packet_number,
+170            can_coalesce: header.can_coalesce(),
+171            min_size,
+172            tag_len,
+173            level,
+174            ack_eliciting,
+175            qlog,
+176            sent_frames: SentFrames::default(),
+177            _span: span,
+178        })
+179    }
+180
+181    #[cfg(test)]
+182    pub(crate) fn simple_data_buf(buf: &'a mut TransmitBuf<'b>) -> Self {
+183        Self {
+184            buf,
+185            space: SpaceId::Data,
+186            path: PathId::ZERO,
+187            partial_encode: PartialEncode::no_header(),
+188            ack_eliciting: true,
+189            packet_number: 0,
+190            can_coalesce: true,
+191            min_size: 0,
+192            tag_len: 0,
+193            _span: trace_span!("test").entered(),
+194            qlog: QlogSentPacket::default(),
+195            sent_frames: SentFrames::default(),
+196            level: EncryptionLevel::Initial,
+197        }
+198    }
+199
+200    /// Append the minimum amount of padding to the packet such that, after encryption, the
+201    /// enclosing datagram will occupy at least `min_size` bytes
+202    pub(super) fn pad_to(&mut self, min_size: u16) {
+203        // The datagram might already have a larger minimum size than the caller is requesting, if
+204        // e.g. we're coalescing packets and have populated more than `min_size` bytes with packets
+205        // already.
+206        self.min_size = Ord::max(
+207            self.min_size,
+208            self.buf.datagram_start_offset() + (min_size as usize) - self.tag_len,
+209        );
+210    }
+211
+212    /// Writes a frame into the underlying buffer.
+213    ///
+214    /// It will also:
+215    /// - Track the frame so that it's registered with the path once [`Self::finish_and_track`] is
+216    ///   called.
+217    /// - Register the sent frame with the given [`FrameStats`].
+218    /// - If the qlog feature is enabled, register the frame.
+219    /// - Log the frame.
+220    pub(super) fn write_frame<'c>(
+221        &mut self,
+222        frame: impl Into<EncodableFrame<'c>>,
+223        stats: &mut FrameStats,
+224    ) {
+225        self.write_frame_with_log_msg(frame, stats, None);
+226    }
+227
+228    /// Writes a frame into the underlying buffer.
+229    ///
+230    /// It will also:
+231    /// - Track the frame so that it's registered with the path once [`Self::finish_and_track`] is
+232    ///   called.
+233    /// - Register the sent frame with the given [`FrameStats`].
+234    /// - If the qlog feature is enabled, register the frame.
+235    /// - Log the frame. If a `msg` is given, this will be added to the log.
+236    pub(super) fn write_frame_with_log_msg<'c>(
+237        &mut self,
+238        frame: impl Into<EncodableFrame<'c>>,
+239        stats: &mut FrameStats,
+240        msg: Option<&'static str>,
+241    ) {
+242        let frame = frame.into();
+243        frame.encode(&mut self.frame_space_mut());
+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    pub(super) 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.permit_idle_reset {
+311                    conn.reset_idle_timeout(now, space_id.kind(), path_id);
+312                }
+313                conn.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/509/docs/src/noq_proto/connection/packet_crypto.rs.html b/pr/509/docs/src/noq_proto/connection/packet_crypto.rs.html new file mode 100644 index 000000000..3832f05d5 --- /dev/null +++ b/pr/509/docs/src/noq_proto/connection/packet_crypto.rs.html @@ -0,0 +1,501 @@ +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, Rng};
+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) 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 = spaces[space].path_space(path_id).and_then(|s| s.rx_packet);
+204        let number = packet
+205            .header
+206            .number()
+207            .ok_or(None)?
+208            .expand(rx_packet.map(|n| n + 1).unwrap_or_default());
+209        let packet_key_phase = packet.header.key_phase();
+210
+211        let mut crypto_update = false;
+212        let crypto = if packet.header.is_0rtt() {
+213            let (_, packet) = self.remote_crypto(EncryptionLevel::ZeroRtt).unwrap();
+214            packet
+215        } else if packet_key_phase == conn_key_phase || space != SpaceKind::Data {
+216            let (_, packet) = self.remote_crypto(space.encryption_level()).unwrap();
+217            packet
+218        } else if let Some(prev) = self.prev_crypto.as_ref().and_then(|crypto| {
+219            // If this packet comes prior to acknowledgment of the key update by the peer,
+220            if crypto.end_packet.is_none_or(|(pn, _)| number < pn) {
+221                // use the previous keys.
+222                Some(crypto)
+223            } else {
+224                // Otherwise, this must be a remotely-initiated key update, so fall through to the
+225                // final case.
+226                None
+227            }
+228        }) {
+229            &*prev.crypto.remote
+230        } else {
+231            // We're in the Data space with a key phase mismatch and either there is no locally
+232            // initiated key update or the locally initiated key update was acknowledged by a
+233            // lower-numbered packet. The key phase mismatch must therefore represent a new
+234            // remotely-initiated key update.
+235            crypto_update = true;
+236            &*self.next_crypto.as_ref().unwrap().remote
+237        };
+238
+239        crypto
+240            .decrypt(path_id, number, &packet.header_data, &mut packet.payload)
+241            .map_err(|_| {
+242                trace!("decryption failed with packet number {}", number);
+243                None
+244            })?;
+245
+246        if !packet.reserved_bits_valid() {
+247            return Err(Some(TransportError::PROTOCOL_VIOLATION(
+248                "reserved bits set",
+249            )));
+250        }
+251
+252        let mut outgoing_key_update_acked = false;
+253        if let Some(ref prev) = self.prev_crypto
+254            && prev.end_packet.is_none()
+255            && packet_key_phase == conn_key_phase
+256        {
+257            outgoing_key_update_acked = true;
+258        }
+259
+260        if crypto_update {
+261            // Validate incoming key update
+262            // If `rx_packet` is `None`, then either the path is entirely new, or we haven't received
+263            // any packets on this path yet. In that case, having the first packet be a crypto update
+264            // is fine.
+265            let invalid_packet_number = rx_packet.is_some_and(|rx_packet| number <= rx_packet);
+266            if invalid_packet_number || self.prev_crypto.as_ref().is_some_and(|x| x.update_unacked)
+267            {
+268                trace!(?number, ?rx_packet, %path_id, "crypto update failed");
+269                return Err(Some(TransportError::KEY_UPDATE_ERROR("")));
+270            }
+271        }
+272
+273        Ok(Some(DecryptPacketResult {
+274            number,
+275            outgoing_key_update_acked,
+276            incoming_key_update: crypto_update,
+277        }))
+278    }
+279
+280    /// Check if keys are available for the given encryption level.
+281    pub(super) fn has_keys(&self, level: EncryptionLevel) -> bool {
+282        match level {
+283            EncryptionLevel::Initial => self.spaces[0].keys.is_some(),
+284            EncryptionLevel::ZeroRtt => self.zero_rtt_crypto.is_some(),
+285            EncryptionLevel::Handshake => self.spaces[1].keys.is_some(),
+286            EncryptionLevel::OneRtt => self.spaces[2].keys.is_some(),
+287        }
+288    }
+289
+290    /// Discard temporary key state (0-RTT and previous keys).
+291    pub(super) fn discard_temporary_keys(&mut self) {
+292        self.zero_rtt_crypto = None;
+293        self.prev_crypto = None;
+294    }
+295
+296    /// Enable 0-RTT crypto with the given keys.
+297    pub(super) fn enable_zero_rtt(
+298        &mut self,
+299        header: Box<dyn HeaderKey>,
+300        packet: Box<dyn PacketKey>,
+301    ) {
+302        self.zero_rtt_enabled = true;
+303        self.zero_rtt_crypto = Some(ZeroRttCrypto { header, packet });
+304    }
+305
+306    /// Discard 0-RTT crypto keys.
+307    pub(super) fn discard_zero_rtt(&mut self) {
+308        self.zero_rtt_crypto = None;
+309    }
+310
+311    /// Get the integrity limit for the given space's local packet keys.
+312    pub(super) fn integrity_limit(&self, space: SpaceKind) -> Option<u64> {
+313        let keys = self.spaces[space].keys.as_ref()?;
+314        Some(keys.packet.local.integrity_limit())
+315    }
+316
+317    /// Get local (sending) crypto keys for the given encryption level.
+318    ///
+319    /// Use this only when sure the keys are allowed to be used. [`Self::encryption_keys`] should
+320    /// be preferred otherwise.
+321    pub(super) fn local_crypto(
+322        &self,
+323        level: EncryptionLevel,
+324    ) -> Option<(&dyn HeaderKey, &dyn PacketKey)> {
+325        match level {
+326            EncryptionLevel::Initial => self.spaces[0].keys.as_ref().map(Keys::local),
+327            EncryptionLevel::Handshake => self.spaces[1].keys.as_ref().map(Keys::local),
+328            EncryptionLevel::OneRtt => self.spaces[2].keys.as_ref().map(Keys::local),
+329            // 0-RTT uses the same keys for both directions
+330            EncryptionLevel::ZeroRtt => self.zero_rtt_crypto.as_ref().map(ZeroRttCrypto::keys),
+331        }
+332    }
+333
+334    /// Get remote (receiving) crypto keys for the given encryption level.
+335    ///
+336    /// Returns header and packet keys used for decrypting incoming packets.
+337    fn remote_crypto(&self, level: EncryptionLevel) -> Option<(&dyn HeaderKey, &dyn PacketKey)> {
+338        match level {
+339            EncryptionLevel::Initial => self.spaces[0].keys.as_ref().map(Keys::remote),
+340            EncryptionLevel::Handshake => self.spaces[1].keys.as_ref().map(Keys::remote),
+341            EncryptionLevel::OneRtt => self.spaces[2].keys.as_ref().map(Keys::remote),
+342            // 0-RTT uses the same keys for both directions
+343            EncryptionLevel::ZeroRtt => self.zero_rtt_crypto.as_ref().map(ZeroRttCrypto::keys),
+344        }
+345    }
+346
+347    /// Get local (sending) crypto keys and the actual encryption level for a given space.
+348    ///
+349    /// This method takes a [`SpaceKind`] and resolves the encryption level automatically: for the
+350    /// [`SpaceKind::Data`] space on the client side, it falls back to 0-RTT keys when 1-RTT keys
+351    /// are not yet available. Resolving the appropriate encryption keys makes this method
+352    /// preferable to [`Self::local_crypto`] in general.
+353    ///
+354    /// Returns `None` if no keys are available.
+355    pub(super) fn encryption_keys(
+356        &self,
+357        kind: SpaceKind,
+358        side: Side,
+359    ) -> Option<(&dyn HeaderKey, &dyn PacketKey, EncryptionLevel)> {
+360        let mut keys = self.spaces[kind].keys.as_ref().map(Keys::local);
+361        let mut level = match kind {
+362            SpaceKind::Initial => EncryptionLevel::Initial,
+363            SpaceKind::Handshake => EncryptionLevel::Handshake,
+364            SpaceKind::Data => EncryptionLevel::OneRtt,
+365        };
+366
+367        // Clients use 0-RTT keys if 1-RTT keys are not available. Servers never encrypt 0-RTT
+368        if keys.is_none() && kind == SpaceKind::Data && side.is_client() {
+369            keys = self.zero_rtt_crypto.as_ref().map(ZeroRttCrypto::keys);
+370            level = EncryptionLevel::ZeroRtt;
+371        }
+372
+373        keys.map(|(header_keys, packet_keys)| (header_keys, packet_keys, level))
+374    }
+375
+376    /// Perform a 1-RTT key update.
+377    ///
+378    /// Generates the next set of keys, rotates current keys into previous, and installs the new
+379    /// keys. Updates `key_phase` and `key_phase_size` accordingly.
+380    ///
+381    /// PANICS: If 1-RTT keys are missing.
+382    pub(super) fn update_keys(&mut self, end_packet: Option<(u64, Instant)>, remote: bool) {
+383        trace!("executing key update");
+384
+385        let new = self
+386            .session
+387            .next_1rtt_keys()
+388            .expect("only called for `Data` packets");
+389        let confidentiality_limit = new.local.confidentiality_limit();
+390        let old = mem::replace(
+391            &mut self.spaces[SpaceKind::Data]
+392                .keys
+393                .as_mut()
+394                .unwrap() // safe because update_keys() can only be triggered by short packets
+395                .packet,
+396            mem::replace(self.next_crypto.as_mut().unwrap(), new),
+397        );
+398        self.prev_crypto = Some(PrevCrypto {
+399            crypto: old,
+400            end_packet,
+401            update_unacked: remote,
+402        });
+403
+404        self.key_phase_size = confidentiality_limit.saturating_sub(KEY_UPDATE_MARGIN);
+405        self.key_phase = !self.key_phase;
+406        self.spaces[2].sent_with_keys = 0;
+407    }
+408
+409    /// Number of packets encrypted with the current set of keys at `level`.
+410    ///
+411    /// For [`EncryptionLevel::OneRtt`], this counter resets to zero on every key update (see
+412    /// [`Self::update_keys`]).
+413    pub(crate) fn sent_with_keys(&self, level: EncryptionLevel) -> u64 {
+414        match level {
+415            EncryptionLevel::Initial => self.spaces[0].sent_with_keys,
+416            EncryptionLevel::ZeroRtt => self.sent_with_zero_rtt,
+417            EncryptionLevel::Handshake => self.spaces[1].sent_with_keys,
+418            EncryptionLevel::OneRtt => self.spaces[2].sent_with_keys,
+419        }
+420    }
+421
+422    /// Number of packets that may still be sent before the AEAD confidentiality limit is reached
+423    /// at the given encryption level.
+424    ///
+425    /// For [`EncryptionLevel::OneRtt`] the effective limit is the minimum of the AEAD
+426    /// confidentiality limit and the current key-phase size. For all other levels the raw AEAD
+427    /// confidentiality limit is used.
+428    ///
+429    /// Returns `None` when no keys are available for `level`.
+430    pub(crate) fn remaining_packet_budget(&self, level: EncryptionLevel) -> Option<u64> {
+431        let sent_with_keys = self.sent_with_keys(level);
+432        let (_header_keys, packet_keys) = self.local_crypto(level)?;
+433        let limit = match level {
+434            EncryptionLevel::OneRtt => self.key_phase_size.min(packet_keys.confidentiality_limit()),
+435            _ => packet_keys.confidentiality_limit(),
+436        };
+437
+438        Some(limit.saturating_sub(sent_with_keys))
+439    }
+440
+441    /// Record that a packet has been encrypted at the given level.
+442    pub(crate) fn inc_sent_with_keys(&mut self, level: EncryptionLevel) {
+443        let count = match level {
+444            EncryptionLevel::Initial => &mut self.spaces[0].sent_with_keys,
+445            EncryptionLevel::ZeroRtt => &mut self.sent_with_zero_rtt,
+446            EncryptionLevel::Handshake => &mut self.spaces[1].sent_with_keys,
+447            EncryptionLevel::OneRtt => &mut self.spaces[2].sent_with_keys,
+448        };
+449        *count = count.saturating_add(1u64);
+450    }
+451}
+452
+453/// Per space kind cryptographic state.
+454#[derive(Default)]
+455pub(super) struct CryptoSpace {
+456    /// Packet protection keys for this space.
+457    pub(super) keys: Option<Keys>,
+458    /// Incoming cryptographic handshake stream.
+459    pub(super) crypto_stream: Assembler,
+460    /// Current offset of outgoing cryptographic handshake stream.
+461    pub(super) crypto_offset: u64,
+462    /// Number of packets encrypted with the current set of keys.
+463    pub(super) sent_with_keys: u64,
+464}
+465
+466/// QUIC packet protection levels (RFC 9001).
+467#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+468pub(crate) enum EncryptionLevel {
+469    /// Initial packets (client and server).
+470    Initial,
+471    /// Early data (0-RTT), client only.
+472    ZeroRtt,
+473    /// Handshake packets.
+474    Handshake,
+475    /// Application data (1-RTT).
+476    OneRtt,
+477}
+478
+479impl From<SpaceKind> for crate::packet::SpaceId {
+480    fn from(kind: SpaceKind) -> Self {
+481        match kind {
+482            SpaceKind::Initial => Self::Initial,
+483            SpaceKind::Handshake => Self::Handshake,
+484            SpaceKind::Data => Self::Data,
+485        }
+486    }
+487}
+488
+489impl IndexMut<SpaceKind> for [CryptoSpace; 3] {
+490    fn index_mut(&mut self, index: SpaceKind) -> &mut Self::Output {
+491        &mut self[index as usize]
+492    }
+493}
+494
+495impl Index<SpaceKind> for [CryptoSpace; 3] {
+496    type Output = CryptoSpace;
+497
+498    fn index(&self, index: SpaceKind) -> &Self::Output {
+499        &self[index as usize]
+500    }
+501}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/connection/paths.rs.html b/pr/509/docs/src/noq_proto/connection/paths.rs.html new file mode 100644 index 000000000..880a46e3f --- /dev/null +++ b/pr/509/docs/src/noq_proto/connection/paths.rs.html @@ -0,0 +1,1091 @@ +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 identity_hash::IdentityHashable for PathId {}
+36
+37impl Decodable for PathId {
+38    fn decode<B: bytes::Buf>(r: &mut B) -> coding::Result<Self> {
+39        let v = VarInt::decode(r)?;
+40        let v = u32::try_from(v.0).map_err(|_| coding::UnexpectedEnd)?;
+41        Ok(Self(v))
+42    }
+43}
+44
+45impl Encodable for PathId {
+46    fn encode<B: bytes::BufMut>(&self, w: &mut B) {
+47        VarInt(self.0.into()).encode(w)
+48    }
+49}
+50
+51impl PathId {
+52    /// The maximum path ID allowed.
+53    pub const MAX: Self = Self(u32::MAX);
+54
+55    /// The 0 path id.
+56    pub const ZERO: Self = Self(0);
+57
+58    /// The number of bytes this [`PathId`] uses when encoded as a [`VarInt`]
+59    pub(crate) const fn size(&self) -> usize {
+60        VarInt(self.0 as u64).size()
+61    }
+62
+63    /// Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead
+64    /// of overflowing.
+65    pub fn saturating_add(self, rhs: impl Into<Self>) -> Self {
+66        let rhs = rhs.into();
+67        let inner = self.0.saturating_add(rhs.0);
+68        Self(inner)
+69    }
+70
+71    /// Saturating integer subtraction. Computes self - rhs, saturating at the numeric bounds
+72    /// instead of overflowing.
+73    pub fn saturating_sub(self, rhs: impl Into<Self>) -> Self {
+74        let rhs = rhs.into();
+75        let inner = self.0.saturating_sub(rhs.0);
+76        Self(inner)
+77    }
+78
+79    /// Get the next [`PathId`]
+80    pub(crate) fn next(&self) -> Self {
+81        self.saturating_add(Self(1))
+82    }
+83
+84    /// Get the underlying u32
+85    pub(crate) fn as_u32(&self) -> u32 {
+86        self.0
+87    }
+88}
+89
+90impl std::fmt::Display for PathId {
+91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+92        self.0.fmt(f)
+93    }
+94}
+95
+96impl<T: Into<u32>> From<T> for PathId {
+97    fn from(source: T) -> Self {
+98        Self(source.into())
+99    }
+100}
+101
+102/// State needed for a single path ID.
+103///
+104/// A single path ID can migrate according to the rules in RFC9000 §9, either voluntary or
+105/// involuntary. We need to keep the [`PathData`] of the previously used such path available
+106/// in order to defend against migration attacks (see RFC9000 §9.3.1, §9.3.2 and §9.3.3) as
+107/// well as to support path probing (RFC9000 §9.1).
+108#[derive(Debug)]
+109pub(super) struct PathState {
+110    pub(super) data: PathData,
+111    pub(super) prev: Option<(ConnectionId, PathData)>,
+112}
+113
+114impl PathState {
+115    /// Update counters to account for a packet becoming acknowledged, lost, or abandoned
+116    pub(super) fn remove_in_flight(&mut self, packet: &SentPacket) {
+117        // Visit known paths from newest to oldest to find the one `pn` was sent on
+118        for path_data in [&mut self.data]
+119            .into_iter()
+120            .chain(self.prev.as_mut().map(|(_, data)| data))
+121        {
+122            if path_data.remove_in_flight(packet) {
+123                return;
+124            }
+125        }
+126    }
+127}
+128
+129#[derive(Debug)]
+130pub(super) struct SentChallengeInfo {
+131    /// When was the challenge sent on the wire.
+132    pub(super) sent_instant: Instant,
+133    /// The 4-tuple on which this path challenge was sent.
+134    pub(super) network_path: FourTuple,
+135}
+136
+137/// Description of a particular network path
+138#[derive(Debug)]
+139pub(super) struct PathData {
+140    pub(super) network_path: FourTuple,
+141    pub(super) rtt: RttEstimator,
+142    /// Whether we're enabling ECN on outgoing packets
+143    pub(super) sending_ecn: bool,
+144    /// Congestion controller state
+145    pub(super) congestion: Box<dyn congestion::Controller>,
+146    /// Pacing state
+147    pub(super) pacing: Pacer,
+148    /// Path challenges sent (on the wire, on-path) that we didn't receive a path response for yet
+149    on_path_challenges_unconfirmed: IntMap<u64, SentChallengeInfo>,
+150    /// Path challenges sent (on the wire, off-path) that we didn't receive a path response for yet
+151    off_path_challenges_unconfirmed: IntMap<u64, SentChallengeInfo>,
+152    /// Whether to trigger sending another PATH_CHALLENGE in the next poll_transmit.
+153    ///
+154    /// This is picked up by [`super::Connection::space_can_send`].
+155    ///
+156    /// Only used for RFC9000-style path migration and multipath path validation (for opening).
+157    ///
+158    /// This is **not used** for n0 nat traversal challenge sending.
+159    pub(super) pending_on_path_challenge: bool,
+160    /// Pending responses to PATH_CHALLENGE frames
+161    pub(super) path_responses: PathResponses,
+162    /// Whether we're certain the peer can both send and receive on this address
+163    ///
+164    /// Initially equal to `use_stateless_retry` for servers, and becomes false again on every
+165    /// migration. Always true for clients.
+166    pub(super) validated: bool,
+167    /// Total size of all UDP datagrams sent on this path
+168    pub(super) total_sent: u64,
+169    /// Total size of all UDP datagrams received on this path
+170    pub(super) total_recvd: u64,
+171    /// The state of the MTU discovery process
+172    pub(super) mtud: MtuDiscovery,
+173    /// Packet number of the first packet sent after an RTT sample was collected on this path
+174    ///
+175    /// Used in persistent congestion determination.
+176    pub(super) first_packet_after_rtt_sample: Option<(SpaceKind, u64)>,
+177    /// The in-flight packets and bytes
+178    ///
+179    /// Note that this is across all spaces on this path
+180    pub(super) in_flight: InFlight,
+181    /// Whether this path has had it's remote address reported back to the peer. This only happens
+182    /// if both peers agree to so based on their transport parameters.
+183    pub(super) observed_addr_sent: bool,
+184    /// Observed address frame with the largest sequence number received from the peer on this path.
+185    pub(super) last_observed_addr_report: Option<ObservedAddr>,
+186    /// The QUIC-MULTIPATH path status
+187    pub(super) status: PathStatusState,
+188    /// Number of the first packet sent on this path
+189    ///
+190    /// With RFC9000 §9 style migration (i.e. not multipath) the PathId does not change and
+191    /// hence packet numbers continue. This is used to determine whether a packet was sent
+192    /// on such an earlier path. Insufficient to determine if a packet was sent on a later
+193    /// path.
+194    first_packet: Option<u64>,
+195    /// The number of times a PTO has been sent without receiving an ack.
+196    pub(super) pto_count: u32,
+197
+198    //
+199    // Per-path idle & keep alive
+200    //
+201    /// Idle timeout for the path
+202    ///
+203    /// If expired, the path will be abandoned.  This is different from the connection-wide
+204    /// idle timeout which closes the connection if expired.
+205    pub(super) idle_timeout: Option<Duration>,
+206    /// Keep alives to send on this path
+207    ///
+208    /// There is also a connection-level keep alive configured in the
+209    /// [`TransportParameters`].  This triggers activity on any path which can keep the
+210    /// connection alive.
+211    ///
+212    /// [`TransportParameters`]: crate::transport_parameters::TransportParameters
+213    pub(super) keep_alive: Option<Duration>,
+214
+215    /// Whether the path has already been considered opened from an application perspective.
+216    ///
+217    /// This means, for paths other than the original [`PathId::ZERO`], a first path challenge has
+218    /// been responded to, regardless of the initial validation status of the path. This state is
+219    /// irreversible, since it's not affected by the path being closed.
+220    ///
+221    /// Sending a PATH_CHALLENGE and receiving a valid response before the application is informed
+222    /// of the path, is a way to ensure the path is usable before it is reported. This is not
+223    /// required by the spec, and in the future might be changed for simply requiring a first ack'd
+224    /// packet.
+225    pub(super) open_status: OpenStatus,
+226
+227    /// Whether we're currently draining the path after having abandoned it.
+228    ///
+229    /// This should only be true when a path discard timer is armed, and after the path was
+230    /// abandoned (and added to the abandoned_paths set).
+231    ///
+232    /// This will only ever be set from false to true.
+233    pub(super) draining: bool,
+234
+235    /// Snapshot of the qlog recovery metrics
+236    #[cfg(feature = "qlog")]
+237    recovery_metrics: RecoveryMetrics,
+238
+239    /// Tag uniquely identifying a path in a connection
+240    generation: u64,
+241}
+242
+243impl PathData {
+244    pub(super) fn new(
+245        network_path: FourTuple,
+246        allow_mtud: bool,
+247        peer_max_udp_payload_size: Option<u16>,
+248        generation: u64,
+249        now: Instant,
+250        config: &TransportConfig,
+251    ) -> Self {
+252        let congestion = config
+253            .congestion_controller_factory
+254            .clone()
+255            .build(now, config.get_initial_mtu());
+256        Self {
+257            network_path,
+258            rtt: RttEstimator::new(config.initial_rtt),
+259            sending_ecn: true,
+260            pacing: Pacer::new(
+261                config.initial_rtt,
+262                congestion.initial_window(),
+263                config.get_initial_mtu(),
+264                now,
+265            ),
+266            congestion,
+267            on_path_challenges_unconfirmed: Default::default(),
+268            off_path_challenges_unconfirmed: Default::default(),
+269            pending_on_path_challenge: false,
+270            path_responses: PathResponses::default(),
+271            validated: false,
+272            total_sent: 0,
+273            total_recvd: 0,
+274            mtud: config
+275                .mtu_discovery_config
+276                .as_ref()
+277                .filter(|_| allow_mtud)
+278                .map_or_else(
+279                    || MtuDiscovery::disabled(config.get_initial_mtu(), config.min_mtu),
+280                    |mtud_config| {
+281                        MtuDiscovery::new(
+282                            config.get_initial_mtu(),
+283                            config.min_mtu,
+284                            peer_max_udp_payload_size,
+285                            mtud_config.clone(),
+286                        )
+287                    },
+288                ),
+289            first_packet_after_rtt_sample: None,
+290            in_flight: InFlight::new(),
+291            observed_addr_sent: false,
+292            last_observed_addr_report: None,
+293            status: Default::default(),
+294            first_packet: None,
+295            pto_count: 0,
+296            idle_timeout: config.default_path_max_idle_timeout,
+297            keep_alive: config.default_path_keep_alive_interval,
+298            open_status: OpenStatus::default(),
+299            draining: false,
+300            #[cfg(feature = "qlog")]
+301            recovery_metrics: RecoveryMetrics::default(),
+302            generation,
+303        }
+304    }
+305
+306    /// Create a new path from a previous one.
+307    ///
+308    /// This should only be called when migrating paths.
+309    pub(super) fn from_previous(
+310        network_path: FourTuple,
+311        prev: &Self,
+312        generation: u64,
+313        now: Instant,
+314    ) -> Self {
+315        let congestion = prev.congestion.clone_box();
+316        let smoothed_rtt = prev.rtt.get();
+317        Self {
+318            network_path,
+319            rtt: prev.rtt,
+320            pacing: Pacer::new(smoothed_rtt, congestion.window(), prev.current_mtu(), now),
+321            sending_ecn: true,
+322            congestion,
+323            on_path_challenges_unconfirmed: Default::default(),
+324            off_path_challenges_unconfirmed: Default::default(),
+325            pending_on_path_challenge: false,
+326            path_responses: PathResponses::default(),
+327            validated: false,
+328            total_sent: 0,
+329            total_recvd: 0,
+330            mtud: prev.mtud.clone(),
+331            first_packet_after_rtt_sample: prev.first_packet_after_rtt_sample,
+332            in_flight: InFlight::new(),
+333            observed_addr_sent: false,
+334            last_observed_addr_report: None,
+335            status: prev.status.clone(),
+336            first_packet: None,
+337            pto_count: 0,
+338            idle_timeout: prev.idle_timeout,
+339            keep_alive: prev.keep_alive,
+340            open_status: OpenStatus::default(),
+341            draining: false,
+342            #[cfg(feature = "qlog")]
+343            recovery_metrics: prev.recovery_metrics.clone(),
+344            generation,
+345        }
+346    }
+347
+348    /// Whether we're in the process of validating this path with PATH_CHALLENGEs
+349    pub(super) fn is_validating_path(&self) -> bool {
+350        !self.on_path_challenges_unconfirmed.is_empty() || self.pending_on_path_challenge
+351    }
+352
+353    /// Indicates whether we're a server that hasn't validated the peer's address and hasn't
+354    /// received enough data from the peer to permit sending `bytes_to_send` additional bytes
+355    pub(super) fn anti_amplification_blocked(&self, bytes_to_send: u64) -> bool {
+356        !self.validated && self.total_recvd * 3 < self.total_sent + bytes_to_send
+357    }
+358
+359    /// Returns the path's current MTU
+360    pub(super) fn current_mtu(&self) -> u16 {
+361        self.mtud.current_mtu()
+362    }
+363
+364    /// Account for transmission of `packet` with number `pn` in `space`
+365    pub(super) fn sent(&mut self, pn: u64, packet: SentPacket, space: &mut PacketNumberSpace) {
+366        self.in_flight.insert(&packet);
+367        if self.first_packet.is_none() {
+368            self.first_packet = Some(pn);
+369        }
+370        if let Some(forgotten) = space.sent(pn, packet) {
+371            self.remove_in_flight(&forgotten);
+372        }
+373    }
+374
+375    pub(super) fn record_path_challenge_sent(
+376        &mut self,
+377        now: Instant,
+378        token: u64,
+379        network_path: FourTuple,
+380    ) {
+381        let info = SentChallengeInfo {
+382            sent_instant: now,
+383            network_path,
+384        };
+385        if network_path == self.network_path {
+386            self.on_path_challenges_unconfirmed.insert(token, info);
+387        } else {
+388            self.off_path_challenges_unconfirmed.insert(token, info);
+389        }
+390    }
+391
+392    /// Remove `packet` with number `pn` from this path's congestion control counters, or return
+393    /// `false` if `pn` was sent before this path was established.
+394    pub(super) fn remove_in_flight(&mut self, packet: &SentPacket) -> bool {
+395        if packet.path_generation != self.generation {
+396            return false;
+397        }
+398        self.in_flight.remove(packet);
+399        true
+400    }
+401
+402    /// Increment the total size of sent UDP datagrams
+403    pub(super) fn inc_total_sent(&mut self, inc: u64) {
+404        self.total_sent = self.total_sent.saturating_add(inc);
+405        if !self.validated {
+406            trace!(
+407                network_path = %self.network_path,
+408                anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent),
+409                "anti amplification budget decreased"
+410            );
+411        }
+412    }
+413
+414    /// Increment the total size of received UDP datagrams
+415    pub(super) fn inc_total_recvd(&mut self, inc: u64) {
+416        self.total_recvd = self.total_recvd.saturating_add(inc);
+417        if !self.validated {
+418            trace!(
+419                network_path = %self.network_path,
+420                anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent),
+421                "anti amplification budget increased"
+422            );
+423        }
+424    }
+425
+426    /// The earliest time at which an on-path challenge we sent is considered lost.
+427    pub(super) fn earliest_on_path_expiring_challenge(&self) -> Option<Instant> {
+428        if self.on_path_challenges_unconfirmed.is_empty() {
+429            return None;
+430        }
+431        let pto = self.rtt.pto_base();
+432        self.on_path_challenges_unconfirmed
+433            .values()
+434            .map(|info| info.sent_instant + pto)
+435            .min()
+436    }
+437
+438    /// Handle receiving a PATH_RESPONSE.
+439    pub(super) fn on_path_response_received(
+440        &mut self,
+441        now: Instant,
+442        token: u64,
+443        network_path: FourTuple,
+444    ) -> OnPathResponseReceived {
+445        // > § 8.2.3
+446        // > Path validation succeeds when a PATH_RESPONSE frame is received that contains the
+447        // > data that was sent in a previous PATH_CHALLENGE frame. A PATH_RESPONSE frame
+448        // > received on any network path validates the path on which the PATH_CHALLENGE was
+449        // > sent.
+450        //
+451        // At this point we have three potentially different network paths:
+452        // - current network path (`Self::network_path`)
+453        // - network path used to send the path challenge (`SentChallengeInfo::network_path`)
+454        // - network path over which the response arrived (`network_path`)
+455        //
+456        // As per the spec, this only validates the network path on which this was *sent*.
+457        match self.on_path_challenges_unconfirmed.remove(&token) {
+458            // Response to an on-path PathChallenge that validates this path.
+459            // The sent path should match the current path. However, it's possible that the
+460            // challenge was sent when no local_ip was known. This case is allowed as well
+461            Some(info) if info.network_path.is_probably_same_path(&self.network_path) => {
+462                self.network_path.update_local_if_same_remote(&network_path);
+463                let sent_instant = info.sent_instant;
+464                if !std::mem::replace(&mut self.validated, true) {
+465                    trace!("new path validated");
+466                }
+467                // Clear any other on-path sent challenge
+468                self.on_path_challenges_unconfirmed.clear();
+469
+470                self.pending_on_path_challenge = false;
+471
+472                // This RTT can only be used for the initial RTT, not as a normal
+473                // sample: https://www.rfc-editor.org/rfc/rfc9002#section-6.2.2-2.
+474                let rtt = now.saturating_duration_since(sent_instant);
+475                self.rtt.reset_initial_rtt(rtt);
+476
+477                let prev_status = std::mem::replace(&mut self.open_status, OpenStatus::Informed);
+478                OnPathResponseReceived::OnPath {
+479                    was_open: prev_status == OpenStatus::Informed,
+480                }
+481            }
+482            // Response to an on-path PathChallenge that does not validate this path
+483            Some(info) => {
+484                // This is a valid path response, but this validates a path we no longer have in
+485                // use. Keep only sent challenges for the current path.
+486
+487                self.on_path_challenges_unconfirmed
+488                    .retain(|_token, i| i.network_path == self.network_path);
+489
+490                // if there are no challenges for the current path, schedule one
+491                if !self.on_path_challenges_unconfirmed.is_empty() {
+492                    self.pending_on_path_challenge = true;
+493                }
+494                OnPathResponseReceived::Ignored {
+495                    sent_on: info.network_path,
+496                    current_path: self.network_path,
+497                }
+498            }
+499            None => match self.off_path_challenges_unconfirmed.remove(&token) {
+500                // Response to an off-path PathChallenge
+501                Some(info) => {
+502                    // Since we do not store validation state for these paths, we only really care
+503                    // about reaching the same remote
+504                    self.off_path_challenges_unconfirmed
+505                        .retain(|_token, i| i.network_path.remote != info.network_path.remote);
+506                    OnPathResponseReceived::OffPath
+507                }
+508                // Response to an unknown PathChallenge. Does not indicate failure
+509                None => OnPathResponseReceived::Unknown,
+510            },
+511        }
+512    }
+513
+514    /// Removes all on-path challenges we remember and cancels sending new on-path challenges.
+515    pub(super) fn reset_on_path_challenges(&mut self) {
+516        self.on_path_challenges_unconfirmed.clear();
+517        self.pending_on_path_challenge = false;
+518    }
+519
+520    #[cfg(feature = "qlog")]
+521    pub(super) fn qlog_recovery_metrics(
+522        &mut self,
+523        path_id: PathId,
+524    ) -> Option<RecoveryMetricsUpdated> {
+525        let controller_metrics = self.congestion.metrics();
+526
+527        let metrics = RecoveryMetrics {
+528            min_rtt: Some(self.rtt.min),
+529            smoothed_rtt: Some(self.rtt.get()),
+530            latest_rtt: Some(self.rtt.latest),
+531            rtt_variance: Some(self.rtt.var),
+532            pto_count: Some(self.pto_count),
+533            bytes_in_flight: Some(self.in_flight.bytes),
+534            packets_in_flight: Some(self.in_flight.ack_eliciting),
+535
+536            congestion_window: Some(controller_metrics.congestion_window),
+537            ssthresh: controller_metrics.ssthresh,
+538            pacing_rate: controller_metrics.pacing_rate,
+539        };
+540
+541        let event = metrics.to_qlog_event(path_id, &self.recovery_metrics);
+542        self.recovery_metrics = metrics;
+543        event
+544    }
+545
+546    /// Return how long we need to wait before sending `bytes_to_send`
+547    ///
+548    /// See [`Pacer::delay`].
+549    pub(super) fn pacing_delay(&mut self, bytes_to_send: u64, now: Instant) -> Option<Instant> {
+550        let smoothed_rtt = self.rtt.get();
+551        self.pacing.delay(
+552            smoothed_rtt,
+553            bytes_to_send,
+554            self.current_mtu(),
+555            self.congestion.window(),
+556            now,
+557        )
+558    }
+559
+560    /// Updates the last observed address report received on this path.
+561    ///
+562    /// If the address was updated, it's returned to be informed to the application.
+563    #[must_use = "updated observed address must be reported to the application"]
+564    pub(super) fn update_observed_addr_report(
+565        &mut self,
+566        observed: ObservedAddr,
+567    ) -> Option<SocketAddr> {
+568        match self.last_observed_addr_report.as_mut() {
+569            Some(prev) => {
+570                if prev.seq_no >= observed.seq_no {
+571                    // frames that do not increase the sequence number on this path are ignored
+572                    None
+573                } else if prev.ip == observed.ip && prev.port == observed.port {
+574                    // keep track of the last seq_no but do not report the address as updated
+575                    prev.seq_no = observed.seq_no;
+576                    None
+577                } else {
+578                    let addr = observed.socket_addr();
+579                    self.last_observed_addr_report = Some(observed);
+580                    Some(addr)
+581                }
+582            }
+583            None => {
+584                let addr = observed.socket_addr();
+585                self.last_observed_addr_report = Some(observed);
+586                Some(addr)
+587            }
+588        }
+589    }
+590
+591    pub(crate) fn remote_status(&self) -> Option<PathStatus> {
+592        self.status.remote_status.map(|(_seq, status)| status)
+593    }
+594
+595    pub(crate) fn local_status(&self) -> PathStatus {
+596        self.status.local_status
+597    }
+598
+599    pub(super) fn generation(&self) -> u64 {
+600        self.generation
+601    }
+602}
+603
+604pub(super) enum OnPathResponseReceived {
+605    /// This response validates the path on its current remote address.
+606    OnPath { was_open: bool },
+607    /// This response is valid, but it's for a remote other than the path's current remote address.
+608    OffPath,
+609    /// The received token is unknown.
+610    Unknown,
+611    /// The response is valid but it's not usable for path validation.
+612    Ignored {
+613        sent_on: FourTuple,
+614        current_path: FourTuple,
+615    },
+616}
+617
+618#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
+619pub(super) enum OpenStatus {
+620    /// A first packet has not been sent using this [`PathId`].
+621    #[default]
+622    Pending,
+623    /// The first packet has been sent using this [`PathId`]. However, it is not yet deemed good
+624    /// enough to be reported to the application.
+625    Sent,
+626    /// The application has been informed of this path.
+627    Informed,
+628}
+629
+630/// Congestion metrics as described in [`recovery_metrics_updated`].
+631///
+632/// [`recovery_metrics_updated`]: https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-quic-events.html#name-recovery_metrics_updated
+633#[cfg(feature = "qlog")]
+634#[derive(Default, Clone, PartialEq, Debug)]
+635#[non_exhaustive]
+636struct RecoveryMetrics {
+637    pub min_rtt: Option<Duration>,
+638    pub smoothed_rtt: Option<Duration>,
+639    pub latest_rtt: Option<Duration>,
+640    pub rtt_variance: Option<Duration>,
+641    pub pto_count: Option<u32>,
+642    pub bytes_in_flight: Option<u64>,
+643    pub packets_in_flight: Option<u64>,
+644    pub congestion_window: Option<u64>,
+645    pub ssthresh: Option<u64>,
+646    pub pacing_rate: Option<u64>,
+647}
+648
+649#[cfg(feature = "qlog")]
+650impl RecoveryMetrics {
+651    /// Retain only values that have been updated since the last snapshot.
+652    fn retain_updated(&self, previous: &Self) -> Self {
+653        macro_rules! keep_if_changed {
+654            ($name:ident) => {
+655                if previous.$name == self.$name {
+656                    None
+657                } else {
+658                    self.$name
+659                }
+660            };
+661        }
+662
+663        Self {
+664            min_rtt: keep_if_changed!(min_rtt),
+665            smoothed_rtt: keep_if_changed!(smoothed_rtt),
+666            latest_rtt: keep_if_changed!(latest_rtt),
+667            rtt_variance: keep_if_changed!(rtt_variance),
+668            pto_count: keep_if_changed!(pto_count),
+669            bytes_in_flight: keep_if_changed!(bytes_in_flight),
+670            packets_in_flight: keep_if_changed!(packets_in_flight),
+671            congestion_window: keep_if_changed!(congestion_window),
+672            ssthresh: keep_if_changed!(ssthresh),
+673            pacing_rate: keep_if_changed!(pacing_rate),
+674        }
+675    }
+676
+677    /// Emit a `MetricsUpdated` event containing only updated values
+678    fn to_qlog_event(&self, path_id: PathId, previous: &Self) -> Option<RecoveryMetricsUpdated> {
+679        let updated = self.retain_updated(previous);
+680
+681        if updated == Self::default() {
+682            return None;
+683        }
+684
+685        Some(RecoveryMetricsUpdated {
+686            min_rtt: updated.min_rtt.map(|rtt| rtt.as_secs_f32()),
+687            smoothed_rtt: updated.smoothed_rtt.map(|rtt| rtt.as_secs_f32()),
+688            latest_rtt: updated.latest_rtt.map(|rtt| rtt.as_secs_f32()),
+689            rtt_variance: updated.rtt_variance.map(|rtt| rtt.as_secs_f32()),
+690            pto_count: updated
+691                .pto_count
+692                .map(|count| count.try_into().unwrap_or(u16::MAX)),
+693            bytes_in_flight: updated.bytes_in_flight,
+694            packets_in_flight: updated.packets_in_flight,
+695            congestion_window: updated.congestion_window,
+696            ssthresh: updated.ssthresh,
+697            pacing_rate: updated.pacing_rate,
+698            path_id: Some(path_id.as_u32() as u64),
+699        })
+700    }
+701}
+702
+703/// RTT estimation for a particular network path
+704#[derive(Copy, Clone, Debug)]
+705pub struct RttEstimator {
+706    /// The most recent RTT measurement made when receiving an ack for a previously unacked packet
+707    latest: Duration,
+708    /// The smoothed RTT of the connection, computed as described in RFC6298
+709    smoothed: Option<Duration>,
+710    /// The RTT variance, computed as described in RFC6298
+711    var: Duration,
+712    /// The minimum RTT seen in the connection, ignoring ack delay.
+713    min: Duration,
+714}
+715
+716impl RttEstimator {
+717    pub(super) fn new(initial_rtt: Duration) -> Self {
+718        Self {
+719            latest: initial_rtt,
+720            smoothed: None,
+721            var: initial_rtt / 2,
+722            min: initial_rtt,
+723        }
+724    }
+725
+726    /// Resets the estimator using a new initial_rtt value.
+727    ///
+728    /// This only resets the initial_rtt **if** no samples have been recorded yet. If there
+729    /// are any recorded samples the initial estimate can not be adjusted after the fact.
+730    ///
+731    /// This is useful when you receive a PATH_RESPONSE in the first packet received on a
+732    /// new path. In this case you can use the delay of the PATH_CHALLENGE-PATH_RESPONSE as
+733    /// the initial RTT to get a better expected estimation.
+734    ///
+735    /// A PATH_CHALLENGE-PATH_RESPONSE pair later in the connection should not be used
+736    /// explicitly as an estimation since PATH_CHALLENGE is an ACK-eliciting packet itself
+737    /// already.
+738    pub(crate) fn reset_initial_rtt(&mut self, initial_rtt: Duration) {
+739        if self.smoothed.is_none() {
+740            self.latest = initial_rtt;
+741            self.var = initial_rtt / 2;
+742            self.min = initial_rtt;
+743        }
+744    }
+745
+746    /// The current best RTT estimation.
+747    pub fn get(&self) -> Duration {
+748        self.smoothed.unwrap_or(self.latest)
+749    }
+750
+751    /// Conservative estimate of RTT
+752    ///
+753    /// Takes the maximum of smoothed and latest RTT, as recommended
+754    /// in 6.1.2 of the recovery spec (draft 29).
+755    pub fn conservative(&self) -> Duration {
+756        self.get().max(self.latest)
+757    }
+758
+759    /// Minimum RTT registered so far for this estimator.
+760    pub fn min(&self) -> Duration {
+761        self.min
+762    }
+763
+764    /// PTO computed as described in RFC9002#6.2.1.
+765    pub(crate) fn pto_base(&self) -> Duration {
+766        self.get() + cmp::max(4 * self.var, TIMER_GRANULARITY)
+767    }
+768
+769    /// Records an RTT sample.
+770    pub(crate) fn update(&mut self, ack_delay: Duration, rtt: Duration) {
+771        self.latest = rtt;
+772        // https://www.rfc-editor.org/rfc/rfc9002.html#section-5.2-3:
+773        // min_rtt does not adjust for ack_delay to avoid underestimating.
+774        self.min = cmp::min(self.min, self.latest);
+775        // Based on RFC6298.
+776        if let Some(smoothed) = self.smoothed {
+777            let adjusted_rtt = if self.min + ack_delay <= self.latest {
+778                self.latest - ack_delay
+779            } else {
+780                self.latest
+781            };
+782            let var_sample = smoothed.abs_diff(adjusted_rtt);
+783            self.var = (3 * self.var + var_sample) / 4;
+784            self.smoothed = Some((7 * smoothed + adjusted_rtt) / 8);
+785        } else {
+786            self.smoothed = Some(self.latest);
+787            self.var = self.latest / 2;
+788            self.min = self.latest;
+789        }
+790    }
+791}
+792
+793#[derive(Default, Debug)]
+794pub(crate) struct PathResponses {
+795    pending: Vec<PathResponse>,
+796}
+797
+798impl PathResponses {
+799    pub(crate) fn push(&mut self, packet: u64, token: u64, network_path: FourTuple) {
+800        /// Arbitrary permissive limit to prevent abuse
+801        const MAX_PATH_RESPONSES: usize = 16;
+802        let response = PathResponse {
+803            packet,
+804            token,
+805            network_path,
+806        };
+807        let existing = self
+808            .pending
+809            .iter_mut()
+810            .find(|x| x.network_path.remote == network_path.remote);
+811        if let Some(existing) = existing {
+812            // Update a queued response
+813            if existing.packet <= packet {
+814                *existing = response;
+815            }
+816            return;
+817        }
+818        if self.pending.len() < MAX_PATH_RESPONSES {
+819            self.pending.push(response);
+820        } else {
+821            // We don't expect to ever hit this with well-behaved peers, so we don't bother dropping
+822            // older challenges.
+823            trace!("ignoring excessive PATH_CHALLENGE");
+824        }
+825    }
+826
+827    pub(crate) fn pop_off_path(&mut self, network_path: FourTuple) -> Option<(u64, FourTuple)> {
+828        let response = *self.pending.last()?;
+829        // We use an exact comparison here, because once we've received for the first time,
+830        // we really should either already have a local_ip, or we will never get one
+831        // (because our OS doesn't support it).
+832        if response.network_path == network_path {
+833            // We don't bother searching further because we expect that the on-path response will
+834            // get drained in the immediate future by a call to `pop_on_path`
+835            return None;
+836        }
+837        self.pending.pop();
+838        Some((response.token, response.network_path))
+839    }
+840
+841    pub(crate) fn pop_on_path(&mut self, network_path: FourTuple) -> Option<u64> {
+842        let response = *self.pending.last()?;
+843        // Using an exact comparison. See explanation in `pop_off_path`.
+844        if response.network_path != network_path {
+845            // We don't bother searching further because we expect that the off-path response will
+846            // get drained in the immediate future by a call to `pop_off_path`
+847            return None;
+848        }
+849        self.pending.pop();
+850        Some(response.token)
+851    }
+852
+853    pub(crate) fn is_empty(&self) -> bool {
+854        self.pending.is_empty()
+855    }
+856}
+857
+858#[derive(Copy, Clone, Debug)]
+859struct PathResponse {
+860    /// The packet number the corresponding PATH_CHALLENGE was received in
+861    packet: u64,
+862    /// The token of the PATH_CHALLENGE
+863    token: u64,
+864    /// The path the corresponding PATH_CHALLENGE was received from
+865    network_path: FourTuple,
+866}
+867
+868/// Summary statistics of packets that have been sent on a particular path, but which have not yet
+869/// been acked or deemed lost
+870#[derive(Debug)]
+871pub(super) struct InFlight {
+872    /// Sum of the sizes of all sent packets considered "in flight" by congestion control
+873    ///
+874    /// The size does not include IP or UDP overhead. Packets only containing ACK frames do not
+875    /// count towards this to ensure congestion control does not impede congestion feedback.
+876    pub(super) bytes: u64,
+877    /// Number of packets in flight containing frames other than ACK and PADDING
+878    ///
+879    /// This can be 0 even when bytes is not 0 because PADDING frames cause a packet to be
+880    /// considered "in flight" by congestion control. However, if this is nonzero, bytes will always
+881    /// also be nonzero.
+882    pub(super) ack_eliciting: u64,
+883}
+884
+885impl InFlight {
+886    fn new() -> Self {
+887        Self {
+888            bytes: 0,
+889            ack_eliciting: 0,
+890        }
+891    }
+892
+893    fn insert(&mut self, packet: &SentPacket) {
+894        self.bytes += u64::from(packet.size);
+895        self.ack_eliciting += u64::from(packet.ack_eliciting);
+896    }
+897
+898    /// Update counters to account for a packet becoming acknowledged, lost, or abandoned
+899    fn remove(&mut self, packet: &SentPacket) {
+900        self.bytes -= u64::from(packet.size);
+901        self.ack_eliciting -= u64::from(packet.ack_eliciting);
+902    }
+903}
+904
+905/// State for QUIC-MULTIPATH PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP frames
+906#[derive(Debug, Clone, Default)]
+907pub(super) struct PathStatusState {
+908    /// The local status
+909    local_status: PathStatus,
+910    /// Local sequence number, for both PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP
+911    ///
+912    /// This is the number of the *next* path status frame to be sent.
+913    local_seq: VarInt,
+914    /// The status set by the remote
+915    remote_status: Option<(VarInt, PathStatus)>,
+916}
+917
+918impl PathStatusState {
+919    /// To be called on received PATH_STATUS_AVAILABLE/PATH_STATUS_BACKUP frames
+920    pub(super) fn remote_update(&mut self, status: PathStatus, seq: VarInt) {
+921        if self.remote_status.is_some_and(|(curr, _)| curr >= seq) {
+922            return trace!(%seq, "ignoring path status update");
+923        }
+924
+925        let prev = self.remote_status.replace((seq, status)).map(|(_, s)| s);
+926        if prev != Some(status) {
+927            debug!(?status, ?seq, "remote changed path status");
+928        }
+929    }
+930
+931    /// Updates the local status
+932    ///
+933    /// If the local status changed, the previous value is returned
+934    pub(super) fn local_update(&mut self, status: PathStatus) -> Option<PathStatus> {
+935        if self.local_status == status {
+936            return None;
+937        }
+938
+939        self.local_seq = self.local_seq.saturating_add(1u8);
+940        Some(std::mem::replace(&mut self.local_status, status))
+941    }
+942
+943    pub(crate) fn seq(&self) -> VarInt {
+944        self.local_seq
+945    }
+946}
+947
+948/// The QUIC-MULTIPATH path status
+949///
+950/// See section "3.3 Path Status Management":
+951/// <https://quicwg.org/multipath/draft-ietf-quic-multipath.html#name-path-status-management>
+952#[cfg_attr(test, derive(test_strategy::Arbitrary))]
+953#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
+954pub enum PathStatus {
+955    /// Paths marked with as available will be used when scheduling packets
+956    ///
+957    /// If multiple paths are available, packets will be scheduled on whichever has
+958    /// capacity.
+959    #[default]
+960    Available,
+961    /// Paths marked as backup will only be used if there are no available paths
+962    ///
+963    /// If the max_idle_timeout is specified the path will be kept alive so that it does not
+964    /// expire.
+965    Backup,
+966}
+967
+968/// Application events about paths
+969#[derive(Debug, Clone, PartialEq, Eq)]
+970pub enum PathEvent {
+971    /// A new path has been opened
+972    Opened {
+973        /// Which path is now open
+974        id: PathId,
+975    },
+976    /// A path was abandoned and is no longer usable.
+977    ///
+978    /// This event will always be followed by [`Self::Discarded`] after some time.
+979    Abandoned {
+980        /// With path was abandoned.
+981        id: PathId,
+982        /// Reason why this path was abandoned.
+983        reason: PathAbandonReason,
+984    },
+985    /// A path was discarded and all remaining state for it has been removed.
+986    ///
+987    /// This event is the last event for a path, and is always emitted after [`Self::Abandoned`].
+988    Discarded {
+989        /// Which path had its state dropped
+990        id: PathId,
+991        /// The final path stats, they are no longer available via [`Connection::stats`]
+992        ///
+993        /// [`Connection::stats`]: super::Connection::stats
+994        path_stats: PathStats,
+995    },
+996    /// The remote changed the status of the path
+997    ///
+998    /// The local status is not changed because of this event. It is up to the application
+999    /// to update the local status, which is used for packet scheduling, when the remote
+1000    /// changes the status.
+1001    RemoteStatus {
+1002        /// Path which has changed status
+1003        id: PathId,
+1004        /// The new status set by the remote
+1005        status: PathStatus,
+1006    },
+1007    /// Received an observation of our external address from the peer.
+1008    ObservedAddr {
+1009        /// Path over which the observed address was reported, [`PathId::ZERO`] when multipath is
+1010        /// not negotiated
+1011        id: PathId,
+1012        /// The address observed by the remote over this path
+1013        addr: SocketAddr,
+1014    },
+1015}
+1016
+1017/// Reason for why a path was abandoned.
+1018#[derive(Debug, Clone, Eq, PartialEq)]
+1019pub enum PathAbandonReason {
+1020    /// The path was closed locally by the application.
+1021    ApplicationClosed {
+1022        /// The error code to be sent with the abandon frame.
+1023        error_code: VarInt,
+1024    },
+1025    /// We didn't receive a path response in time after opening this path.
+1026    ValidationFailed,
+1027    /// We didn't receive any data from the remote within the path's idle timeout.
+1028    TimedOut,
+1029    /// The path became unusable after a local network change.
+1030    UnusableAfterNetworkChange,
+1031    /// The path was opened in a NAT traversal round which was terminated.
+1032    NatTraversalRoundEnded,
+1033    /// The remote closed the path.
+1034    RemoteAbandoned {
+1035        /// The error that was sent with the abandon frame.
+1036        error_code: VarInt,
+1037    },
+1038}
+1039
+1040impl PathAbandonReason {
+1041    /// Returns `true` if the closing of this path was initiated locally.
+1042    pub(crate) fn is_locally_initiated(&self) -> bool {
+1043        !matches!(self, Self::RemoteAbandoned { .. })
+1044    }
+1045
+1046    /// Returns the error code to send with a PATH_ABANDON frame.
+1047    pub(crate) fn error_code(&self) -> TransportErrorCode {
+1048        match self {
+1049            Self::ApplicationClosed { error_code } => (*error_code).into(),
+1050            Self::NatTraversalRoundEnded => TransportErrorCode::APPLICATION_ABANDON_PATH,
+1051            Self::ValidationFailed | Self::TimedOut | Self::UnusableAfterNetworkChange => {
+1052                TransportErrorCode::PATH_UNSTABLE_OR_POOR
+1053            }
+1054            Self::RemoteAbandoned { error_code } => (*error_code).into(),
+1055        }
+1056    }
+1057}
+1058
+1059/// Error from setting path status
+1060#[derive(Debug, Error, Clone, PartialEq, Eq)]
+1061pub enum SetPathStatusError {
+1062    /// Error indicating that a path has not been opened or has already been abandoned
+1063    #[error("closed path")]
+1064    ClosedPath,
+1065    /// Error indicating that this operation requires multipath to be negotiated whereas it hasn't been
+1066    #[error("multipath not negotiated")]
+1067    MultipathNotNegotiated,
+1068}
+1069
+1070/// Error indicating that a path has not been opened or has already been abandoned
+1071#[derive(Debug, Default, Error, Clone, PartialEq, Eq)]
+1072#[error("closed path")]
+1073pub struct ClosedPath {
+1074    pub(super) _private: (),
+1075}
+1076
+1077#[cfg(test)]
+1078mod tests {
+1079    use super::*;
+1080
+1081    #[test]
+1082    fn test_path_id_saturating_add() {
+1083        // add within range behaves normally
+1084        let large: PathId = u16::MAX.into();
+1085        let next = u32::from(u16::MAX) + 1;
+1086        assert_eq!(large.saturating_add(1u8), PathId::from(next));
+1087
+1088        // outside range saturates
+1089        assert_eq!(PathId::MAX.saturating_add(1u8), PathId::MAX)
+1090    }
+1091}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/connection/qlog.rs.html b/pr/509/docs/src/noq_proto/connection/qlog.rs.html new file mode 100644 index 000000000..f3d89ca25 --- /dev/null +++ b/pr/509/docs/src/noq_proto/connection/qlog.rs.html @@ -0,0 +1,1280 @@ +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            },
+316            Timer::PerPath(_, path_timer) => match path_timer {
+317                PathTimer::LossDetection => Some(QlogTimerType::LossTimeout.into()),
+318                PathTimer::PathIdle => Some(TimerType::custom("path_idle")),
+319                PathTimer::PathValidation => Some(QlogTimerType::PathValidation.into()),
+320                PathTimer::PathChallengeLost => Some(TimerType::custom("path_challenge_lost")),
+321                PathTimer::PathOpen => Some(TimerType::custom("path_open")),
+322                PathTimer::PathKeepAlive => Some(TimerType::custom("path_keep_alive")),
+323                PathTimer::Pacing => Some(TimerType::custom("pacing")),
+324                PathTimer::MaxAckDelay => Some(QlogTimerType::Ack.into()),
+325                PathTimer::DiscardPath => Some(TimerType::custom("discard_path")),
+326            },
+327        };
+328
+329        let Some(timer_type) = timer_type else {
+330            return;
+331        };
+332
+333        let delta = match op {
+334            TimerOp::Set(instant) => instant
+335                .checked_duration_since(now)
+336                .map(|dur| dur.as_secs_f32() * 1000.),
+337            _ => None,
+338        };
+339        let path_id = match timer {
+340            Timer::Conn(_) => None,
+341            Timer::PerPath(path_id, _) => Some(path_id.as_u32() as u64),
+342        };
+343
+344        let event_type = match op {
+345            TimerOp::Set(_) => TimerEventType::Set,
+346            TimerOp::Expire => TimerEventType::Expired,
+347            TimerOp::Cancelled => TimerEventType::Cancelled,
+348        };
+349
+350        let event = TimerUpdated {
+351            path_id,
+352            timer_type: Some(timer_type),
+353            timer_id: None,
+354            packet_number_space: None,
+355            event_type,
+356            delta,
+357        };
+358        stream.emit_event(EventData::TimerUpdated(event), now);
+359    }
+360
+361    /// Returns a [`QlogSinkWithTime`] that passes along a `now` timestamp.
+362    ///
+363    /// This may be used if you want to pass a [`QlogSink`] downwards together with the current
+364    /// `now` timestamp, to not have to pass the latter separately as an additional argument just
+365    /// for qlog support.
+366    pub(super) fn with_time(&self, now: Instant) -> QlogSinkWithTime<'_> {
+367        #[cfg(feature = "qlog")]
+368        let s = QlogSinkWithTime { sink: self, now };
+369        #[cfg(not(feature = "qlog"))]
+370        let s = QlogSinkWithTime {
+371            _phantom: PhantomData,
+372        };
+373        s
+374    }
+375}
+376
+377/// A [`QlogSink`] with a `now` timestamp.
+378#[derive(Clone)]
+379pub(super) struct QlogSinkWithTime<'a> {
+380    #[cfg(feature = "qlog")]
+381    sink: &'a QlogSink,
+382    #[cfg(feature = "qlog")]
+383    now: Instant,
+384    #[cfg(not(feature = "qlog"))]
+385    _phantom: PhantomData<&'a ()>,
+386}
+387
+388impl<'a> QlogSinkWithTime<'a> {
+389    pub(super) fn emit_timer_stop(&self, timer: Timer) {
+390        #[cfg(feature = "qlog")]
+391        self.sink.emit_timer(timer, TimerOp::Cancelled, self.now)
+392    }
+393
+394    pub(super) fn emit_timer_set(&self, timer: Timer, expire_at: Instant) {
+395        #[cfg(feature = "qlog")]
+396        self.sink
+397            .emit_timer(timer, TimerOp::Set(expire_at), self.now)
+398    }
+399
+400    pub(super) fn emit_timer_expire(&self, timer: Timer) {
+401        #[cfg(feature = "qlog")]
+402        self.sink.emit_timer(timer, TimerOp::Expire, self.now)
+403    }
+404}
+405
+406#[cfg(feature = "qlog")]
+407enum TimerOp {
+408    Set(Instant),
+409    Expire,
+410    Cancelled,
+411}
+412
+413/// Info about a sent packet. Zero-sized struct if `qlog` feature is not enabled.
+414#[derive(Default)]
+415pub(crate) struct QlogSentPacket {
+416    #[cfg(feature = "qlog")]
+417    inner: PacketSent,
+418}
+419
+420impl QlogSentPacket {
+421    /// Sets data from the packet header.
+422    pub(crate) fn header(
+423        &mut self,
+424        header: &Header,
+425        pn: Option<u64>,
+426        encryption_level: EncryptionLevel,
+427        path_id: PathId,
+428    ) {
+429        #[cfg(feature = "qlog")]
+430        {
+431            self.inner.header.scid = header.src_cid().map(stringify_cid);
+432            self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
+433            self.inner.header.packet_number = pn;
+434            self.inner.header.packet_type = encryption_level.into();
+435            self.inner.header.path_id = Some(path_id.as_u32() as u64);
+436        }
+437    }
+438
+439    /// Adds a PADDING frame.
+440    ///
+441    /// This is a no-op if the `qlog` feature is not enabled.
+442    pub(crate) fn frame_padding(&mut self, count: usize) {
+443        #[cfg(feature = "qlog")]
+444        self.frame_raw(QuicFrame::Padding {
+445            raw: Some(RawInfo {
+446                length: Some(count as u64),
+447                payload_length: Some(count as u64),
+448                data: None,
+449            }),
+450        });
+451    }
+452
+453    /// Adds a frame by pushing a [`QuicFrame`].
+454    ///
+455    /// This function is only available if the `qlog` feature is enabled, because constructing a [`QuicFrame`] may involve
+456    /// calculations which shouldn't be performed if the `qlog` feature is disabled.
+457    #[cfg(feature = "qlog")]
+458    fn frame_raw(&mut self, frame: QuicFrame) {
+459        self.inner.frames.get_or_insert_default().push(frame);
+460    }
+461
+462    /// Finalizes the packet by setting the final packet length (after encryption).
+463    pub(super) fn finalize(&mut self, len: usize) {
+464        #[cfg(feature = "qlog")]
+465        {
+466            self.inner.header.length = Some(len as u16);
+467        }
+468    }
+469
+470    pub(crate) fn record<'a>(&mut self, frame: &EncodableFrame<'a>) {
+471        #[cfg(feature = "qlog")]
+472        self.frame_raw(frame.to_qlog());
+473    }
+474}
+475
+476/// Info about a received packet. Zero-sized struct if `qlog` feature is not enabled.
+477pub(crate) struct QlogRecvPacket {
+478    #[cfg(feature = "qlog")]
+479    inner: PacketReceived,
+480    #[cfg(feature = "qlog")]
+481    padding: usize,
+482}
+483
+484impl QlogRecvPacket {
+485    /// Creates a new [`QlogRecvPacket`]. Noop if `qlog` feature is not enabled.
+486    ///
+487    /// `len` is the packet's full length (before decryption).
+488    pub(crate) fn new(len: usize) -> Self {
+489        #[cfg(not(feature = "qlog"))]
+490        let this = Self {};
+491
+492        #[cfg(feature = "qlog")]
+493        let this = {
+494            let mut this = Self {
+495                inner: Default::default(),
+496                padding: 0,
+497            };
+498            this.inner.header.length = Some(len as u16);
+499            this
+500        };
+501
+502        this
+503    }
+504
+505    /// Adds info from the packet header.
+506    pub(crate) fn header(&mut self, header: &Header, pn: Option<u64>, path_id: PathId) {
+507        #[cfg(feature = "qlog")]
+508        {
+509            let is_0rtt = !header.is_1rtt();
+510            self.inner.header.scid = header.src_cid().map(stringify_cid);
+511            self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
+512            self.inner.header.packet_number = pn;
+513            self.inner.header.packet_type = packet_type(header.space(), is_0rtt);
+514            self.inner.header.path_id = Some(path_id.as_u32() as u64);
+515        }
+516    }
+517
+518    /// Adds a frame.
+519    pub(crate) fn frame(&mut self, frame: &Frame) {
+520        #[cfg(feature = "qlog")]
+521        {
+522            if matches!(frame, crate::Frame::Padding) {
+523                self.padding += 1;
+524            } else {
+525                self.emit_padding();
+526                self.inner
+527                    .frames
+528                    .get_or_insert_default()
+529                    .push(frame.to_qlog())
+530            }
+531        }
+532    }
+533
+534    #[cfg(feature = "qlog")]
+535    fn emit_padding(&mut self) {
+536        if self.padding > 0 {
+537            self.inner
+538                .frames
+539                .get_or_insert_default()
+540                .push(QuicFrame::Padding {
+541                    raw: Some(RawInfo {
+542                        length: Some(self.padding as u64),
+543                        payload_length: Some(self.padding as u64),
+544                        data: None,
+545                    }),
+546                });
+547            self.padding = 0;
+548        }
+549    }
+550}
+551
+552/* Frame conversions to qlog */
+553#[cfg(feature = "qlog")]
+554pub(crate) trait ToQlog {
+555    fn to_qlog(&self) -> QuicFrame;
+556}
+557
+558#[cfg(feature = "qlog")]
+559impl<'a> ToQlog for frame::AckEncoder<'a> {
+560    fn to_qlog(&self) -> QuicFrame {
+561        QuicFrame::Ack {
+562            ack_delay: Some(self.delay as f32),
+563            acked_ranges: Some(AckedRanges::Double(
+564                self.ranges
+565                    .iter()
+566                    .map(|range| (range.start, range.end))
+567                    .collect(),
+568            )),
+569            ect1: self.ecn.map(|e| e.ect1),
+570            ect0: self.ecn.map(|e| e.ect0),
+571            ce: self.ecn.map(|e| e.ce),
+572            raw: None,
+573        }
+574    }
+575}
+576
+577#[cfg(feature = "qlog")]
+578impl ToQlog for frame::AckFrequency {
+579    fn to_qlog(&self) -> QuicFrame {
+580        QuicFrame::AckFrequency {
+581            sequence_number: self.sequence.into_inner(),
+582            ack_eliciting_threshold: self.ack_eliciting_threshold.into_inner(),
+583            requested_max_ack_delay: self.request_max_ack_delay.into_inner(),
+584            reordering_threshold: self.reordering_threshold.into_inner(),
+585            raw: None,
+586        }
+587    }
+588}
+589
+590#[cfg(feature = "qlog")]
+591impl ToQlog for frame::AddAddress {
+592    fn to_qlog(&self) -> QuicFrame {
+593        QuicFrame::AddAddress {
+594            sequence_number: self.seq_no.into_inner(),
+595            ip_v4: match self.ip {
+596                IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
+597                IpAddr::V6(ipv6_addr) => None,
+598            },
+599            ip_v6: match self.ip {
+600                IpAddr::V4(ipv4_addr) => None,
+601                IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
+602            },
+603            port: self.port,
+604        }
+605    }
+606}
+607
+608#[cfg(feature = "qlog")]
+609impl ToQlog for frame::CloseEncoder<'_> {
+610    fn to_qlog(&self) -> QuicFrame {
+611        self.close.to_qlog()
+612    }
+613}
+614
+615#[cfg(feature = "qlog")]
+616impl ToQlog for frame::Close {
+617    fn to_qlog(&self) -> QuicFrame {
+618        match self {
+619            Self::Connection(f) => {
+620                let (error, error_code) = transport_error(f.error_code);
+621                let error = error.map(|transport_error| {
+622                    ConnectionClosedFrameError::TransportError(transport_error)
+623                });
+624                QuicFrame::ConnectionClose {
+625                    error_space: Some(ErrorSpace::TransportError),
+626                    error,
+627                    error_code,
+628                    reason: String::from_utf8(f.reason.to_vec()).ok(),
+629                    reason_bytes: None,
+630                    trigger_frame_type: None,
+631                }
+632            }
+633            Self::Application(f) => QuicFrame::ConnectionClose {
+634                error_space: Some(ErrorSpace::ApplicationError),
+635                error: None,
+636                error_code: Some(f.error_code.into_inner()),
+637                reason: String::from_utf8(f.reason.to_vec()).ok(),
+638                reason_bytes: None,
+639                trigger_frame_type: None,
+640            },
+641        }
+642    }
+643}
+644
+645#[cfg(feature = "qlog")]
+646impl ToQlog for frame::Crypto {
+647    fn to_qlog(&self) -> QuicFrame {
+648        QuicFrame::Crypto {
+649            offset: self.offset,
+650            raw: Some(RawInfo {
+651                length: Some(self.data.len() as u64),
+652                ..Default::default()
+653            }),
+654        }
+655    }
+656}
+657
+658#[cfg(feature = "qlog")]
+659impl ToQlog for frame::Datagram {
+660    fn to_qlog(&self) -> QuicFrame {
+661        QuicFrame::Datagram {
+662            raw: Some(RawInfo {
+663                length: Some(self.data.len() as u64),
+664                ..Default::default()
+665            }),
+666        }
+667    }
+668}
+669
+670#[cfg(feature = "qlog")]
+671impl ToQlog for frame::HandshakeDone {
+672    fn to_qlog(&self) -> QuicFrame {
+673        QuicFrame::HandshakeDone { raw: None }
+674    }
+675}
+676
+677#[cfg(feature = "qlog")]
+678impl ToQlog for frame::ImmediateAck {
+679    fn to_qlog(&self) -> QuicFrame {
+680        QuicFrame::ImmediateAck { raw: None }
+681    }
+682}
+683
+684#[cfg(feature = "qlog")]
+685impl ToQlog for frame::MaxData {
+686    fn to_qlog(&self) -> QuicFrame {
+687        QuicFrame::MaxData {
+688            maximum: self.0.into(),
+689            raw: None,
+690        }
+691    }
+692}
+693
+694#[cfg(feature = "qlog")]
+695impl ToQlog for frame::MaxPathId {
+696    fn to_qlog(&self) -> QuicFrame {
+697        QuicFrame::MaxPathId {
+698            maximum_path_id: self.0.as_u32().into(),
+699            raw: None,
+700        }
+701    }
+702}
+703
+704#[cfg(feature = "qlog")]
+705impl ToQlog for frame::MaxStreamData {
+706    fn to_qlog(&self) -> QuicFrame {
+707        QuicFrame::MaxStreamData {
+708            stream_id: self.id.into(),
+709            maximum: self.offset,
+710            raw: None,
+711        }
+712    }
+713}
+714
+715#[cfg(feature = "qlog")]
+716impl ToQlog for frame::MaxStreams {
+717    fn to_qlog(&self) -> QuicFrame {
+718        QuicFrame::MaxStreams {
+719            maximum: self.count,
+720            stream_type: self.dir.into(),
+721            raw: None,
+722        }
+723    }
+724}
+725
+726#[cfg(feature = "qlog")]
+727impl ToQlog for frame::NewConnectionId {
+728    fn to_qlog(&self) -> QuicFrame {
+729        match self.path_id {
+730            None => QuicFrame::NewConnectionId {
+731                sequence_number: self.sequence,
+732                retire_prior_to: self.retire_prior_to,
+733                connection_id_length: Some(self.id.len() as u8),
+734                connection_id: self.id.to_string(),
+735                stateless_reset_token: Some(self.reset_token.to_string()),
+736                raw: None,
+737            },
+738            Some(path_id) => QuicFrame::PathNewConnectionId {
+739                path_id: path_id.0 as u64,
+740                sequence_number: self.sequence,
+741                retire_prior_to: self.retire_prior_to,
+742                connection_id_length: Some(self.id.len() as u8),
+743                connection_id: self.id.to_string(),
+744                stateless_reset_token: Some(self.reset_token.to_string()),
+745                raw: None,
+746            },
+747        }
+748    }
+749}
+750
+751#[cfg(feature = "qlog")]
+752impl ToQlog for frame::NewToken {
+753    fn to_qlog(&self) -> QuicFrame {
+754        QuicFrame::NewToken {
+755            token: qlog::Token {
+756                ty: Some(TokenType::Retry),
+757                raw: Some(RawInfo {
+758                    data: HexSlice::maybe_string(Some(&self.token)),
+759                    length: Some(self.token.len() as u64),
+760                    payload_length: None,
+761                }),
+762                details: None,
+763            },
+764            raw: None,
+765        }
+766    }
+767}
+768
+769#[cfg(feature = "qlog")]
+770impl ToQlog for frame::ObservedAddr {
+771    fn to_qlog(&self) -> QuicFrame {
+772        QuicFrame::ObservedAddress {
+773            sequence_number: self.seq_no.into_inner(),
+774            ip_v4: match self.ip {
+775                IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
+776                IpAddr::V6(ipv6_addr) => None,
+777            },
+778            ip_v6: match self.ip {
+779                IpAddr::V4(ipv4_addr) => None,
+780                IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
+781            },
+782            port: self.port,
+783            raw: None,
+784        }
+785    }
+786}
+787
+788#[cfg(feature = "qlog")]
+789impl ToQlog for frame::PathAbandon {
+790    fn to_qlog(&self) -> QuicFrame {
+791        QuicFrame::PathAbandon {
+792            path_id: self.path_id.as_u32().into(),
+793            error_code: self.error_code.into(),
+794            raw: None,
+795        }
+796    }
+797}
+798
+799#[cfg(feature = "qlog")]
+800impl ToQlog for frame::PathAckEncoder<'_> {
+801    fn to_qlog(&self) -> QuicFrame {
+802        QuicFrame::PathAck {
+803            path_id: self.path_id.as_u32() as u64,
+804            ack_delay: Some(self.delay as f32),
+805            acked_ranges: Some(AckedRanges::Double(
+806                self.ranges
+807                    .iter()
+808                    .map(|range| (range.start, range.end))
+809                    .collect(),
+810            )),
+811            ect1: self.ecn.map(|e| e.ect1),
+812            ect0: self.ecn.map(|e| e.ect0),
+813            ce: self.ecn.map(|e| e.ce),
+814            raw: None,
+815        }
+816    }
+817}
+818
+819#[cfg(feature = "qlog")]
+820impl ToQlog for frame::PathChallenge {
+821    #[cfg(feature = "qlog")]
+822    fn to_qlog(&self) -> QuicFrame {
+823        QuicFrame::PathChallenge {
+824            data: Some(self.to_string()),
+825            raw: None,
+826        }
+827    }
+828}
+829
+830#[cfg(feature = "qlog")]
+831impl ToQlog for frame::PathCidsBlocked {
+832    fn to_qlog(&self) -> QuicFrame {
+833        QuicFrame::PathCidsBlocked {
+834            path_id: self.path_id.as_u32().into(),
+835            next_sequence_number: self.next_seq.into(),
+836            raw: None,
+837        }
+838    }
+839}
+840
+841#[cfg(feature = "qlog")]
+842impl ToQlog for frame::PathResponse {
+843    fn to_qlog(&self) -> QuicFrame {
+844        QuicFrame::PathResponse {
+845            data: Some(self.to_string()),
+846            raw: None,
+847        }
+848    }
+849}
+850
+851#[cfg(feature = "qlog")]
+852impl ToQlog for frame::ReachOut {
+853    fn to_qlog(&self) -> QuicFrame {
+854        QuicFrame::ReachOut {
+855            round: self.round.into_inner(),
+856            ip_v4: match self.ip {
+857                IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
+858                IpAddr::V6(ipv6_addr) => None,
+859            },
+860            ip_v6: match self.ip {
+861                IpAddr::V4(ipv4_addr) => None,
+862                IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
+863            },
+864            port: self.port,
+865        }
+866    }
+867}
+868
+869#[cfg(feature = "qlog")]
+870impl ToQlog for frame::Ping {
+871    fn to_qlog(&self) -> QuicFrame {
+872        QuicFrame::Ping { raw: None }
+873    }
+874}
+875
+876#[cfg(feature = "qlog")]
+877impl ToQlog for frame::PathStatusAvailable {
+878    fn to_qlog(&self) -> QuicFrame {
+879        QuicFrame::PathStatusAvailable {
+880            path_id: self.path_id.as_u32().into(),
+881            path_status_sequence_number: self.status_seq_no.into(),
+882            raw: None,
+883        }
+884    }
+885}
+886
+887#[cfg(feature = "qlog")]
+888impl ToQlog for frame::PathStatusBackup {
+889    fn to_qlog(&self) -> QuicFrame {
+890        QuicFrame::PathStatusBackup {
+891            path_id: self.path_id.as_u32().into(),
+892            path_status_sequence_number: self.status_seq_no.into(),
+893            raw: None,
+894        }
+895    }
+896}
+897
+898#[cfg(feature = "qlog")]
+899impl ToQlog for frame::PathsBlocked {
+900    fn to_qlog(&self) -> QuicFrame {
+901        QuicFrame::PathsBlocked {
+902            maximum_path_id: self.0.as_u32().into(),
+903            raw: None,
+904        }
+905    }
+906}
+907
+908#[cfg(feature = "qlog")]
+909impl ToQlog for frame::ResetStream {
+910    fn to_qlog(&self) -> QuicFrame {
+911        QuicFrame::ResetStream {
+912            stream_id: self.id.into(),
+913            error_code: Some(self.error_code.into_inner()),
+914            final_size: self.final_offset.into(),
+915            error: ApplicationError::Unknown,
+916            raw: None,
+917        }
+918    }
+919}
+920
+921#[cfg(feature = "qlog")]
+922impl ToQlog for frame::StopSending {
+923    fn to_qlog(&self) -> QuicFrame {
+924        QuicFrame::StopSending {
+925            stream_id: self.id.into(),
+926            error_code: Some(self.error_code.into_inner()),
+927            error: ApplicationError::Unknown,
+928            raw: None,
+929        }
+930    }
+931}
+932
+933#[cfg(feature = "qlog")]
+934impl ToQlog for frame::RetireConnectionId {
+935    fn to_qlog(&self) -> QuicFrame {
+936        match self.path_id {
+937            None => QuicFrame::RetireConnectionId {
+938                sequence_number: self.sequence,
+939                raw: None,
+940            },
+941            Some(path_id) => QuicFrame::PathRetireConnectionId {
+942                path_id: path_id.0 as u64,
+943                sequence_number: self.sequence,
+944                raw: None,
+945            },
+946        }
+947    }
+948}
+949
+950#[cfg(feature = "qlog")]
+951impl ToQlog for frame::RemoveAddress {
+952    fn to_qlog(&self) -> QuicFrame {
+953        QuicFrame::RemoveAddress {
+954            sequence_number: self.seq_no.into_inner(),
+955        }
+956    }
+957}
+958
+959#[cfg(feature = "qlog")]
+960impl ToQlog for frame::StreamMetaEncoder {
+961    fn to_qlog(&self) -> QuicFrame {
+962        let meta = &self.meta;
+963        QuicFrame::Stream {
+964            stream_id: meta.id.into(),
+965            offset: Some(meta.offsets.start),
+966            fin: Some(meta.fin),
+967            raw: Some(RawInfo {
+968                length: Some(meta.offsets.end - meta.offsets.start),
+969                ..Default::default()
+970            }),
+971        }
+972    }
+973}
+974
+975#[cfg(feature = "qlog")]
+976impl Frame {
+977    /// Converts a [`crate::Frame`] into a [`QuicFrame`].
+978    pub(crate) fn to_qlog(&self) -> QuicFrame {
+979        match self {
+980            Self::Padding => QuicFrame::Padding {
+981                raw: Some(RawInfo {
+982                    length: None,
+983                    payload_length: Some(1),
+984                    data: None,
+985                }),
+986            },
+987            Self::Ping => frame::Ping.to_qlog(),
+988            Self::Ack(f) => QuicFrame::Ack {
+989                ack_delay: Some(f.delay as f32),
+990                acked_ranges: Some(AckedRanges::Double(
+991                    f.iter().map(|range| (range.start, range.end)).collect(),
+992                )),
+993                ect1: f.ecn.as_ref().map(|e| e.ect1),
+994                ect0: f.ecn.as_ref().map(|e| e.ect0),
+995                ce: f.ecn.as_ref().map(|e| e.ce),
+996                raw: None,
+997            },
+998            Self::ResetStream(f) => f.to_qlog(),
+999            Self::StopSending(f) => f.to_qlog(),
+1000            Self::Crypto(f) => f.to_qlog(),
+1001            Self::NewToken(f) => f.to_qlog(),
+1002            Self::Stream(s) => QuicFrame::Stream {
+1003                stream_id: s.id.into(),
+1004                offset: Some(s.offset),
+1005                fin: Some(s.fin),
+1006                raw: Some(RawInfo {
+1007                    length: Some(s.data.len() as u64),
+1008                    ..Default::default()
+1009                }),
+1010            },
+1011            Self::MaxData(v) => v.to_qlog(),
+1012            Self::MaxStreamData(f) => f.to_qlog(),
+1013            Self::MaxStreams(f) => f.to_qlog(),
+1014            Self::DataBlocked(DataBlocked(offset)) => QuicFrame::DataBlocked {
+1015                limit: *offset,
+1016                raw: None,
+1017            },
+1018            Self::StreamDataBlocked(StreamDataBlocked { id, offset }) => {
+1019                QuicFrame::StreamDataBlocked {
+1020                    stream_id: (*id).into(),
+1021                    limit: *offset,
+1022                    raw: None,
+1023                }
+1024            }
+1025            Self::StreamsBlocked(StreamsBlocked { dir, limit }) => QuicFrame::StreamsBlocked {
+1026                stream_type: (*dir).into(),
+1027                limit: *limit,
+1028                raw: None,
+1029            },
+1030            Self::NewConnectionId(f) => f.to_qlog(),
+1031            Self::RetireConnectionId(f) => f.to_qlog(),
+1032            Self::PathChallenge(f) => f.to_qlog(),
+1033            Self::PathResponse(f) => f.to_qlog(),
+1034            Self::Close(close) => close.to_qlog(),
+1035            Self::Datagram(d) => d.to_qlog(),
+1036            Self::HandshakeDone => frame::HandshakeDone.to_qlog(),
+1037            Self::PathAck(ack) => QuicFrame::PathAck {
+1038                path_id: ack.path_id.as_u32().into(),
+1039                ack_delay: Some(ack.delay as f32),
+1040                ect1: ack.ecn.as_ref().map(|e| e.ect1),
+1041                ect0: ack.ecn.as_ref().map(|e| e.ect0),
+1042                ce: ack.ecn.as_ref().map(|e| e.ce),
+1043                raw: None,
+1044                acked_ranges: Some(AckedRanges::Double(
+1045                    ack.ranges
+1046                        .iter()
+1047                        .map(|range| (range.start, range.end))
+1048                        .collect(),
+1049                )),
+1050            },
+1051            Self::PathAbandon(frame) => frame.to_qlog(),
+1052            Self::PathStatusAvailable(frame) => frame.to_qlog(),
+1053            Self::PathStatusBackup(frame) => frame.to_qlog(),
+1054            Self::PathsBlocked(frame) => frame.to_qlog(),
+1055            Self::PathCidsBlocked(frame) => frame.to_qlog(),
+1056            Self::MaxPathId(f) => f.to_qlog(),
+1057            Self::AckFrequency(f) => f.to_qlog(),
+1058            Self::ImmediateAck => frame::ImmediateAck.to_qlog(),
+1059            Self::ObservedAddr(f) => f.to_qlog(),
+1060            Self::AddAddress(f) => f.to_qlog(),
+1061            Self::ReachOut(f) => f.to_qlog(),
+1062            Self::RemoveAddress(f) => f.to_qlog(),
+1063        }
+1064    }
+1065}
+1066
+1067#[cfg(feature = "qlog")]
+1068impl From<crate::Dir> for StreamType {
+1069    fn from(value: crate::Dir) -> Self {
+1070        match value {
+1071            crate::Dir::Bi => Self::Bidirectional,
+1072            crate::Dir::Uni => Self::Unidirectional,
+1073        }
+1074    }
+1075}
+1076
+1077#[cfg(feature = "qlog")]
+1078fn packet_type(space: SpaceKind, is_0rtt: bool) -> PacketType {
+1079    match space {
+1080        SpaceKind::Initial => PacketType::Initial,
+1081        SpaceKind::Handshake => PacketType::Handshake,
+1082        SpaceKind::Data if is_0rtt => PacketType::ZeroRtt,
+1083        SpaceKind::Data => PacketType::OneRtt,
+1084    }
+1085}
+1086
+1087#[cfg(feature = "qlog")]
+1088impl From<EncryptionLevel> for PacketType {
+1089    fn from(encryption_level: EncryptionLevel) -> Self {
+1090        match encryption_level {
+1091            EncryptionLevel::Initial => Self::Initial,
+1092            EncryptionLevel::Handshake => Self::Handshake,
+1093            EncryptionLevel::ZeroRtt => Self::ZeroRtt,
+1094            EncryptionLevel::OneRtt => Self::OneRtt,
+1095        }
+1096    }
+1097}
+1098
+1099#[cfg(feature = "qlog")]
+1100fn stringify_cid(cid: ConnectionId) -> String {
+1101    format!("{cid}")
+1102}
+1103
+1104#[cfg(feature = "qlog")]
+1105fn tuple_endpoint_info(
+1106    ip: Option<IpAddr>,
+1107    port: Option<u16>,
+1108    cid: Option<ConnectionId>,
+1109) -> TupleEndpointInfo {
+1110    let (ip_v4, port_v4, ip_v6, port_v6) = match ip {
+1111        Some(addr) => match addr {
+1112            IpAddr::V4(ipv4_addr) => (Some(ipv4_addr.to_string()), port, None, None),
+1113            IpAddr::V6(ipv6_addr) => (None, None, Some(ipv6_addr.to_string()), port),
+1114        },
+1115        None => (None, None, None, None),
+1116    };
+1117    TupleEndpointInfo {
+1118        ip_v4,
+1119        port_v4,
+1120        ip_v6,
+1121        port_v6,
+1122        connection_ids: cid.map(|cid| vec![cid.to_string()]),
+1123    }
+1124}
+1125
+1126#[cfg(feature = "qlog")]
+1127fn transport_error(code: TransportErrorCode) -> (Option<quic::TransportError>, Option<u64>) {
+1128    let transport_error = match code {
+1129        TransportErrorCode::NO_ERROR => Some(quic::TransportError::NoError),
+1130        TransportErrorCode::INTERNAL_ERROR => Some(quic::TransportError::InternalError),
+1131        TransportErrorCode::CONNECTION_REFUSED => Some(quic::TransportError::ConnectionRefused),
+1132        TransportErrorCode::FLOW_CONTROL_ERROR => Some(quic::TransportError::FlowControlError),
+1133        TransportErrorCode::STREAM_LIMIT_ERROR => Some(quic::TransportError::StreamLimitError),
+1134        TransportErrorCode::STREAM_STATE_ERROR => Some(quic::TransportError::StreamStateError),
+1135        TransportErrorCode::FINAL_SIZE_ERROR => Some(quic::TransportError::FinalSizeError),
+1136        TransportErrorCode::FRAME_ENCODING_ERROR => Some(quic::TransportError::FrameEncodingError),
+1137        TransportErrorCode::TRANSPORT_PARAMETER_ERROR => {
+1138            Some(quic::TransportError::TransportParameterError)
+1139        }
+1140        TransportErrorCode::CONNECTION_ID_LIMIT_ERROR => {
+1141            Some(quic::TransportError::ConnectionIdLimitError)
+1142        }
+1143        TransportErrorCode::PROTOCOL_VIOLATION => Some(quic::TransportError::ProtocolViolation),
+1144        TransportErrorCode::INVALID_TOKEN => Some(quic::TransportError::InvalidToken),
+1145        TransportErrorCode::APPLICATION_ERROR => Some(quic::TransportError::ApplicationError),
+1146        TransportErrorCode::CRYPTO_BUFFER_EXCEEDED => {
+1147            Some(quic::TransportError::CryptoBufferExceeded)
+1148        }
+1149        TransportErrorCode::KEY_UPDATE_ERROR => Some(quic::TransportError::KeyUpdateError),
+1150        TransportErrorCode::AEAD_LIMIT_REACHED => Some(quic::TransportError::AeadLimitReached),
+1151        TransportErrorCode::NO_VIABLE_PATH => Some(quic::TransportError::NoViablePath),
+1152        // multipath
+1153        TransportErrorCode::APPLICATION_ABANDON_PATH => {
+1154            Some(quic::TransportError::ApplicationAbandonPath)
+1155        }
+1156        TransportErrorCode::PATH_RESOURCE_LIMIT_REACHED => {
+1157            Some(quic::TransportError::PathResourceLimitReached)
+1158        }
+1159        TransportErrorCode::PATH_UNSTABLE_OR_POOR => Some(quic::TransportError::PathUnstableOrPoor),
+1160        TransportErrorCode::NO_CID_AVAILABLE_FOR_PATH => {
+1161            Some(quic::TransportError::NoCidsAvailableForPath)
+1162        }
+1163        _ => None,
+1164    };
+1165    let code = match transport_error {
+1166        None => Some(code.into()),
+1167        Some(_) => None,
+1168    };
+1169    (transport_error, code)
+1170}
+1171
+1172#[cfg(feature = "qlog")]
+1173fn fmt_tuple_id(path_id: u64) -> String {
+1174    format!("p{path_id}")
+1175}
+1176
+1177#[cfg(feature = "qlog")]
+1178impl TransportParameters {
+1179    fn to_qlog(self, initiator: TransportInitiator) -> ParametersSet {
+1180        ParametersSet {
+1181            initiator: Some(initiator),
+1182            resumption_allowed: None,
+1183            early_data_enabled: None,
+1184            tls_cipher: None,
+1185            original_destination_connection_id: self
+1186                .original_dst_cid
+1187                .as_ref()
+1188                .map(ToString::to_string),
+1189            initial_source_connection_id: self.initial_src_cid.as_ref().map(ToString::to_string),
+1190            retry_source_connection_id: self.retry_src_cid.as_ref().map(ToString::to_string),
+1191            stateless_reset_token: self.stateless_reset_token.as_ref().map(ToString::to_string),
+1192            disable_active_migration: Some(self.disable_active_migration),
+1193            max_idle_timeout: Some(self.max_idle_timeout.into()),
+1194            max_udp_payload_size: Some(self.max_udp_payload_size.into()),
+1195            ack_delay_exponent: Some(self.ack_delay_exponent.into()),
+1196            max_ack_delay: Some(self.max_ack_delay.into()),
+1197            active_connection_id_limit: Some(self.active_connection_id_limit.into()),
+1198            initial_max_data: Some(self.initial_max_data.into()),
+1199            initial_max_stream_data_bidi_local: Some(
+1200                self.initial_max_stream_data_bidi_local.into(),
+1201            ),
+1202            initial_max_stream_data_bidi_remote: Some(
+1203                self.initial_max_stream_data_bidi_remote.into(),
+1204            ),
+1205            initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
+1206            initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
+1207            initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
+1208            preferred_address: self.preferred_address.as_ref().map(Into::into),
+1209            min_ack_delay: self.min_ack_delay.map(Into::into),
+1210            address_discovery: self.address_discovery_role.to_qlog(),
+1211            initial_max_path_id: self.initial_max_path_id.map(|p| p.as_u32() as u64),
+1212            max_remote_nat_traversal_addresses: self
+1213                .max_remote_nat_traversal_addresses
+1214                .map(|v| u64::from(v.get())),
+1215            max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
+1216            grease_quic_bit: Some(self.grease_quic_bit),
+1217            unknown_parameters: Default::default(),
+1218        }
+1219    }
+1220
+1221    fn to_qlog_restored(self) -> ParametersRestored {
+1222        ParametersRestored {
+1223            disable_active_migration: Some(self.disable_active_migration),
+1224            max_idle_timeout: Some(self.max_idle_timeout.into()),
+1225            max_udp_payload_size: Some(self.max_udp_payload_size.into()),
+1226            active_connection_id_limit: Some(self.active_connection_id_limit.into()),
+1227            initial_max_data: Some(self.initial_max_data.into()),
+1228            initial_max_stream_data_bidi_local: Some(
+1229                self.initial_max_stream_data_bidi_local.into(),
+1230            ),
+1231            initial_max_stream_data_bidi_remote: Some(
+1232                self.initial_max_stream_data_bidi_remote.into(),
+1233            ),
+1234            initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
+1235            initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
+1236            initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
+1237            max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
+1238            grease_quic_bit: Some(self.grease_quic_bit),
+1239        }
+1240    }
+1241}
+1242
+1243#[cfg(feature = "qlog")]
+1244impl From<&crate::transport_parameters::PreferredAddress> for PreferredAddress {
+1245    fn from(value: &crate::transport_parameters::PreferredAddress) -> Self {
+1246        let port_v4 = value.address_v4.map(|addr| addr.port()).unwrap_or_default();
+1247        let port_v6 = value.address_v6.map(|addr| addr.port()).unwrap_or_default();
+1248        let ip_v4 = value
+1249            .address_v4
+1250            .map(|addr| addr.ip().to_string())
+1251            .unwrap_or_default();
+1252        let ip_v6 = value
+1253            .address_v6
+1254            .map(|addr| addr.ip().to_string())
+1255            .unwrap_or_default();
+1256        let connection_id = value.connection_id.to_string();
+1257        let stateless_reset_token = value.stateless_reset_token.to_string();
+1258
+1259        Self {
+1260            ip_v4,
+1261            ip_v6,
+1262            port_v4,
+1263            port_v6,
+1264            connection_id,
+1265            stateless_reset_token,
+1266        }
+1267    }
+1268}
+1269
+1270#[cfg(feature = "qlog")]
+1271impl crate::address_discovery::Role {
+1272    fn to_qlog(self) -> Option<AddressDiscoveryRole> {
+1273        match self {
+1274            Self::SendOnly => Some(AddressDiscoveryRole::SendOnly),
+1275            Self::ReceiveOnly => Some(AddressDiscoveryRole::ReceiveOnly),
+1276            Self::Both => Some(AddressDiscoveryRole::Both),
+1277            Self::Disabled => None,
+1278        }
+1279    }
+1280}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/connection/send_buffer.rs.html b/pr/509/docs/src/noq_proto/connection/send_buffer.rs.html new file mode 100644 index 000000000..c3b5ca93e --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/connection/spaces.rs.html b/pr/509/docs/src/noq_proto/connection/spaces.rs.html new file mode 100644 index 000000000..b82e8e13c --- /dev/null +++ b/pr/509/docs/src/noq_proto/connection/spaces.rs.html @@ -0,0 +1,1533 @@ +spaces.rs - source

noq_proto/connection/
spaces.rs

1use std::{
+2    cmp,
+3    collections::{BTreeMap, BTreeSet, VecDeque},
+4    mem,
+5    net::IpAddr,
+6    ops::{Bound, Index, IndexMut},
+7};
+8
+9use rand::{CryptoRng, Rng};
+10use rustc_hash::{FxHashMap, FxHashSet};
+11use sorted_index_buffer::SortedIndexBuffer;
+12use tracing::trace;
+13
+14use super::PathId;
+15use crate::{
+16    Dir, Duration, FourTuple, Instant, StreamId, TransportError, TransportErrorCode, VarInt,
+17    connection::StreamsState,
+18    frame::{self, AddAddress, RemoveAddress},
+19    packet::SpaceId,
+20    range_set::ArrayRangeSet,
+21    shared::IssuedCid,
+22};
+23
+24pub(super) struct PacketSpace {
+25    /// Data to send
+26    pub(super) pending: Retransmits,
+27
+28    /// Multipath packet number spaces
+29    ///
+30    /// Each [`PathId`] has it's own [`PacketNumberSpace`].  Only the [`SpaceId::Data`] can
+31    /// have multiple packet number spaces, the other spaces only have a number space for
+32    /// `PathId::ZERO`, which is populated at creation.
+33    pub(super) number_spaces: BTreeMap<PathId, PacketNumberSpace>,
+34}
+35
+36impl PacketSpace {
+37    pub(super) fn new(now: Instant, space: SpaceId, rng: &mut (impl CryptoRng + ?Sized)) -> Self {
+38        let number_space_0 = PacketNumberSpace::new(now, space, rng);
+39        Self {
+40            pending: Retransmits::default(),
+41            number_spaces: BTreeMap::from([(PathId::ZERO, number_space_0)]),
+42        }
+43    }
+44
+45    #[cfg(test)]
+46    pub(super) fn new_deterministic(now: Instant, space: SpaceId) -> Self {
+47        let number_space_0 = PacketNumberSpace::new_deterministic(now, space);
+48        Self {
+49            pending: Retransmits::default(),
+50            number_spaces: BTreeMap::from([(PathId::ZERO, number_space_0)]),
+51        }
+52    }
+53
+54    /// Returns the [`PacketNumberSpace`] for a path
+55    ///
+56    /// When multipath is disabled use [`PathId::ZERO`].
+57    pub(super) fn path_space(&self, path_id: PathId) -> Option<&PacketNumberSpace> {
+58        self.number_spaces.get(&path_id)
+59    }
+60
+61    /// Returns a mutable reference to the [`PacketNumberSpace`] for a path
+62    ///
+63    /// When multipath is disabled use [`PathId::ZERO`].
+64    pub(super) fn path_space_mut(&mut self, path_id: PathId) -> Option<&mut PacketNumberSpace> {
+65        self.number_spaces.get_mut(&path_id)
+66    }
+67
+68    /// Returns the [`PacketNumberSpace`] for a path
+69    ///
+70    /// When multipath is disabled use `PathId::ZERO`.
+71    // TODO(flub): Note that this only exists as `&mut self` because it creates a new
+72    //    [`PacketNumberSpace`] if one is not yet available for a path.  This forces a few
+73    //    more `&mut` references to users than strictly needed.  An alternative would be to
+74    //    return an Option but that would need to be handled for all callers.  This could be
+75    //    worth exploring once we have all the main multipath bits fitted.
+76    pub(super) fn for_path(&mut self, path: PathId) -> &mut PacketNumberSpace {
+77        self.number_spaces
+78            .get_mut(&path)
+79            .unwrap_or_else(|| panic!("PacketNumberSpace missing for {path}"))
+80    }
+81
+82    pub(super) fn iter_paths_mut(&mut self) -> impl Iterator<Item = &mut PacketNumberSpace> {
+83        self.number_spaces.values_mut()
+84    }
+85
+86    /// Queue data for a tail loss probe (or anti-amplification deadlock prevention) packet
+87    ///
+88    /// Does nothing if no tail loss probe needs to be sent.
+89    ///
+90    /// Probes are sent similarly to normal packets when an expected ACK has not arrived. We never
+91    /// deem a packet lost until we receive an ACK that should have included it, but if a trailing
+92    /// run of packets (or their ACKs) are lost, this might not happen in a timely fashion. We send
+93    /// probe packets to force an ACK, and exempt them from congestion control to prevent a deadlock
+94    /// when the congestion window is filled with lost tail packets.
+95    ///
+96    /// We prefer to send new data, to make the most efficient use of bandwidth. If there's no data
+97    /// waiting to be sent, then we retransmit in-flight data to reduce odds of loss. If there's no
+98    /// in-flight data either, we're probably a client guarding against a handshake
+99    /// anti-amplification deadlock and we just make something up.
+100    pub(super) fn maybe_queue_probe(
+101        &mut self,
+102        path_id: PathId,
+103        request_immediate_ack: bool,
+104        streams: &StreamsState,
+105    ) {
+106        if self.for_path(path_id).loss_probes == 0 {
+107            return;
+108        }
+109
+110        if request_immediate_ack {
+111            // The probe should be ACKed without delay (should only be used in the Data space and
+112            // when the peer supports the acknowledgement frequency extension)
+113            self.for_path(path_id).immediate_ack_pending = true;
+114        }
+115
+116        // Retransmit the data of the oldest in-flight packet
+117        if !self.pending.is_empty(streams) {
+118            // There's real data to send here, no need to make something up
+119            return;
+120        }
+121
+122        // We use retransmits from any path.
+123        for packet in self
+124            .number_spaces
+125            .values_mut()
+126            .flat_map(|s| s.sent_packets.values_mut())
+127        {
+128            if !packet.retransmits.is_empty(streams) {
+129                // Remove retransmitted data from the old packet so we don't end up retransmitting
+130                // it *again* even if the copy we're sending now gets acknowledged.
+131                self.pending |= mem::take(&mut packet.retransmits);
+132                return;
+133            }
+134        }
+135
+136        // Nothing new to send and nothing to retransmit, so fall back on a ping. This should only
+137        // happen in rare cases during the handshake when the server becomes blocked by
+138        // anti-amplification.
+139        if !self.for_path(path_id).immediate_ack_pending {
+140            self.for_path(path_id).ping_pending = true;
+141        }
+142    }
+143
+144    /// Whether there is anything to send in this space
+145    ///
+146    /// For the data space [`Connection::can_send_1rtt`] also needs to be consulted. Prefer
+147    /// to use [`Connection::space_can_send`] which handles this.
+148    ///
+149    /// [`Connection::can_send_1rtt`]: super::Connection::can_send_1rtt
+150    /// [`Connection::space_can_send`]: super::Connection::space_can_send
+151    pub(super) fn can_send(&self, path_id: PathId, streams: &StreamsState) -> SendableFrames {
+152        let acks = self
+153            .number_spaces
+154            .values()
+155            .any(|pns| pns.pending_acks.can_send());
+156        let space_only = self
+157            .number_spaces
+158            .get(&path_id)
+159            .is_some_and(|s| s.ping_pending || s.immediate_ack_pending);
+160        let other = !self.pending.is_empty(streams) || space_only;
+161        SendableFrames {
+162            acks,
+163            close: false,
+164            space_only,
+165            other,
+166        }
+167    }
+168}
+169
+170impl Index<SpaceId> for [PacketSpace; 3] {
+171    type Output = PacketSpace;
+172    fn index(&self, space: SpaceId) -> &PacketSpace {
+173        &self.as_ref()[space as usize]
+174    }
+175}
+176
+177impl IndexMut<SpaceId> for [PacketSpace; 3] {
+178    fn index_mut(&mut self, space: SpaceId) -> &mut PacketSpace {
+179        &mut self.as_mut()[space as usize]
+180    }
+181}
+182
+183/// The three QUIC packet number space kinds
+184///
+185/// Unlike [`SpaceId`], this always has exactly three variants — it represents the
+186/// encryption level / space kind, not a specific packet number space identity.
+187#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+188pub(crate) enum SpaceKind {
+189    /// Initial packets (client and server).
+190    Initial = 0,
+191    /// Handshake packets.
+192    Handshake = 1,
+193    /// Data (1-RTT and 0-RTT)
+194    Data = 2,
+195}
+196
+197impl SpaceKind {
+198    /// Returns the encryption level for this space kind.
+199    pub(crate) fn encryption_level(self) -> super::EncryptionLevel {
+200        match self {
+201            Self::Initial => super::EncryptionLevel::Initial,
+202            Self::Handshake => super::EncryptionLevel::Handshake,
+203            Self::Data => super::EncryptionLevel::OneRtt,
+204        }
+205    }
+206}
+207
+208impl Index<SpaceKind> for [PacketSpace; 3] {
+209    type Output = PacketSpace;
+210    fn index(&self, space: SpaceKind) -> &PacketSpace {
+211        &self.as_ref()[space as usize]
+212    }
+213}
+214
+215impl IndexMut<SpaceKind> for [PacketSpace; 3] {
+216    fn index_mut(&mut self, space: SpaceKind) -> &mut PacketSpace {
+217        &mut self.as_mut()[space as usize]
+218    }
+219}
+220
+221/// The per-path packet number space to support multipath.
+222///
+223/// This contains the data specific to a per-path packet number space.  You should access
+224/// this via [`PacketSpace::for_path`].
+225pub(super) struct PacketNumberSpace {
+226    /// Highest received packet number, if any
+227    pub(super) rx_packet: Option<u64>,
+228    /// The packet number of the next packet that will be sent, if any. In the Data space, the
+229    /// packet number stored here is sometimes skipped by [`PacketNumberFilter`] logic.
+230    pub(super) next_packet_number: u64,
+231    /// The largest packet number the remote peer acknowledged in an ACK frame.
+232    pub(super) largest_acked_packet: Option<u64>,
+233    pub(super) largest_acked_packet_sent: Instant,
+234    /// The highest-numbered ACK-eliciting packet we've sent
+235    pub(super) largest_ack_eliciting_sent: u64,
+236    /// Number of packets in `sent_packets` with numbers above `largest_ack_eliciting_sent`
+237    pub(super) unacked_non_ack_eliciting_tail: u64,
+238    /// Transmitted but not acked
+239    // We use a BTreeMap here so we can efficiently query by range on ACK and for loss detection
+240    pub(super) sent_packets: SortedIndexBuffer<SentPacket>,
+241    /// Packets that were deemed lost
+242    // Older packets are regularly removed in `Connection::drain_lost_packets`.
+243    pub(super) lost_packets: SortedIndexBuffer<LostPacket>,
+244    /// Number of explicit congestion notification codepoints seen on incoming packets
+245    pub(super) ecn_counters: frame::EcnCounts,
+246    /// Recent ECN counters sent by the peer in ACK frames
+247    ///
+248    /// Updated (and inspected) whenever we receive an ACK with a new highest acked packet
+249    /// number. Stored per-space to simplify verification, which would otherwise have difficulty
+250    /// distinguishing between ECN bleaching and counts having been updated by a near-simultaneous
+251    /// ACK already processed in another space.
+252    pub(super) ecn_feedback: frame::EcnCounts,
+253    /// A PING frame needs to be sent on this path
+254    pub(super) ping_pending: bool,
+255    /// An IMMEDIATE_ACK (draft-ietf-quic-ack-frequency) frame needs to be sent on this path
+256    pub(super) immediate_ack_pending: bool,
+257    /// Packet deduplicator
+258    pub(super) dedup: Dedup,
+259    /// Packet numbers to acknowledge
+260    pub(super) pending_acks: PendingAcks,
+261
+262    //
+263    // Loss Detection
+264    //
+265    /// The time the most recently sent retransmittable packet was sent.
+266    pub(super) time_of_last_ack_eliciting_packet: Option<Instant>,
+267    /// Earliest time when we might declare a packet lost.
+268    ///
+269    /// The time at which the earliest sent packet in this space will be considered lost
+270    /// based on exceeding the reordering window in time. Only set for packets numbered
+271    /// prior to a packet that has been acknowledged.
+272    pub(super) loss_time: Option<Instant>,
+273    /// Number of tail loss probes to send
+274    pub(super) loss_probes: u32,
+275
+276    /// Packet numbers to skip, only used in the data package space.
+277    pn_filter: Option<PacketNumberFilter>,
+278}
+279
+280impl PacketNumberSpace {
+281    pub(super) fn new(now: Instant, space: SpaceId, rng: &mut (impl CryptoRng + ?Sized)) -> Self {
+282        let pn_filter = match space {
+283            SpaceId::Initial | SpaceId::Handshake => None,
+284            SpaceId::Data => Some(PacketNumberFilter::new(rng)),
+285        };
+286        Self {
+287            rx_packet: None,
+288            next_packet_number: 0,
+289            largest_acked_packet: None,
+290            largest_acked_packet_sent: now,
+291            largest_ack_eliciting_sent: 0,
+292            unacked_non_ack_eliciting_tail: 0,
+293            sent_packets: SortedIndexBuffer::new(),
+294            lost_packets: SortedIndexBuffer::new(),
+295            ecn_counters: frame::EcnCounts::ZERO,
+296            ecn_feedback: frame::EcnCounts::ZERO,
+297            ping_pending: false,
+298            immediate_ack_pending: false,
+299            dedup: Default::default(),
+300            pending_acks: PendingAcks::new(),
+301            time_of_last_ack_eliciting_packet: None,
+302            loss_time: None,
+303            loss_probes: 0,
+304            pn_filter,
+305        }
+306    }
+307
+308    #[cfg(test)]
+309    fn new_deterministic(now: Instant, space: SpaceId) -> Self {
+310        let pn_filter = match space {
+311            SpaceId::Initial | SpaceId::Handshake => None,
+312            SpaceId::Data => Some(PacketNumberFilter::disabled()),
+313        };
+314        Self {
+315            rx_packet: None,
+316            next_packet_number: 0,
+317            largest_acked_packet: None,
+318            largest_acked_packet_sent: now,
+319            largest_ack_eliciting_sent: 0,
+320            unacked_non_ack_eliciting_tail: 0,
+321            sent_packets: SortedIndexBuffer::new(),
+322            lost_packets: SortedIndexBuffer::new(),
+323            ecn_counters: frame::EcnCounts::ZERO,
+324            ecn_feedback: frame::EcnCounts::ZERO,
+325            ping_pending: false,
+326            immediate_ack_pending: false,
+327            dedup: Default::default(),
+328            pending_acks: PendingAcks::new(),
+329            time_of_last_ack_eliciting_packet: None,
+330            loss_time: None,
+331            loss_probes: 0,
+332            pn_filter,
+333        }
+334    }
+335
+336    /// Get the next outgoing packet number in this space
+337    ///
+338    /// In the Data space, the connection's [`PacketNumberFilter`] must be used rather than calling
+339    /// this directly.
+340    pub(super) fn get_tx_number(&mut self, rng: &mut (impl CryptoRng + ?Sized)) -> u64 {
+341        // TODO: Handle packet number overflow gracefully
+342        assert!(self.next_packet_number < 2u64.pow(62));
+343        let mut pn = self.next_packet_number;
+344        self.next_packet_number += 1;
+345
+346        // Skip this number if the filter says so, only enabled in the data space
+347        if let Some(ref mut filter) = self.pn_filter
+348            && filter.skip_pn(pn, rng)
+349        {
+350            pn = self.next_packet_number;
+351            self.next_packet_number += 1;
+352        }
+353        pn
+354    }
+355
+356    pub(super) fn peek_tx_number(&mut self) -> u64 {
+357        let pn = self.next_packet_number;
+358        if let Some(ref filter) = self.pn_filter
+359            && pn == filter.next_skipped_packet_number
+360        {
+361            return pn + 1;
+362        }
+363        pn
+364    }
+365
+366    /// Checks whether a skipped packet number was ACKed.
+367    pub(super) fn check_ack(&self, range: std::ops::Range<u64>) -> Result<(), TransportError> {
+368        if let Some(ref filter) = self.pn_filter
+369            && filter
+370                .prev_skipped_packet_number
+371                .is_some_and(|pn| range.contains(&pn))
+372        {
+373            return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked"));
+374        }
+375        Ok(())
+376    }
+377
+378    /// Verifies sanity of an ECN block and returns whether congestion was encountered.
+379    pub(super) fn detect_ecn(
+380        &mut self,
+381        newly_acked: u64,
+382        ecn: frame::EcnCounts,
+383    ) -> Result<bool, &'static str> {
+384        let ect0_increase = ecn
+385            .ect0
+386            .checked_sub(self.ecn_feedback.ect0)
+387            .ok_or("peer ECT(0) count regression")?;
+388        let ect1_increase = ecn
+389            .ect1
+390            .checked_sub(self.ecn_feedback.ect1)
+391            .ok_or("peer ECT(1) count regression")?;
+392        let ce_increase = ecn
+393            .ce
+394            .checked_sub(self.ecn_feedback.ce)
+395            .ok_or("peer CE count regression")?;
+396        let total_increase = ect0_increase + ect1_increase + ce_increase;
+397        if total_increase < newly_acked {
+398            return Err("ECN bleaching");
+399        }
+400        if (ect0_increase + ce_increase) < newly_acked || ect1_increase != 0 {
+401            return Err("ECN corruption");
+402        }
+403        // If total_increase > newly_acked (which happens when ACKs are lost), this is required by
+404        // the draft so that long-term drift does not occur. If =, then the only question is whether
+405        // to count CE packets as CE or ECT0. Recording them as CE is more consistent and keeps the
+406        // congestion check obvious.
+407        self.ecn_feedback = ecn;
+408        Ok(ce_increase != 0)
+409    }
+410
+411    /// Stop tracking sent packet `number`, and return what we knew about it
+412    pub(super) fn take(&mut self, number: u64) -> Option<SentPacket> {
+413        let packet = self.sent_packets.remove(number)?;
+414        if !packet.ack_eliciting && number > self.largest_ack_eliciting_sent {
+415            self.unacked_non_ack_eliciting_tail =
+416                self.unacked_non_ack_eliciting_tail.checked_sub(1).unwrap();
+417        }
+418        Some(packet)
+419    }
+420
+421    /// May return a packet that should be forgotten
+422    pub(super) fn sent(&mut self, number: u64, packet: SentPacket) -> Option<SentPacket> {
+423        // Retain state for at most this many non-ACK-eliciting packets sent after the most recently
+424        // sent ACK-eliciting packet. We're never guaranteed to receive an ACK for those, and we
+425        // can't judge them as lost without an ACK, so to limit memory in applications which receive
+426        // packets but don't send ACK-eliciting data for long periods use we must eventually start
+427        // forgetting about them, although it might also be reasonable to just kill the connection
+428        // due to weird peer behavior.
+429        const MAX_UNACKED_NON_ACK_ELICTING_TAIL: u64 = 1_000;
+430
+431        let mut forgotten = None;
+432        if packet.ack_eliciting {
+433            self.unacked_non_ack_eliciting_tail = 0;
+434            self.largest_ack_eliciting_sent = number;
+435        } else if self.unacked_non_ack_eliciting_tail > MAX_UNACKED_NON_ACK_ELICTING_TAIL {
+436            let oldest_after_ack_eliciting = self
+437                .sent_packets
+438                .keys_range((
+439                    Bound::Excluded(self.largest_ack_eliciting_sent),
+440                    Bound::Unbounded,
+441                ))
+442                .next()
+443                .unwrap();
+444            // Per https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types,
+445            // non-ACK-eliciting packets must only contain PADDING, ACK, and CONNECTION_CLOSE
+446            // frames, which require no special handling on ACK or loss beyond removal from
+447            // in-flight counters if padded.
+448            let packet = self
+449                .sent_packets
+450                .remove(oldest_after_ack_eliciting)
+451                .unwrap();
+452            debug_assert!(!packet.ack_eliciting);
+453            forgotten = Some(packet);
+454        } else {
+455            self.unacked_non_ack_eliciting_tail += 1;
+456        }
+457
+458        self.sent_packets.insert(number, packet);
+459        forgotten
+460    }
+461
+462    /// Whether any congestion-controlled packets in this space are not yet acknowledged or lost
+463    pub(super) fn has_in_flight(&self) -> bool {
+464        // The number of non-congestion-controlled (i.e. size == 0) packets in flight at a time
+465        // should be small, since otherwise congestion control wouldn't be effective. Therefore,
+466        // this shouldn't need to visit many packets before finishing one way or another.
+467        self.sent_packets.values().any(|x| x.size != 0)
+468    }
+469}
+470
+471/// Represents one or more packets subject to retransmission
+472#[derive(Debug, Clone)]
+473pub(super) struct SentPacket {
+474    /// [`PathData::generation`](super::PathData::generation) of the path on which this packet was sent
+475    pub(super) path_generation: u64,
+476    /// The time the packet was sent.
+477    pub(super) time_sent: Instant,
+478    /// The number of bytes sent in the packet, not including UDP or IP overhead, but including QUIC
+479    /// framing overhead. Zero if this packet is not counted towards congestion control, i.e. not an
+480    /// "in flight" packet.
+481    pub(super) size: u16,
+482    /// Whether an acknowledgement is expected directly in response to this packet.
+483    pub(super) ack_eliciting: bool,
+484    /// The largest packet number acknowledged by this packet
+485    pub(super) largest_acked: FxHashMap<PathId, u64>,
+486    /// Data which needs to be retransmitted in case the packet is lost.
+487    /// The data is boxed to minimize `SentPacket` size for the typical case of
+488    /// packets only containing ACKs and STREAM frames.
+489    pub(super) retransmits: ThinRetransmits,
+490    /// Metadata for stream frames in a packet
+491    ///
+492    /// The actual application data is stored with the stream state.
+493    pub(super) stream_frames: frame::StreamMetaVec,
+494}
+495
+496/// Represents one or more packets that are deemed lost.
+497#[derive(Debug)]
+498pub(super) struct LostPacket {
+499    /// The time the packet was sent.
+500    pub(super) time_sent: Instant,
+501}
+502
+503/// Retransmittable data queue
+504#[allow(unreachable_pub)] // fuzzing only
+505#[derive(Debug, Default, Clone)]
+506pub struct Retransmits {
+507    pub(super) max_data: bool,
+508    pub(super) max_stream_id: [bool; 2],
+509    pub(super) reset_stream: Vec<(StreamId, VarInt)>,
+510    pub(super) stop_sending: Vec<frame::StopSending>,
+511    pub(super) max_stream_data: FxHashSet<StreamId>,
+512    pub(super) crypto: VecDeque<frame::Crypto>,
+513    pub(super) new_cids: PendingNewCids,
+514    pub(super) retire_cids: Vec<(PathId, u64)>,
+515    pub(super) ack_frequency: bool,
+516    pub(super) handshake_done: bool,
+517    pub(super) observed_addr: bool,
+518    /// Whether we should inform the peer we will allow higher [`PathId`]s.
+519    pub(super) max_path_id: bool,
+520    /// Whether we should inform the peer that their max [`PathId`] is blocking our attempt to open
+521    /// new paths.
+522    // TODO(@divma): we need logic to prevent sending this more than once after being ack-d once
+523    pub(super) paths_blocked: bool,
+524    /// For each enqueued NEW_TOKEN frame, a copy of the path's remote address
+525    ///
+526    /// There are 2 reasons this is unusual:
+527    ///
+528    /// - If the path changes, NEW_TOKEN frames bound for the old path are not retransmitted on the
+529    ///   new path. That is why this field stores the remote address: so that ones for old paths
+530    ///   can be filtered out.
+531    /// - If a token is lost, a new randomly generated token is re-transmitted, rather than the
+532    ///   original. This is so that if both transmissions are received, the client won't risk
+533    ///   sending the same token twice. That is why this field does _not_ store any actual token.
+534    ///
+535    /// It is true that a QUIC endpoint will only want to effectively have NEW_TOKEN frames
+536    /// enqueued for its current path at a given point in time. Based on that, we could conceivably
+537    /// change this from a vector to an `Option<(FourTuple, usize)>` or just a `usize` or
+538    /// something. However, due to the architecture of noq, it is considerably simpler to not do
+539    /// that; consider what such a change would mean for implementing `BitOrAssign` on Self.
+540    pub(super) new_tokens: Vec<FourTuple>,
+541    /// Paths which need to be abandoned
+542    pub(super) path_abandon: BTreeMap<PathId, TransportErrorCode>,
+543    /// If a [`frame::PathStatusAvailable`] and [`frame::PathStatusBackup`] need to be sent for a path
+544    pub(super) path_status: BTreeSet<PathId>,
+545    /// If a PATH_CIDS_BLOCKED frame needs to be sent for a path
+546    pub(super) path_cids_blocked: BTreeSet<PathId>,
+547
+548    // Nat traversal data
+549    /// Addresses to report in `ADD_ADDRESS` frames
+550    pub(super) add_address: BTreeSet<AddAddress>,
+551    /// Address IDs to remove in `REMOVE_ADDRESS` frames
+552    pub(super) remove_address: BTreeSet<RemoveAddress>,
+553    /// Round and local addresses to advertise in `REACH_OUT` frames
+554    pub(super) reach_out: Option<(VarInt, FxHashSet<(IpAddr, u16)>)>,
+555}
+556
+557impl Retransmits {
+558    pub(super) fn is_empty(&self, streams: &StreamsState) -> bool {
+559        let Self {
+560            max_data,
+561            max_stream_id,
+562            reset_stream,
+563            stop_sending,
+564            max_stream_data,
+565            crypto,
+566            new_cids,
+567            retire_cids,
+568            ack_frequency,
+569            handshake_done,
+570            observed_addr,
+571            max_path_id,
+572            paths_blocked,
+573            new_tokens,
+574            path_abandon,
+575            path_status,
+576            path_cids_blocked,
+577            add_address,
+578            remove_address,
+579            reach_out,
+580        } = &self;
+581        !max_data
+582            && !max_stream_id.iter().any(|x| *x)
+583            && reset_stream.is_empty()
+584            && stop_sending.is_empty()
+585            && max_stream_data
+586                .iter()
+587                .all(|&id| !streams.can_send_flow_control(id))
+588            && crypto.is_empty()
+589            && new_cids.is_empty()
+590            && retire_cids.is_empty()
+591            && !ack_frequency
+592            && !handshake_done
+593            && !observed_addr
+594            && !max_path_id
+595            && !paths_blocked
+596            && new_tokens.is_empty()
+597            && path_abandon.is_empty()
+598            && path_status.is_empty()
+599            && path_cids_blocked.is_empty()
+600            && add_address.is_empty()
+601            && remove_address.is_empty()
+602            && reach_out.is_none()
+603    }
+604}
+605
+606impl ::std::ops::BitOrAssign for Retransmits {
+607    fn bitor_assign(&mut self, rhs: Self) {
+608        let Self {
+609            max_data,
+610            max_stream_id,
+611            reset_stream,
+612            stop_sending,
+613            max_stream_data,
+614            crypto,
+615            new_cids,
+616            retire_cids,
+617            ack_frequency,
+618            handshake_done,
+619            observed_addr,
+620            max_path_id,
+621            paths_blocked,
+622            new_tokens,
+623            mut path_abandon,
+624            mut path_status,
+625            mut path_cids_blocked,
+626            add_address,
+627            remove_address,
+628            reach_out,
+629        } = rhs;
+630
+631        // We reduce in-stream head-of-line blocking by queueing retransmits before other data for
+632        // STREAM and CRYPTO frames.
+633        self.max_data |= max_data;
+634        for dir in Dir::iter() {
+635            self.max_stream_id[dir as usize] |= max_stream_id[dir as usize];
+636        }
+637        self.reset_stream.extend_from_slice(&reset_stream);
+638        self.stop_sending.extend_from_slice(&stop_sending);
+639        self.max_stream_data.extend(&max_stream_data);
+640        for crypto in crypto.into_iter().rev() {
+641            self.crypto.push_front(crypto);
+642        }
+643        self.new_cids.extend(&new_cids);
+644        self.retire_cids.extend(retire_cids);
+645        self.ack_frequency |= ack_frequency;
+646        self.handshake_done |= handshake_done;
+647        self.observed_addr |= observed_addr;
+648        self.max_path_id |= max_path_id;
+649        self.paths_blocked |= paths_blocked;
+650        self.new_tokens.extend_from_slice(&new_tokens);
+651        self.path_abandon.append(&mut path_abandon);
+652        self.path_status.append(&mut path_status);
+653        self.path_cids_blocked.append(&mut path_cids_blocked);
+654        self.add_address.extend(add_address.iter().copied());
+655        self.remove_address.extend(remove_address.iter().copied());
+656        if let Some((rhs_round, rhs_addrs)) = reach_out {
+657            match self.reach_out.as_mut() {
+658                // Use RHS if there is no recorded round.
+659                None => self.reach_out = Some((rhs_round, rhs_addrs)),
+660                // Use RHS if newer.
+661                Some((lhs_round, _lhs_addrs)) if rhs_round > *lhs_round => {
+662                    self.reach_out = Some((rhs_round, rhs_addrs));
+663                }
+664                // If both rounds are the same, merge them.
+665                Some((lhs_round, lhs_addrs)) if rhs_round == *lhs_round => {
+666                    lhs_addrs.extend(rhs_addrs);
+667                }
+668                // LHS round is newer, ignore RHS
+669                Some(_) => {}
+670            }
+671        }
+672    }
+673}
+674
+675impl ::std::ops::BitOrAssign<ThinRetransmits> for Retransmits {
+676    fn bitor_assign(&mut self, rhs: ThinRetransmits) {
+677        let ThinRetransmits { retransmits } = rhs;
+678        if let Some(retransmits) = retransmits {
+679            self.bitor_assign(*retransmits)
+680        }
+681    }
+682}
+683
+684impl ::std::iter::FromIterator<Self> for Retransmits {
+685    fn from_iter<T>(iter: T) -> Self
+686    where
+687        T: IntoIterator<Item = Self>,
+688    {
+689        let mut result = Self::default();
+690        for packet in iter {
+691            result |= packet;
+692        }
+693        result
+694    }
+695}
+696
+697/// The queue of new CIDs to be transmitted to the peer.
+698///
+699/// This queue is always sorted, so that popping off the last item is always the lowest
+700/// sequence number of the lowest path ID. Which is the CID you want to be issued next.
+701///
+702/// This is but a newtype over a `Vec` to enforce the sorted invariant.
+703#[derive(Clone, Debug, Default)]
+704pub(super) struct PendingNewCids {
+705    /// The CIDs themselves.
+706    cids: Vec<IssuedCid>,
+707    /// Whether [`Self::cids`] is sorted or not.
+708    sorted: bool,
+709}
+710
+711impl PendingNewCids {
+712    /// Inserts an issued CID into the queue.
+713    pub(super) fn push(&mut self, cid: IssuedCid) {
+714        self.cids.push(cid);
+715        self.sorted = false;
+716    }
+717
+718    /// Pops the next issued CID to transmit from the queue.
+719    pub(super) fn pop(&mut self) -> Option<IssuedCid> {
+720        if !std::mem::replace(&mut self.sorted, true) {
+721            self.cids
+722                .sort_by_key(|cid| cmp::Reverse((cid.path_id, cid.sequence)));
+723        }
+724        self.cids.pop()
+725    }
+726
+727    pub(super) fn is_empty(&self) -> bool {
+728        self.cids.is_empty()
+729    }
+730
+731    pub(super) fn extend(&mut self, other: &Self) {
+732        self.cids.extend(&other.cids);
+733        self.sorted = false;
+734    }
+735
+736    pub(super) fn retain<F>(&mut self, f: F)
+737    where
+738        F: FnMut(&IssuedCid) -> bool,
+739    {
+740        self.cids.retain(f);
+741    }
+742}
+743
+744/// A variant of `Retransmits` which only allocates storage when required
+745#[derive(Debug, Default, Clone)]
+746pub(super) struct ThinRetransmits {
+747    retransmits: Option<Box<Retransmits>>,
+748}
+749
+750impl ThinRetransmits {
+751    /// Returns `true` if no retransmits are necessary
+752    pub(super) fn is_empty(&self, streams: &StreamsState) -> bool {
+753        match &self.retransmits {
+754            Some(retransmits) => retransmits.is_empty(streams),
+755            None => true,
+756        }
+757    }
+758
+759    /// Returns a reference to the retransmits stored in this box
+760    pub(super) fn get(&self) -> Option<&Retransmits> {
+761        self.retransmits.as_deref()
+762    }
+763
+764    /// Returns a mutable reference to the retransmits stored in this box
+765    pub(super) fn get_mut(&mut self) -> Option<&mut Retransmits> {
+766        self.retransmits.as_deref_mut()
+767    }
+768
+769    /// Returns a mutable reference to the stored retransmits
+770    ///
+771    /// This function will allocate a backing storage if required.
+772    pub(super) fn get_or_create(&mut self) -> &mut Retransmits {
+773        if self.retransmits.is_none() {
+774            self.retransmits = Some(Box::default());
+775        }
+776        self.retransmits.as_deref_mut().unwrap()
+777    }
+778}
+779
+780/// RFC4303-style sliding window packet number deduplicator.
+781///
+782/// A contiguous bitfield, where each bit corresponds to a packet number and the rightmost bit is
+783/// always set. A set bit represents a packet that has been successfully authenticated. Bits left of
+784/// the window are assumed to be set.
+785///
+786/// ```text
+787/// ...xxxxxxxxx 1 0
+788///     ^        ^ ^
+789/// window highest next
+790/// ```
+791#[derive(Debug, Default)]
+792pub(super) struct Dedup {
+793    window: Window,
+794    /// Lowest packet number higher than all yet authenticated.
+795    next: u64,
+796}
+797
+798/// Inner bitfield type.
+799///
+800/// Because QUIC never reuses packet numbers, this only needs to be large enough to deal with
+801/// packets that are reordered but still delivered in a timely manner.
+802type Window = u128;
+803
+804/// Number of packets tracked by `Dedup`.
+805const WINDOW_SIZE: u64 = 1 + mem::size_of::<Window>() as u64 * 8;
+806
+807impl Dedup {
+808    /// Construct an empty window positioned at the start.
+809    #[cfg(test)]
+810    pub(super) fn new() -> Self {
+811        Self { window: 0, next: 0 }
+812    }
+813
+814    /// Highest packet number authenticated.
+815    fn highest(&self) -> u64 {
+816        self.next - 1
+817    }
+818
+819    /// Record a newly authenticated packet number.
+820    ///
+821    /// Returns whether the packet might be a duplicate.
+822    pub(super) fn insert(&mut self, packet: u64) -> bool {
+823        if let Some(diff) = packet.checked_sub(self.next) {
+824            // Right of window
+825            self.window = ((self.window << 1) | 1)
+826                .checked_shl(cmp::min(diff, u64::from(u32::MAX)) as u32)
+827                .unwrap_or(0);
+828            self.next = packet + 1;
+829            false
+830        } else if self.highest() - packet < WINDOW_SIZE {
+831            // Within window
+832            if let Some(bit) = (self.highest() - packet).checked_sub(1) {
+833                // < highest
+834                let mask = 1 << bit;
+835                let duplicate = self.window & mask != 0;
+836                self.window |= mask;
+837                duplicate
+838            } else {
+839                // == highest
+840                true
+841            }
+842        } else {
+843            // Left of window
+844            true
+845        }
+846    }
+847
+848    /// Returns the packet number of the smallest packet missing between the provided interval
+849    ///
+850    /// If there are no missing packets, returns `None`
+851    fn smallest_missing_in_interval(&self, lower_bound: u64, upper_bound: u64) -> Option<u64> {
+852        debug_assert!(lower_bound <= upper_bound);
+853        debug_assert!(upper_bound <= self.highest());
+854        const BITFIELD_SIZE: u64 = (mem::size_of::<Window>() * 8) as u64;
+855
+856        // Since we already know the packets at the boundaries have been received, we only need to
+857        // check those in between them (this removes the necessity of extra logic to deal with the
+858        // highest packet, which is stored outside the bitfield)
+859        let lower_bound = lower_bound + 1;
+860        let upper_bound = upper_bound.saturating_sub(1);
+861
+862        // Note: the offsets are counted from the right
+863        // The highest packet is not included in the bitfield, so we subtract 1 to account for that
+864        let start_offset = (self.highest() - upper_bound).max(1) - 1;
+865        if start_offset >= BITFIELD_SIZE {
+866            // The start offset is outside of the window. All packets outside of the window are
+867            // considered to be received.
+868            return None;
+869        }
+870
+871        let end_offset_exclusive = self.highest().saturating_sub(lower_bound);
+872
+873        // The range is clamped at the edge of the window, because any earlier packets are
+874        // considered to be received
+875        let range_len = end_offset_exclusive
+876            .saturating_sub(start_offset)
+877            .min(BITFIELD_SIZE);
+878        if range_len == 0 {
+879            return None;
+880        }
+881
+882        // Ensure the shift is within bounds (we already know start_offset < BITFIELD_SIZE,
+883        // because of the early return)
+884        let mask = if range_len == BITFIELD_SIZE {
+885            u128::MAX
+886        } else {
+887            ((1u128 << range_len) - 1) << start_offset
+888        };
+889        let gaps = !self.window & mask;
+890
+891        let smallest_missing_offset = 128 - gaps.leading_zeros() as u64;
+892        let smallest_missing_packet = self.highest() - smallest_missing_offset;
+893
+894        if smallest_missing_packet <= upper_bound {
+895            Some(smallest_missing_packet)
+896        } else {
+897            None
+898        }
+899    }
+900
+901    /// Returns true if there are any missing packets between the provided interval
+902    ///
+903    /// The provided packet numbers must have been received before calling this function
+904    fn missing_in_interval(&self, lower_bound: u64, upper_bound: u64) -> bool {
+905        self.smallest_missing_in_interval(lower_bound, upper_bound)
+906            .is_some()
+907    }
+908}
+909
+910/// Indicates which data is available for sending.
+911///
+912/// This applies to a particular space ID that was queried and all refers to on-path data.
+913#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+914pub(super) struct SendableFrames {
+915    /// Whether there are ACK frames to send, these are not ack-eliciting.
+916    pub(super) acks: bool,
+917    /// Whether there is a CONNECTION_CLOSE to send, this is not ack-eliciting.
+918    pub(super) close: bool,
+919    /// Whether there are any frames that must be sent on this specific space.
+920    ///
+921    /// A space here in the sense of a QUIC Multipath packet number space: `Initial`,
+922    /// `Handshake` and all `Data(PathId)` spaces.
+923    ///
+924    /// These are ack-eliciting. Some frames are scheduled per path, e.g. PING,
+925    /// IMMEDIATE_ACK, PATH_CHALLENGE or PATH_RESPONSE.
+926    pub(super) space_only: bool,
+927    /// Whether there are any other frames to send, these are ack-eliciting.
+928    pub(super) other: bool,
+929}
+930
+931impl SendableFrames {
+932    /// Returns that no data is available for sending
+933    pub(super) fn empty() -> Self {
+934        Self {
+935            acks: false,
+936            close: false,
+937            space_only: false,
+938            other: false,
+939        }
+940    }
+941
+942    /// Whether an ack-eliciting packet will be sent.
+943    pub(super) fn is_ack_eliciting(&self) -> bool {
+944        let Self {
+945            acks: _,
+946            close,
+947            space_only,
+948            other,
+949        } = *self;
+950        if close {
+951            // No ack-eliciting frames are included with a CONNECTION_CLOSE, only acks.
+952            return false;
+953        }
+954        space_only || other
+955    }
+956
+957    /// Whether no data is sendable.
+958    pub(super) fn is_empty(&self) -> bool {
+959        let Self {
+960            acks,
+961            close,
+962            space_only,
+963            other,
+964        } = *self;
+965        !acks && !close && !space_only && !other
+966    }
+967}
+968
+969impl ::std::ops::BitOrAssign for SendableFrames {
+970    fn bitor_assign(&mut self, rhs: Self) {
+971        let Self {
+972            acks,
+973            close,
+974            space_only,
+975            other,
+976        } = rhs;
+977
+978        self.acks |= acks;
+979        self.close |= close;
+980        self.space_only |= space_only;
+981        self.other |= other;
+982    }
+983}
+984
+985#[derive(Debug)]
+986pub(super) struct PendingAcks {
+987    /// Whether we should send an ACK immediately, even if that means sending an ACK-only packet
+988    ///
+989    /// When `immediate_ack_required` is false, the normal behavior is to send ACK frames only when
+990    /// there is other data to send, or when the `MaxAckDelay` timer expires.
+991    immediate_ack_required: bool,
+992    /// The number of ack-eliciting packets received since the last ACK frame was sent
+993    ///
+994    /// Once the count _exceeds_ `ack_eliciting_threshold`, an immediate ACK is required
+995    ack_eliciting_since_last_ack_sent: u64,
+996    non_ack_eliciting_since_last_ack_sent: u64,
+997    ack_eliciting_threshold: u64,
+998    /// The reordering threshold, controlling how we respond to out-of-order ack-eliciting packets
+999    ///
+1000    /// Different values enable different behavior:
+1001    ///
+1002    /// * `0`: no special action is taken
+1003    /// * `1`: an ACK is immediately sent if it is out-of-order according to RFC 9000
+1004    /// * `>1`: an ACK is immediately sent if it is out-of-order according to the ACK frequency draft
+1005    reordering_threshold: u64,
+1006    /// The earliest ack-eliciting packet since the last ACK was sent, used to calculate the moment
+1007    /// upon which `max_ack_delay` elapses
+1008    earliest_ack_eliciting_since_last_ack_sent: Option<Instant>,
+1009    /// Packet number ranges for which to still send acknowledgements.
+1010    ///
+1011    /// These are packet number ranges of ack-eliciting packets the peer has sent and which
+1012    /// need to be acknowledged.  Packet numbers are only removed from here once the peer has
+1013    /// acknowledged the ACKs for them.
+1014    ranges: ArrayRangeSet,
+1015    /// The largest packet number received and the time it was received
+1016    ///
+1017    /// Used to calculate ACK delay in [`PendingAcks::ack_delay`].
+1018    largest_packet: Option<(u64, Instant)>,
+1019    /// The ack-eliciting packet we have received with the largest packet number
+1020    largest_ack_eliciting_packet: Option<u64>,
+1021    /// The largest acknowledged packet number sent in an ACK frame
+1022    largest_acked: Option<u64>,
+1023}
+1024
+1025impl PendingAcks {
+1026    fn new() -> Self {
+1027        Self {
+1028            immediate_ack_required: false,
+1029            ack_eliciting_since_last_ack_sent: 0,
+1030            non_ack_eliciting_since_last_ack_sent: 0,
+1031            ack_eliciting_threshold: 1,
+1032            reordering_threshold: 1,
+1033            earliest_ack_eliciting_since_last_ack_sent: None,
+1034            ranges: Default::default(),
+1035            largest_packet: Default::default(),
+1036            largest_ack_eliciting_packet: Default::default(),
+1037            largest_acked: Default::default(),
+1038        }
+1039    }
+1040
+1041    pub(super) fn set_ack_frequency_params(&mut self, frame: &frame::AckFrequency) {
+1042        self.ack_eliciting_threshold = frame.ack_eliciting_threshold.into_inner();
+1043        self.reordering_threshold = frame.reordering_threshold.into_inner();
+1044    }
+1045
+1046    pub(super) fn set_immediate_ack_required(&mut self) {
+1047        self.immediate_ack_required = true;
+1048    }
+1049
+1050    pub(super) fn on_max_ack_delay_timeout(&mut self) {
+1051        self.immediate_ack_required = self.ack_eliciting_since_last_ack_sent > 0;
+1052    }
+1053
+1054    pub(super) fn max_ack_delay_timeout(&self, max_ack_delay: Duration) -> Option<Instant> {
+1055        self.earliest_ack_eliciting_since_last_ack_sent
+1056            .map(|earliest_unacked| earliest_unacked + max_ack_delay)
+1057    }
+1058
+1059    /// Whether any ACK frames SHOULD be sent
+1060    ///
+1061    /// This is used in the top-level [`Connection::space_can_send`], so determines if a
+1062    /// packet will be built. It is often possible to construct new ACK ranges to send
+1063    /// before this returns `true`. This results in more ACK frames being sent, and
+1064    /// processing those at the receiver costs CPU for very little improvements.
+1065    ///
+1066    /// [`Connection::space_can_send`]: super::Connection::space_can_send
+1067    pub(super) fn can_send(&self) -> bool {
+1068        self.immediate_ack_required && !self.ranges.is_empty()
+1069    }
+1070
+1071    /// Returns the delay since the packet with the largest packet number was received
+1072    pub(super) fn ack_delay(&self, now: Instant) -> Duration {
+1073        self.largest_packet
+1074            .map_or_else(Duration::default, |(_, received)| now - received)
+1075    }
+1076
+1077    /// Handle receipt of a new packet
+1078    ///
+1079    /// Returns true if the max ack delay timer should be armed
+1080    pub(super) fn packet_received(
+1081        &mut self,
+1082        now: Instant,
+1083        packet_number: u64,
+1084        ack_eliciting: bool,
+1085        dedup: &Dedup,
+1086    ) -> bool {
+1087        if !ack_eliciting {
+1088            self.non_ack_eliciting_since_last_ack_sent += 1;
+1089            return false;
+1090        }
+1091
+1092        let prev_largest_ack_eliciting = self.largest_ack_eliciting_packet.unwrap_or(0);
+1093
+1094        // Track largest ack-eliciting packet
+1095        self.largest_ack_eliciting_packet = self
+1096            .largest_ack_eliciting_packet
+1097            .map(|pn| pn.max(packet_number))
+1098            .or(Some(packet_number));
+1099
+1100        // Handle ack_eliciting_threshold
+1101        self.ack_eliciting_since_last_ack_sent += 1;
+1102        self.immediate_ack_required |=
+1103            self.ack_eliciting_since_last_ack_sent > self.ack_eliciting_threshold;
+1104
+1105        // Handle out-of-order packets
+1106        self.immediate_ack_required |=
+1107            self.is_out_of_order(packet_number, prev_largest_ack_eliciting, dedup);
+1108
+1109        // Arm max_ack_delay timer if necessary
+1110        if self.earliest_ack_eliciting_since_last_ack_sent.is_none() && !self.can_send() {
+1111            self.earliest_ack_eliciting_since_last_ack_sent = Some(now);
+1112            return true;
+1113        }
+1114
+1115        false
+1116    }
+1117
+1118    fn is_out_of_order(
+1119        &self,
+1120        packet_number: u64,
+1121        prev_largest_ack_eliciting: u64,
+1122        dedup: &Dedup,
+1123    ) -> bool {
+1124        match self.reordering_threshold {
+1125            0 => false,
+1126            1 => {
+1127                // From https://www.rfc-editor.org/rfc/rfc9000#section-13.2.1-7
+1128                packet_number < prev_largest_ack_eliciting
+1129                    || dedup.missing_in_interval(prev_largest_ack_eliciting, packet_number)
+1130            }
+1131            _ => {
+1132                // From acknowledgement frequency draft, section 6.1: send an ACK immediately if
+1133                // doing so would cause the sender to detect a new packet loss
+1134                let Some((largest_acked, largest_unacked)) =
+1135                    self.largest_acked.zip(self.largest_ack_eliciting_packet)
+1136                else {
+1137                    return false;
+1138                };
+1139                if self.reordering_threshold > largest_acked {
+1140                    return false;
+1141                }
+1142                // The largest packet number that could be declared lost without a new ACK being
+1143                // sent
+1144                let largest_reported = largest_acked - self.reordering_threshold + 1;
+1145                let Some(smallest_missing_unreported) =
+1146                    dedup.smallest_missing_in_interval(largest_reported, largest_unacked)
+1147                else {
+1148                    return false;
+1149                };
+1150                largest_unacked - smallest_missing_unreported >= self.reordering_threshold
+1151            }
+1152        }
+1153    }
+1154
+1155    /// Should be called whenever ACKs have been sent
+1156    ///
+1157    /// This will suppress sending further ACKs until additional ACK eliciting frames arrive
+1158    pub(super) fn acks_sent(&mut self) {
+1159        // It is possible (though unlikely) that the ACKs we just sent do not cover all the
+1160        // ACK-eliciting packets we have received (e.g. if there is not enough room in the packet to
+1161        // fit all the ranges). To keep things simple, however, we assume they do. If there are
+1162        // indeed some ACKs that weren't covered, the packets might be ACKed later anyway, because
+1163        // they are still contained in `self.ranges`. If we somehow fail to send the ACKs at a later
+1164        // moment, the peer will assume the packets got lost and will retransmit their frames in a
+1165        // new packet, which is suboptimal, because we already received them. Our assumption here is
+1166        // that simplicity results in code that is more performant, even in the presence of
+1167        // occasional redundant retransmits.
+1168        self.immediate_ack_required = false;
+1169        self.ack_eliciting_since_last_ack_sent = 0;
+1170        self.non_ack_eliciting_since_last_ack_sent = 0;
+1171        self.earliest_ack_eliciting_since_last_ack_sent = None;
+1172        self.largest_acked = self.largest_ack_eliciting_packet;
+1173    }
+1174
+1175    /// Insert one packet that needs to be acknowledged
+1176    pub(super) fn insert_one(&mut self, packet: u64, now: Instant) {
+1177        self.ranges.insert_one(packet);
+1178
+1179        if self.largest_packet.is_none_or(|(pn, _)| packet > pn) {
+1180            self.largest_packet = Some((packet, now));
+1181        }
+1182
+1183        if self.ranges.len() > MAX_ACK_BLOCKS {
+1184            self.ranges.pop_min();
+1185        }
+1186    }
+1187
+1188    /// Remove ACKs of packets numbered at or below `max` from the set of pending ACKs
+1189    pub(super) fn subtract_below(&mut self, max: u64) {
+1190        self.ranges.remove(0..(max + 1));
+1191    }
+1192
+1193    /// Returns the set of currently pending ACK ranges
+1194    pub(super) fn ranges(&self) -> &ArrayRangeSet {
+1195        &self.ranges
+1196    }
+1197
+1198    /// Queue an ACK if a significant number of non-ACK-eliciting packets have not yet been
+1199    /// acknowledged
+1200    ///
+1201    /// Should be called immediately before a non-probing packet is composed, when we've already
+1202    /// committed to sending a packet regardless.
+1203    pub(super) fn maybe_ack_non_eliciting(&mut self) {
+1204        // If we're going to send a packet anyway, and we've received a significant number of
+1205        // non-ACK-eliciting packets, then include an ACK to help the peer perform timely loss
+1206        // detection even if they're not sending any ACK-eliciting packets themselves. Exact
+1207        // threshold chosen somewhat arbitrarily.
+1208        const LAZY_ACK_THRESHOLD: u64 = 10;
+1209        if self.non_ack_eliciting_since_last_ack_sent > LAZY_ACK_THRESHOLD {
+1210            self.immediate_ack_required = true;
+1211        }
+1212    }
+1213}
+1214
+1215/// Helper for mitigating [optimistic ACK attacks]
+1216///
+1217/// A malicious peer could prompt the local application to begin a large data transfer, and then
+1218/// send ACKs without first waiting for data to be received. This could defeat congestion control,
+1219/// allowing the connection to consume disproportionate resources. We therefore occasionally skip
+1220/// packet numbers, and classify any ACK referencing a skipped packet number as a transport error.
+1221///
+1222/// Skipped packet numbers occur only in the application data space (where costly transfers might
+1223/// take place) and are distributed exponentially to reflect the reduced likelihood and impact of
+1224/// bad behavior from a peer that has been well-behaved for an extended period.
+1225///
+1226/// ACKs for packet numbers that have not yet been allocated are also a transport error, but an
+1227/// attacker with knowledge of the congestion control algorithm in use could time falsified ACKs to
+1228/// arrive after the packets they reference are sent.
+1229///
+1230/// [optimistic ACK attacks]: https://www.rfc-editor.org/rfc/rfc9000.html#name-optimistic-ack-attack
+1231pub(super) struct PacketNumberFilter {
+1232    /// Next outgoing packet number to skip
+1233    next_skipped_packet_number: u64,
+1234    /// Most recently skipped packet number
+1235    prev_skipped_packet_number: Option<u64>,
+1236    /// Next packet number to skip is randomly selected from 2^n..2^n+1
+1237    exponent: u32,
+1238}
+1239
+1240impl PacketNumberFilter {
+1241    pub(super) fn new(rng: &mut (impl CryptoRng + ?Sized)) -> Self {
+1242        // First skipped PN is in 0..64
+1243        let exponent = 6;
+1244        Self {
+1245            next_skipped_packet_number: rng.random_range(0..2u64.saturating_pow(exponent)),
+1246            prev_skipped_packet_number: None,
+1247            exponent,
+1248        }
+1249    }
+1250
+1251    #[cfg(test)]
+1252    pub(super) fn disabled() -> Self {
+1253        Self {
+1254            next_skipped_packet_number: u64::MAX,
+1255            prev_skipped_packet_number: None,
+1256            exponent: u32::MAX,
+1257        }
+1258    }
+1259
+1260    /// Whether to use the provided packet number (false) or to skip it (true)
+1261    pub(super) fn skip_pn(&mut self, n: u64, rng: &mut (impl CryptoRng + ?Sized)) -> bool {
+1262        if n != self.next_skipped_packet_number {
+1263            return false;
+1264        }
+1265
+1266        trace!("skipping pn {n}");
+1267        // Skip this packet number, and choose the next one to skip
+1268        self.prev_skipped_packet_number = Some(self.next_skipped_packet_number);
+1269        let next_exponent = self.exponent.saturating_add(1);
+1270        self.next_skipped_packet_number = rng
+1271            .random_range(2u64.saturating_pow(self.exponent)..2u64.saturating_pow(next_exponent));
+1272        self.exponent = next_exponent;
+1273        true
+1274    }
+1275}
+1276
+1277/// Ensures we can always fit all our ACKs in a single minimum-MTU packet with room to spare
+1278const MAX_ACK_BLOCKS: usize = 64;
+1279
+1280#[cfg(test)]
+1281mod test {
+1282    use rand::RngCore;
+1283    use rand::seq::SliceRandom;
+1284
+1285    use crate::token::ResetToken;
+1286    use crate::{ConnectionIdGenerator, RandomConnectionIdGenerator};
+1287
+1288    use super::*;
+1289
+1290    #[test]
+1291    fn sanity() {
+1292        let mut dedup = Dedup::new();
+1293        assert!(!dedup.insert(0));
+1294        assert_eq!(dedup.next, 1);
+1295        assert_eq!(dedup.window, 0b1);
+1296        assert!(dedup.insert(0));
+1297        assert_eq!(dedup.next, 1);
+1298        assert_eq!(dedup.window, 0b1);
+1299        assert!(!dedup.insert(1));
+1300        assert_eq!(dedup.next, 2);
+1301        assert_eq!(dedup.window, 0b11);
+1302        assert!(!dedup.insert(2));
+1303        assert_eq!(dedup.next, 3);
+1304        assert_eq!(dedup.window, 0b111);
+1305        assert!(!dedup.insert(4));
+1306        assert_eq!(dedup.next, 5);
+1307        assert_eq!(dedup.window, 0b11110);
+1308        assert!(!dedup.insert(7));
+1309        assert_eq!(dedup.next, 8);
+1310        assert_eq!(dedup.window, 0b1111_0100);
+1311        assert!(dedup.insert(4));
+1312        assert!(!dedup.insert(3));
+1313        assert_eq!(dedup.next, 8);
+1314        assert_eq!(dedup.window, 0b1111_1100);
+1315        assert!(!dedup.insert(6));
+1316        assert_eq!(dedup.next, 8);
+1317        assert_eq!(dedup.window, 0b1111_1101);
+1318        assert!(!dedup.insert(5));
+1319        assert_eq!(dedup.next, 8);
+1320        assert_eq!(dedup.window, 0b1111_1111);
+1321    }
+1322
+1323    #[test]
+1324    fn happypath() {
+1325        let mut dedup = Dedup::new();
+1326        for i in 0..(2 * WINDOW_SIZE) {
+1327            assert!(!dedup.insert(i));
+1328            for j in 0..=i {
+1329                assert!(dedup.insert(j));
+1330            }
+1331        }
+1332    }
+1333
+1334    #[test]
+1335    fn jump() {
+1336        let mut dedup = Dedup::new();
+1337        dedup.insert(2 * WINDOW_SIZE);
+1338        assert!(dedup.insert(WINDOW_SIZE));
+1339        assert_eq!(dedup.next, 2 * WINDOW_SIZE + 1);
+1340        assert_eq!(dedup.window, 0);
+1341        assert!(!dedup.insert(WINDOW_SIZE + 1));
+1342        assert_eq!(dedup.next, 2 * WINDOW_SIZE + 1);
+1343        assert_eq!(dedup.window, 1 << (WINDOW_SIZE - 2));
+1344    }
+1345
+1346    #[test]
+1347    fn dedup_has_missing() {
+1348        let mut dedup = Dedup::new();
+1349
+1350        dedup.insert(0);
+1351        assert!(!dedup.missing_in_interval(0, 0));
+1352
+1353        dedup.insert(1);
+1354        assert!(!dedup.missing_in_interval(0, 1));
+1355
+1356        dedup.insert(3);
+1357        assert!(dedup.missing_in_interval(1, 3));
+1358
+1359        dedup.insert(4);
+1360        assert!(!dedup.missing_in_interval(3, 4));
+1361        assert!(dedup.missing_in_interval(0, 4));
+1362
+1363        dedup.insert(2);
+1364        assert!(!dedup.missing_in_interval(0, 4));
+1365    }
+1366
+1367    #[test]
+1368    fn dedup_outside_of_window_has_missing() {
+1369        let mut dedup = Dedup::new();
+1370
+1371        for i in 0..140 {
+1372            dedup.insert(i);
+1373        }
+1374
+1375        // 0 and 4 are outside of the window
+1376        assert!(!dedup.missing_in_interval(0, 4));
+1377        dedup.insert(160);
+1378        assert!(!dedup.missing_in_interval(0, 4));
+1379        assert!(!dedup.missing_in_interval(0, 140));
+1380        assert!(dedup.missing_in_interval(0, 160));
+1381    }
+1382
+1383    #[test]
+1384    fn dedup_smallest_missing() {
+1385        let mut dedup = Dedup::new();
+1386
+1387        dedup.insert(0);
+1388        assert_eq!(dedup.smallest_missing_in_interval(0, 0), None);
+1389
+1390        dedup.insert(1);
+1391        assert_eq!(dedup.smallest_missing_in_interval(0, 1), None);
+1392
+1393        dedup.insert(5);
+1394        dedup.insert(7);
+1395        assert_eq!(dedup.smallest_missing_in_interval(0, 7), Some(2));
+1396        assert_eq!(dedup.smallest_missing_in_interval(5, 7), Some(6));
+1397
+1398        dedup.insert(2);
+1399        assert_eq!(dedup.smallest_missing_in_interval(1, 7), Some(3));
+1400
+1401        dedup.insert(170);
+1402        dedup.insert(172);
+1403        dedup.insert(300);
+1404        assert_eq!(dedup.smallest_missing_in_interval(170, 172), None);
+1405
+1406        dedup.insert(500);
+1407        assert_eq!(dedup.smallest_missing_in_interval(0, 500), Some(372));
+1408        assert_eq!(dedup.smallest_missing_in_interval(0, 373), Some(372));
+1409        assert_eq!(dedup.smallest_missing_in_interval(0, 372), None);
+1410    }
+1411
+1412    #[test]
+1413    fn pending_acks_first_packet_is_not_considered_reordered() {
+1414        let mut acks = PendingAcks::new();
+1415        let mut dedup = Dedup::new();
+1416        dedup.insert(0);
+1417        acks.packet_received(Instant::now(), 0, true, &dedup);
+1418        assert!(!acks.immediate_ack_required);
+1419    }
+1420
+1421    #[test]
+1422    fn pending_acks_after_immediate_ack_set() {
+1423        let mut acks = PendingAcks::new();
+1424        let mut dedup = Dedup::new();
+1425
+1426        // Receive ack-eliciting packet
+1427        dedup.insert(0);
+1428        let now = Instant::now();
+1429        acks.insert_one(0, now);
+1430        acks.packet_received(now, 0, true, &dedup);
+1431
+1432        // Sanity check
+1433        assert!(!acks.ranges.is_empty());
+1434        assert!(!acks.can_send());
+1435
+1436        // Can send ACK after max_ack_delay exceeded
+1437        acks.set_immediate_ack_required();
+1438        assert!(acks.can_send());
+1439    }
+1440
+1441    #[test]
+1442    fn pending_acks_ack_delay() {
+1443        let mut acks = PendingAcks::new();
+1444        let mut dedup = Dedup::new();
+1445
+1446        let t1 = Instant::now();
+1447        let t2 = t1 + Duration::from_millis(2);
+1448        let t3 = t2 + Duration::from_millis(5);
+1449        assert_eq!(acks.ack_delay(t1), Duration::from_millis(0));
+1450        assert_eq!(acks.ack_delay(t2), Duration::from_millis(0));
+1451        assert_eq!(acks.ack_delay(t3), Duration::from_millis(0));
+1452
+1453        // In-order packet
+1454        dedup.insert(0);
+1455        acks.insert_one(0, t1);
+1456        acks.packet_received(t1, 0, true, &dedup);
+1457        assert_eq!(acks.ack_delay(t1), Duration::from_millis(0));
+1458        assert_eq!(acks.ack_delay(t2), Duration::from_millis(2));
+1459        assert_eq!(acks.ack_delay(t3), Duration::from_millis(7));
+1460
+1461        // Out of order (higher than expected)
+1462        dedup.insert(3);
+1463        acks.insert_one(3, t2);
+1464        acks.packet_received(t2, 3, true, &dedup);
+1465        assert_eq!(acks.ack_delay(t2), Duration::from_millis(0));
+1466        assert_eq!(acks.ack_delay(t3), Duration::from_millis(5));
+1467
+1468        // Out of order (lower than expected, so previous instant is kept)
+1469        dedup.insert(2);
+1470        acks.insert_one(2, t3);
+1471        acks.packet_received(t3, 2, true, &dedup);
+1472        assert_eq!(acks.ack_delay(t3), Duration::from_millis(5));
+1473    }
+1474
+1475    #[test]
+1476    fn sent_packet_size() {
+1477        // The tracking state of sent packets should be minimal, and not grow
+1478        // over time.
+1479        assert!(std::mem::size_of::<SentPacket>() <= 128);
+1480    }
+1481
+1482    #[test]
+1483    fn pending_new_cids() {
+1484        #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+1485        use aws_lc_rs::hmac;
+1486        #[cfg(feature = "ring")]
+1487        use ring::hmac;
+1488
+1489        let mut cid_generator = RandomConnectionIdGenerator::new(8);
+1490        let mut reset_key = [0; 64];
+1491        rand::rng().fill_bytes(&mut reset_key);
+1492        let hmac = hmac::Key::new(hmac::HMAC_SHA256, &reset_key);
+1493
+1494        let cid_a = cid_generator.generate_cid();
+1495        let a = IssuedCid {
+1496            path_id: PathId::ZERO,
+1497            sequence: 1,
+1498            id: cid_a,
+1499            reset_token: ResetToken::new(&hmac, cid_a),
+1500        };
+1501        let cid_b = cid_generator.generate_cid();
+1502        let b = IssuedCid {
+1503            path_id: PathId::ZERO,
+1504            sequence: 2,
+1505            id: cid_b,
+1506            reset_token: ResetToken::new(&hmac, cid_b),
+1507        };
+1508        let cid_c = cid_generator.generate_cid();
+1509        let c = IssuedCid {
+1510            path_id: PathId(1),
+1511            sequence: 1,
+1512            id: cid_c,
+1513            reset_token: ResetToken::new(&hmac, cid_c),
+1514        };
+1515
+1516        let mut pending_cids = PendingNewCids::default();
+1517
+1518        for _ in 0..9 {
+1519            // Push CIDs in a random order
+1520            let mut input = vec![a, b, c];
+1521            input.shuffle(&mut rand::rng());
+1522            for cid in input {
+1523                pending_cids.push(cid);
+1524            }
+1525
+1526            // Pop order is always the same
+1527            assert_eq!(pending_cids.pop().map(|i| i.id), Some(a.id));
+1528            assert_eq!(pending_cids.pop().map(|i| i.id), Some(b.id));
+1529            assert_eq!(pending_cids.pop().map(|i| i.id), Some(c.id));
+1530            assert!(pending_cids.pop().is_none());
+1531        }
+1532    }
+1533}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/connection/state.rs.html b/pr/509/docs/src/noq_proto/connection/state.rs.html new file mode 100644 index 000000000..d069d420d --- /dev/null +++ b/pr/509/docs/src/noq_proto/connection/state.rs.html @@ -0,0 +1,433 @@ +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    pub(super) fn move_to_drained(&mut self, error: Option<ConnectionError>) {
+70        let (error, is_local) = if let Some(error) = error {
+71            (Some(error), false)
+72        } else {
+73            let error = match &mut self.inner {
+74                InnerState::Draining { error, .. } => error.take(),
+75                InnerState::Drained { .. } => panic!("invalid state transition drained -> drained"),
+76                InnerState::Closed { error_read, .. } if *error_read => None,
+77                InnerState::Closed { remote_reason, .. } => {
+78                    let error = match remote_reason.clone().into() {
+79                        ConnectionError::ConnectionClosed(close) => {
+80                            if close.error_code == TransportErrorCode::PROTOCOL_VIOLATION {
+81                                ConnectionError::TransportError(TransportError::new(
+82                                    close.error_code,
+83                                    std::string::String::from_utf8_lossy(&close.reason[..])
+84                                        .to_string(),
+85                                ))
+86                            } else {
+87                                ConnectionError::ConnectionClosed(close)
+88                            }
+89                        }
+90                        e => e,
+91                    };
+92                    Some(error)
+93                }
+94                InnerState::Handshake(_) | InnerState::Established => None,
+95            };
+96            (error, self.is_local_close())
+97        };
+98        self.inner = InnerState::Drained { error, is_local };
+99        trace!("connection state: drained");
+100    }
+101
+102    /// Moves to a draining state.
+103    ///
+104    /// Panics if the state is already draining or drained.
+105    pub(super) fn move_to_draining(&mut self, error: Option<ConnectionError>) {
+106        assert!(
+107            matches!(
+108                self.inner,
+109                InnerState::Handshake(_) | InnerState::Established | InnerState::Closed { .. }
+110            ),
+111            "invalid state transition {:?} -> draining",
+112            self.as_type()
+113        );
+114        let is_local = self.is_local_close();
+115
+116        // If no error is provided and we were in the `Closed` state with an unread non-local
+117        // error, preserve that error so `take_error` can still surface it. Otherwise the
+118        // `ConnectionLost` event would never be emitted, breaking the invariant that a
+119        // drained noq-proto connection will always produce a `ConnectionLost` event.
+120        let error = error.or_else(|| {
+121            if let InnerState::Closed {
+122                ref remote_reason,
+123                error_read: false,
+124                is_local: false,
+125            } = self.inner
+126            {
+127                Some(remote_reason.clone().into())
+128            } else {
+129                None
+130            }
+131        });
+132
+133        self.inner = InnerState::Draining { error, is_local };
+134        trace!("connection state: draining");
+135    }
+136
+137    fn is_local_close(&self) -> bool {
+138        match self.inner {
+139            InnerState::Handshake(_) => false,
+140            InnerState::Established => false,
+141            InnerState::Closed { is_local, .. } => is_local,
+142            InnerState::Draining { is_local, .. } => is_local,
+143            InnerState::Drained { is_local, .. } => is_local,
+144        }
+145    }
+146
+147    /// Enters the Closing connection state, due to changes in the [`Connection`] state.
+148    ///
+149    /// This is the closing state from
+150    /// <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1> due to the local side
+151    /// having initiated immediate close.
+152    ///
+153    /// Crucially, this is to be used when internal state changes result in initiating an
+154    /// immediate close. The resulting error will be surfaced as a [`ConnectionLost`] event
+155    /// in [`Connection::poll`].
+156    ///
+157    /// # Panics
+158    ///
+159    /// Panics if the state is later than established.
+160    ///
+161    /// [`Connection`]: super::Connection
+162    /// [`ConnectionLost`]: crate::Event::ConnectionLost
+163    /// [`Connection::poll`]: super::Connection::poll
+164    pub(super) fn move_to_closed<R: Into<CloseReason>>(&mut self, reason: R) {
+165        assert!(
+166            matches!(
+167                self.inner,
+168                InnerState::Handshake(_) | InnerState::Established | InnerState::Closed { .. }
+169            ),
+170            "invalid state transition {:?} -> closed",
+171            self.as_type()
+172        );
+173        let remote_reason = reason.into();
+174        let is_local = false;
+175        trace!(?remote_reason, ?is_local, "connection state: closed");
+176        self.inner = InnerState::Closed {
+177            error_read: false,
+178            remote_reason,
+179            is_local,
+180        };
+181    }
+182
+183    /// Enters the Closing connection state, initiated by explicit API calls.
+184    ///
+185    /// This is the closing state from
+186    /// <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1> due to the local side
+187    /// having initiated immediate close.
+188    ///
+189    /// Crucially, this is to be used when immediate close is entered due to an API
+190    /// being called. It means the close will NOT surface as a [`ConnectionLost`] event in
+191    /// [`Connection::poll`].
+192    ///
+193    /// See [`Self::move_to_closed`] for when the internal state changes resulted in
+194    /// initiating an immediate close.
+195    ///
+196    /// # Panics
+197    ///
+198    /// Panics if the state is later than established.
+199    ///
+200    /// [`ConnectionLost`]: crate::Event::ConnectionLost
+201    /// [`Connection::poll`]: super::Connection::poll
+202    pub(super) fn move_to_closed_local<R: Into<CloseReason>>(&mut self, reason: R) {
+203        assert!(
+204            matches!(
+205                self.inner,
+206                InnerState::Handshake(_) | InnerState::Established | InnerState::Closed { .. }
+207            ),
+208            "invalid state transition {:?} -> closed (local)",
+209            self.as_type()
+210        );
+211        let remote_reason = reason.into();
+212        let is_local = true;
+213        trace!(?remote_reason, ?is_local, "connection state: closed");
+214        self.inner = InnerState::Closed {
+215            error_read: false,
+216            remote_reason,
+217            is_local,
+218        };
+219    }
+220
+221    pub(super) fn is_handshake(&self) -> bool {
+222        matches!(self.inner, InnerState::Handshake(_))
+223    }
+224
+225    pub(super) fn is_established(&self) -> bool {
+226        matches!(self.inner, InnerState::Established)
+227    }
+228
+229    pub(super) fn is_closed(&self) -> bool {
+230        matches!(
+231            self.inner,
+232            InnerState::Closed { .. } | InnerState::Draining { .. } | InnerState::Drained { .. }
+233        )
+234    }
+235
+236    pub(super) fn is_drained(&self) -> bool {
+237        matches!(self.inner, InnerState::Drained { .. })
+238    }
+239
+240    pub(super) fn take_error(&mut self) -> Option<ConnectionError> {
+241        match &mut self.inner {
+242            InnerState::Draining { error, is_local } => {
+243                if !*is_local {
+244                    error.take()
+245                } else {
+246                    None
+247                }
+248            }
+249            InnerState::Drained { error, is_local } => {
+250                if !*is_local {
+251                    error.take()
+252                } else {
+253                    None
+254                }
+255            }
+256            InnerState::Closed {
+257                remote_reason,
+258                is_local: local_reason,
+259                error_read,
+260            } => {
+261                if *error_read {
+262                    None
+263                } else {
+264                    *error_read = true;
+265                    if *local_reason {
+266                        None
+267                    } else {
+268                        Some(remote_reason.clone().into())
+269                    }
+270                }
+271            }
+272            InnerState::Handshake(_) | InnerState::Established => None,
+273        }
+274    }
+275
+276    pub(super) fn as_type(&self) -> StateType {
+277        match self.inner {
+278            InnerState::Handshake(_) => StateType::Handshake,
+279            InnerState::Established => StateType::Established,
+280            InnerState::Closed { .. } => StateType::Closed,
+281            InnerState::Draining { .. } => StateType::Draining,
+282            InnerState::Drained { .. } => StateType::Drained,
+283        }
+284    }
+285}
+286
+287/// The state a [`Connection`] can be in.
+288///
+289/// [`Connection`]: super::Connection
+290#[derive(Debug, Clone)]
+291pub(super) enum StateType {
+292    /// Before the handshake is *confirmed*.
+293    Handshake,
+294    /// Once the handshake is *confirmed*.
+295    Established,
+296    /// The connection is closed, waiting for remote to confirm close.
+297    ///
+298    /// Specifically <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1>.
+299    ///
+300    /// So the side that initiates an immediate close will stay in this state while it is
+301    /// waiting for the remote to also send a CONNECTION_CLOSE. The side that receives a
+302    /// connection close will skip straight to [`StateType::Draining`].
+303    Closed,
+304    /// The connection is draining, giving time to gracefully discard any in-flight packets.
+305    ///
+306    /// Specifically <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.2>.
+307    ///
+308    /// See [`StateType::Closed`] above for more details.
+309    Draining,
+310    /// The connection is drained, waiting for the application to drop us.
+311    ///
+312    /// This is a terminal state in which the connection does nothing and can never do
+313    /// anything again. Waiting for the application to drop the [`Connection`] struct.
+314    ///
+315    /// [`Connection`]: super::Connection
+316    Drained,
+317}
+318
+319#[derive(Debug, Clone)]
+320pub(super) enum CloseReason {
+321    TransportError(TransportError),
+322    Connection(ConnectionClose),
+323    Application(ApplicationClose),
+324}
+325
+326impl From<TransportError> for CloseReason {
+327    fn from(x: TransportError) -> Self {
+328        Self::TransportError(x)
+329    }
+330}
+331impl From<ConnectionClose> for CloseReason {
+332    fn from(x: ConnectionClose) -> Self {
+333        Self::Connection(x)
+334    }
+335}
+336impl From<ApplicationClose> for CloseReason {
+337    fn from(x: ApplicationClose) -> Self {
+338        Self::Application(x)
+339    }
+340}
+341
+342impl From<Close> for CloseReason {
+343    fn from(value: Close) -> Self {
+344        match value {
+345            Close::Application(reason) => Self::Application(reason),
+346            Close::Connection(reason) => Self::Connection(reason),
+347        }
+348    }
+349}
+350
+351impl From<CloseReason> for ConnectionError {
+352    fn from(value: CloseReason) -> Self {
+353        match value {
+354            CloseReason::TransportError(err) => Self::TransportError(err),
+355            CloseReason::Connection(reason) => Self::ConnectionClosed(reason),
+356            CloseReason::Application(reason) => Self::ApplicationClosed(reason),
+357        }
+358    }
+359}
+360
+361impl From<CloseReason> for Close {
+362    fn from(value: CloseReason) -> Self {
+363        match value {
+364            CloseReason::TransportError(err) => Self::Connection(err.into()),
+365            CloseReason::Connection(reason) => Self::Connection(reason),
+366            CloseReason::Application(reason) => Self::Application(reason),
+367        }
+368    }
+369}
+370
+371#[derive(Debug, Clone)]
+372enum InnerState {
+373    /// See [`StateType::Handshake`].
+374    Handshake(Handshake),
+375    /// See [`StateType::Established`].
+376    Established,
+377    /// See [`StateType::Closed`].
+378    Closed {
+379        /// The reason the remote closed the connection, or the reason we are sending to the remote.
+380        remote_reason: CloseReason,
+381        /// Set to true if we closed the connection locally.
+382        is_local: bool,
+383        /// Did we read this as error already?
+384        error_read: bool,
+385    },
+386    /// See [`StateType::Draining`].
+387    Draining {
+388        /// Why the connection was lost, if it has been.
+389        error: Option<ConnectionError>,
+390        /// Set to true if we closed the connection locally.
+391        is_local: bool,
+392    },
+393    /// See [`StateType::Drained`].
+394    /// Waiting for application to call close so we can dispose of the resources.
+395    Drained {
+396        /// Why the connection was lost, if it has been.
+397        error: Option<ConnectionError>,
+398        /// Set to true if we closed the connection locally.
+399        is_local: bool,
+400    },
+401}
+402
+403#[allow(unreachable_pub)] // fuzzing only
+404#[derive(Debug, Clone)]
+405pub struct Handshake {
+406    /// Whether the remote CID has been set by the peer yet.
+407    ///
+408    /// Always set for servers.
+409    pub(super) remote_cid_set: bool,
+410    /// Stateless retry token received in the first Initial by a server.
+411    ///
+412    /// Must be present in every Initial. Always empty for clients.
+413    pub(super) expected_token: Bytes,
+414    /// First cryptographic message.
+415    ///
+416    /// Only set for clients.
+417    pub(super) client_hello: Option<Bytes>,
+418    /// Whether the server address is allowed to migrate.
+419    ///
+420    /// We allow the server to migrate during the handshake as long as we have not
+421    /// received an authenticated handshake packet: it can send a response from a
+422    /// different address than we sent the initial to.  This allows us to send the
+423    /// initial packet over multiple paths - by means of an IPv6 ULA address that copies
+424    /// the packets sent to it to multiple destinations - and accept one response.
+425    ///
+426    /// This is only ever set to true if for a client which hasn't yet received an
+427    /// authenticated handshake packet.  It is set back to false in
+428    /// [`super::Connection::on_packet_authenticated`].
+429    ///
+430    /// THIS IS NOT RFC 9000 COMPLIANT!  A server is not allowed to migrate addresses,
+431    /// other than using the preferred-address transport parameter.
+432    pub(super) allow_server_migration: bool,
+433}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/connection/stats.rs.html b/pr/509/docs/src/noq_proto/connection/stats.rs.html new file mode 100644 index 000000000..6dadf7df9 --- /dev/null +++ b/pr/509/docs/src/noq_proto/connection/stats.rs.html @@ -0,0 +1,205 @@ +stats.rs - source

noq_proto/connection/
stats.rs

1//! Connection statistics
+2
+3use crate::Duration;
+4use crate::FrameType;
+5
+6/// Statistics about UDP datagrams transmitted or received on a connection
+7///
+8/// All QUIC packets are carried by UDP datagrams. Hence, these statistics cover all traffic on a connection.
+9#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
+10#[non_exhaustive]
+11pub struct UdpStats {
+12    /// The amount of UDP datagrams observed
+13    pub datagrams: u64,
+14    /// The total amount of bytes which have been transferred inside UDP datagrams
+15    pub bytes: u64,
+16    /// The amount of I/O operations executed
+17    ///
+18    /// Can be less than `datagrams` when GSO, GRO, and/or batched system calls are in use.
+19    pub ios: u64,
+20}
+21
+22impl UdpStats {
+23    pub(crate) fn on_sent(&mut self, datagrams: u64, bytes: usize) {
+24        self.datagrams += datagrams;
+25        self.bytes += bytes as u64;
+26        self.ios += 1;
+27    }
+28}
+29
+30/// Number of frames transmitted or received of each frame type
+31#[derive(Default, Copy, Clone)]
+32#[non_exhaustive]
+33#[allow(missing_docs)]
+34pub struct FrameStats {
+35    pub acks: u64,
+36    pub path_acks: u64,
+37    pub ack_frequency: u64,
+38    pub crypto: u64,
+39    pub connection_close: u64,
+40    pub data_blocked: u64,
+41    pub datagram: u64,
+42    pub handshake_done: u8,
+43    pub immediate_ack: u64,
+44    pub max_data: u64,
+45    pub max_stream_data: u64,
+46    pub max_streams_bidi: u64,
+47    pub max_streams_uni: u64,
+48    pub new_connection_id: u64,
+49    pub path_new_connection_id: u64,
+50    pub new_token: u64,
+51    pub path_challenge: u64,
+52    pub path_response: u64,
+53    pub ping: u64,
+54    pub reset_stream: u64,
+55    pub retire_connection_id: u64,
+56    pub path_retire_connection_id: u64,
+57    pub stream_data_blocked: u64,
+58    pub streams_blocked_bidi: u64,
+59    pub streams_blocked_uni: u64,
+60    pub stop_sending: u64,
+61    pub stream: u64,
+62    pub observed_addr: u64,
+63    pub path_abandon: u64,
+64    pub path_status_available: u64,
+65    pub path_status_backup: u64,
+66    pub max_path_id: u64,
+67    pub paths_blocked: u64,
+68    pub path_cids_blocked: u64,
+69    pub add_address: u64,
+70    pub reach_out: u64,
+71    pub remove_address: u64,
+72}
+73
+74impl FrameStats {
+75    pub(crate) fn record(&mut self, frame_type: FrameType) {
+76        use FrameType::*;
+77        // Increments the field. Added for readability
+78        macro_rules! inc {
+79            ($field_name: ident) => {{ self.$field_name = self.$field_name.saturating_add(1) }};
+80        }
+81        match frame_type {
+82            Padding => {}
+83            Ping => inc!(ping),
+84            Ack | AckEcn => inc!(acks),
+85            PathAck | PathAckEcn => inc!(path_acks),
+86            ResetStream => inc!(reset_stream),
+87            StopSending => inc!(stop_sending),
+88            Crypto => inc!(crypto),
+89            Datagram(_) => inc!(datagram),
+90            NewToken => inc!(new_token),
+91            MaxData => inc!(max_data),
+92            MaxStreamData => inc!(max_stream_data),
+93            MaxStreamsBidi => inc!(max_streams_bidi),
+94            MaxStreamsUni => inc!(max_streams_uni),
+95            DataBlocked => inc!(data_blocked),
+96            Stream(_) => inc!(stream),
+97            StreamDataBlocked => inc!(stream_data_blocked),
+98            StreamsBlockedUni => inc!(streams_blocked_uni),
+99            StreamsBlockedBidi => inc!(streams_blocked_bidi),
+100            NewConnectionId => inc!(new_connection_id),
+101            PathNewConnectionId => inc!(path_new_connection_id),
+102            RetireConnectionId => inc!(retire_connection_id),
+103            PathRetireConnectionId => inc!(path_retire_connection_id),
+104            PathChallenge => inc!(path_challenge),
+105            PathResponse => inc!(path_response),
+106            ConnectionClose | ApplicationClose => inc!(connection_close),
+107            AckFrequency => inc!(ack_frequency),
+108            ImmediateAck => inc!(immediate_ack),
+109            HandshakeDone => inc!(handshake_done),
+110            ObservedIpv4Addr | ObservedIpv6Addr => inc!(observed_addr),
+111            PathAbandon => inc!(path_abandon),
+112            PathStatusAvailable => inc!(path_status_available),
+113            PathStatusBackup => inc!(path_status_backup),
+114            MaxPathId => inc!(max_path_id),
+115            PathsBlocked => inc!(paths_blocked),
+116            PathCidsBlocked => inc!(path_cids_blocked),
+117            AddIpv4Address | AddIpv6Address => inc!(add_address),
+118            ReachOutAtIpv4 | ReachOutAtIpv6 => inc!(reach_out),
+119            RemoveAddress => inc!(remove_address),
+120        };
+121    }
+122}
+123
+124impl std::fmt::Debug for FrameStats {
+125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+126        f.debug_struct("FrameStats")
+127            .field("ACK", &self.acks)
+128            .field("ACK_FREQUENCY", &self.ack_frequency)
+129            .field("CONNECTION_CLOSE", &self.connection_close)
+130            .field("CRYPTO", &self.crypto)
+131            .field("DATA_BLOCKED", &self.data_blocked)
+132            .field("DATAGRAM", &self.datagram)
+133            .field("HANDSHAKE_DONE", &self.handshake_done)
+134            .field("IMMEDIATE_ACK", &self.immediate_ack)
+135            .field("MAX_DATA", &self.max_data)
+136            .field("MAX_PATH_ID", &self.max_path_id)
+137            .field("MAX_STREAM_DATA", &self.max_stream_data)
+138            .field("MAX_STREAMS_BIDI", &self.max_streams_bidi)
+139            .field("MAX_STREAMS_UNI", &self.max_streams_uni)
+140            .field("NEW_CONNECTION_ID", &self.new_connection_id)
+141            .field("NEW_TOKEN", &self.new_token)
+142            .field("PATHS_BLOCKED", &self.paths_blocked)
+143            .field("PATH_ABANDON", &self.path_abandon)
+144            .field("PATH_ACK", &self.path_acks)
+145            .field("PATH_STATUS_AVAILABLE", &self.path_status_available)
+146            .field("PATH_STATUS_BACKUP", &self.path_status_backup)
+147            .field("PATH_CHALLENGE", &self.path_challenge)
+148            .field("PATH_CIDS_BLOCKED", &self.path_cids_blocked)
+149            .field("PATH_NEW_CONNECTION_ID", &self.path_new_connection_id)
+150            .field("PATH_RESPONSE", &self.path_response)
+151            .field("PATH_RETIRE_CONNECTION_ID", &self.path_retire_connection_id)
+152            .field("PING", &self.ping)
+153            .field("RESET_STREAM", &self.reset_stream)
+154            .field("RETIRE_CONNECTION_ID", &self.retire_connection_id)
+155            .field("STREAM_DATA_BLOCKED", &self.stream_data_blocked)
+156            .field("STREAMS_BLOCKED_BIDI", &self.streams_blocked_bidi)
+157            .field("STREAMS_BLOCKED_UNI", &self.streams_blocked_uni)
+158            .field("STOP_SENDING", &self.stop_sending)
+159            .field("STREAM", &self.stream)
+160            .finish()
+161    }
+162}
+163
+164/// Statistics related to a transmission path
+165#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
+166#[non_exhaustive]
+167pub struct PathStats {
+168    /// Current best estimate of this connection's latency (round-trip-time)
+169    pub rtt: Duration,
+170    /// Statistics about datagrams and bytes sent on this path
+171    pub udp_tx: UdpStats,
+172    /// Statistics about datagrams and bytes received on this path
+173    pub udp_rx: UdpStats,
+174    /// Current congestion window of the connection
+175    pub cwnd: u64,
+176    /// Congestion events on the connection
+177    pub congestion_events: u64,
+178    /// The amount of packets lost on this path
+179    pub lost_packets: u64,
+180    /// The amount of bytes lost on this path
+181    pub lost_bytes: u64,
+182    /// The amount of PLPMTUD probe packets sent on this path (also counted by `udp_tx.datagrams`)
+183    pub sent_plpmtud_probes: u64,
+184    /// The amount of PLPMTUD probe packets lost on this path (ignored by `lost_packets` and
+185    /// `lost_bytes`)
+186    pub lost_plpmtud_probes: u64,
+187    /// The number of times a black hole was detected in the path
+188    pub black_holes_detected: u64,
+189    /// Largest UDP payload size the path currently supports
+190    pub current_mtu: u16,
+191}
+192
+193/// Connection statistics
+194#[derive(Debug, Default, Clone)]
+195#[non_exhaustive]
+196pub struct ConnectionStats {
+197    /// Statistics about UDP datagrams transmitted on a connection
+198    pub udp_tx: UdpStats,
+199    /// Statistics about UDP datagrams received on a connection
+200    pub udp_rx: UdpStats,
+201    /// Statistics about frames transmitted on a connection
+202    pub frame_tx: FrameStats,
+203    /// Statistics about frames received on a connection
+204    pub frame_rx: FrameStats,
+205}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/connection/streams/mod.rs.html b/pr/509/docs/src/noq_proto/connection/streams/mod.rs.html new file mode 100644 index 000000000..d927a36e6 --- /dev/null +++ b/pr/509/docs/src/noq_proto/connection/streams/mod.rs.html @@ -0,0 +1,554 @@ +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, Written};
+25
+26mod state;
+27#[allow(unreachable_pub)] // fuzzing only
+28pub use state::StreamsState;
+29
+30/// Access to streams
+31pub struct Streams<'a> {
+32    pub(super) state: &'a mut StreamsState,
+33    pub(super) conn_state: &'a super::State,
+34}
+35
+36#[allow(clippy::needless_lifetimes)] // Needed for cfg(fuzzing)
+37impl<'a> Streams<'a> {
+38    #[cfg(fuzzing)]
+39    pub fn new(state: &'a mut StreamsState, conn_state: &'a super::State) -> Self {
+40        Self { state, conn_state }
+41    }
+42
+43    /// Open a single stream if possible
+44    ///
+45    /// Returns `None` if the streams in the given direction are currently exhausted.
+46    pub fn open(&mut self, dir: Dir) -> Option<StreamId> {
+47        if self.conn_state.is_closed() {
+48            return None;
+49        }
+50
+51        // TODO: Queue STREAM_ID_BLOCKED if this fails
+52        if self.state.next[dir as usize] >= self.state.max[dir as usize] {
+53            return None;
+54        }
+55
+56        self.state.next[dir as usize] += 1;
+57        let id = StreamId::new(self.state.side, dir, self.state.next[dir as usize] - 1);
+58        self.state.insert(false, id);
+59        self.state.send_streams += 1;
+60        Some(id)
+61    }
+62
+63    /// Accept a remotely initiated stream of a certain directionality, if possible
+64    ///
+65    /// Returns `None` if there are no new incoming streams for this connection.
+66    /// Has no impact on the data flow-control or stream concurrency limits.
+67    pub fn accept(&mut self, dir: Dir) -> Option<StreamId> {
+68        if self.state.next_remote[dir as usize] == self.state.next_reported_remote[dir as usize] {
+69            return None;
+70        }
+71
+72        let x = self.state.next_reported_remote[dir as usize];
+73        self.state.next_reported_remote[dir as usize] = x + 1;
+74        if dir == Dir::Bi {
+75            self.state.send_streams += 1;
+76        }
+77
+78        Some(StreamId::new(!self.state.side, dir, x))
+79    }
+80
+81    #[cfg(fuzzing)]
+82    pub fn state(&mut self) -> &mut StreamsState {
+83        self.state
+84    }
+85
+86    /// The number of streams that may have unacknowledged data.
+87    pub fn send_streams(&self) -> usize {
+88        self.state.send_streams
+89    }
+90
+91    /// The number of remotely initiated open streams of a certain directionality.
+92    ///
+93    /// Includes remotely initiated streams, which have not been accepted via [`accept`](Self::accept).
+94    /// These streams count against the respective concurrency limit reported by
+95    /// [`Connection::max_concurrent_streams`](super::Connection::max_concurrent_streams).
+96    pub fn remote_open_streams(&self, dir: Dir) -> u64 {
+97        // total opened - total closed = total opened - ( total permitted - total permitted unclosed )
+98        self.state.next_remote[dir as usize]
+99            - (self.state.max_remote[dir as usize]
+100                - self.state.allocated_remote_count[dir as usize])
+101    }
+102}
+103
+104/// Access to streams
+105pub struct RecvStream<'a> {
+106    pub(super) id: StreamId,
+107    pub(super) state: &'a mut StreamsState,
+108    pub(super) pending: &'a mut Retransmits,
+109}
+110
+111impl RecvStream<'_> {
+112    /// Read from the given recv stream
+113    ///
+114    /// `max_length` limits the maximum size of the returned `Bytes` value; passing `usize::MAX`
+115    /// will yield the best performance. `ordered` will make sure the returned chunk's offset will
+116    /// have an offset exactly equal to the previously returned offset plus the previously returned
+117    /// bytes' length.
+118    ///
+119    /// Yields `Ok(None)` if the stream was finished. Otherwise, yields a segment of data and its
+120    /// offset in the stream. If `ordered` is `false`, segments may be received in any order, and
+121    /// the `Chunk`'s `offset` field can be used to determine ordering in the caller.
+122    ///
+123    /// While most applications will prefer to consume stream data in order, unordered reads can
+124    /// improve performance when packet loss occurs and data cannot be retransmitted before the flow
+125    /// control window is filled. On any given stream, you can switch from ordered to unordered
+126    /// reads, but ordered reads on streams that have seen previous unordered reads will return
+127    /// `ReadError::IllegalOrderedRead`.
+128    pub fn read(&mut self, ordered: bool) -> Result<Chunks<'_>, ReadableError> {
+129        Chunks::new(self.id, ordered, self.state, self.pending)
+130    }
+131
+132    /// Stop accepting data on the given receive stream
+133    ///
+134    /// Discards unread data and notifies the peer to stop transmitting. Once stopped, further
+135    /// attempts to operate on a stream will yield `ClosedStream` errors.
+136    pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+137        let mut entry = match self.state.recv.entry(self.id) {
+138            hash_map::Entry::Occupied(s) => s,
+139            hash_map::Entry::Vacant(_) => return Err(ClosedStream { _private: () }),
+140        };
+141        let stream = get_or_insert_recv(self.state.stream_receive_window)(entry.get_mut());
+142
+143        let (read_credits, stop_sending) = stream.stop()?;
+144        if stop_sending.should_transmit() {
+145            self.pending.stop_sending.push(frame::StopSending {
+146                id: self.id,
+147                error_code,
+148            });
+149        }
+150
+151        // We need to keep stopped streams around until they're finished or reset so we can update
+152        // connection-level flow control to account for discarded data. Otherwise, we can discard
+153        // state immediately.
+154        if !stream.final_offset_unknown() {
+155            let recv = entry.remove().expect("must have recv when stopping");
+156            self.state.stream_recv_freed(self.id, recv);
+157        }
+158
+159        if self.state.add_read_credits(read_credits).should_transmit() {
+160            self.pending.max_data = true;
+161        }
+162
+163        Ok(())
+164    }
+165
+166    /// Check whether this stream has been reset by the peer, returning the reset error code if so
+167    ///
+168    /// After returning `Ok(Some(_))` once, stream state will be discarded and all future calls will
+169    /// return `Err(ClosedStream)`.
+170    pub fn received_reset(&mut self) -> Result<Option<VarInt>, ClosedStream> {
+171        let hash_map::Entry::Occupied(entry) = self.state.recv.entry(self.id) else {
+172            return Err(ClosedStream { _private: () });
+173        };
+174        let Some(s) = entry.get().as_ref().and_then(|s| s.as_open_recv()) else {
+175            return Ok(None);
+176        };
+177        if s.stopped {
+178            return Err(ClosedStream { _private: () });
+179        }
+180        let Some(code) = s.reset_code() else {
+181            return Ok(None);
+182        };
+183
+184        // Clean up state after application observes the reset, since there's no reason for the
+185        // application to attempt to read or stop the stream once it knows it's reset
+186        let (_, recv) = entry.remove_entry();
+187        self.state
+188            .stream_recv_freed(self.id, recv.expect("must have recv on reset"));
+189        self.state.queue_max_stream_id(self.pending);
+190
+191        Ok(Some(code))
+192    }
+193}
+194
+195/// Access to streams
+196pub struct SendStream<'a> {
+197    pub(super) id: StreamId,
+198    pub(super) state: &'a mut StreamsState,
+199    pub(super) pending: &'a mut Retransmits,
+200    pub(super) conn_state: &'a super::State,
+201}
+202
+203#[allow(clippy::needless_lifetimes)] // Needed for cfg(fuzzing)
+204impl<'a> SendStream<'a> {
+205    #[cfg(fuzzing)]
+206    pub fn new(
+207        id: StreamId,
+208        state: &'a mut StreamsState,
+209        pending: &'a mut Retransmits,
+210        conn_state: &'a super::State,
+211    ) -> Self {
+212        Self {
+213            id,
+214            state,
+215            pending,
+216            conn_state,
+217        }
+218    }
+219
+220    /// Send data on the given stream
+221    ///
+222    /// Returns the number of bytes successfully written.
+223    pub fn write(&mut self, data: &[u8]) -> Result<usize, WriteError> {
+224        Ok(self.write_source(&mut ByteSlice::from_slice(data))?.bytes)
+225    }
+226
+227    /// Send data on the given stream
+228    ///
+229    /// Returns the number of bytes and chunks successfully written.
+230    /// Note that this method might also write a partial chunk. In this case
+231    /// [`Written::chunks`] will not count this chunk as fully written. However
+232    /// the chunk will be advanced and contain only non-written data after the call.
+233    pub fn write_chunks(&mut self, data: &mut [Bytes]) -> Result<Written, WriteError> {
+234        self.write_source(&mut BytesArray::from_chunks(data))
+235    }
+236
+237    fn write_source<'b, B: BytesSource<'b>>(
+238        &mut self,
+239        source: &'b mut B,
+240    ) -> Result<Written, WriteError> {
+241        if self.conn_state.is_closed() {
+242            trace!(%self.id, "write blocked; connection draining");
+243            return Err(WriteError::Blocked);
+244        }
+245
+246        let limit = self.state.write_limit();
+247
+248        let max_send_data = self.state.max_send_data(self.id);
+249
+250        let stream = self
+251            .state
+252            .send
+253            .get_mut(&self.id)
+254            .map(get_or_insert_send(max_send_data))
+255            .ok_or(WriteError::ClosedStream)?;
+256
+257        if limit == 0 {
+258            trace!(
+259                stream = %self.id, max_data = self.state.max_data, data_sent = self.state.data_sent,
+260                "write blocked by connection-level flow control or send window"
+261            );
+262            if !stream.connection_blocked {
+263                stream.connection_blocked = true;
+264                self.state.connection_blocked.push(self.id);
+265            }
+266            return Err(WriteError::Blocked);
+267        }
+268
+269        let was_pending = stream.is_pending();
+270        let written = stream.write(source, limit)?;
+271        self.state.data_sent += written.bytes as u64;
+272        self.state.unacked_data += written.bytes as u64;
+273        trace!(stream = %self.id, "wrote {} bytes", written.bytes);
+274        if !was_pending {
+275            self.state.pending.push_pending(self.id, stream.priority);
+276        }
+277        Ok(written)
+278    }
+279
+280    /// Check if this stream was stopped, get the reason if it was
+281    pub fn stopped(&self) -> Result<Option<VarInt>, ClosedStream> {
+282        match self.state.send.get(&self.id).as_ref() {
+283            Some(Some(s)) => Ok(s.stop_reason),
+284            Some(None) => Ok(None),
+285            None => Err(ClosedStream { _private: () }),
+286        }
+287    }
+288
+289    /// Finish a send stream, signalling that no more data will be sent.
+290    ///
+291    /// If this fails, no [`StreamEvent::Finished`] will be generated.
+292    ///
+293    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+294    pub fn finish(&mut self) -> Result<(), FinishError> {
+295        let max_send_data = self.state.max_send_data(self.id);
+296        let stream = self
+297            .state
+298            .send
+299            .get_mut(&self.id)
+300            .map(get_or_insert_send(max_send_data))
+301            .ok_or(FinishError::ClosedStream)?;
+302
+303        let was_pending = stream.is_pending();
+304        stream.finish()?;
+305        if !was_pending {
+306            self.state.pending.push_pending(self.id, stream.priority);
+307        }
+308
+309        Ok(())
+310    }
+311
+312    /// Abandon transmitting data on a stream
+313    ///
+314    /// # Panics
+315    /// - when applied to a receive stream
+316    pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+317        let max_send_data = self.state.max_send_data(self.id);
+318        let stream = self
+319            .state
+320            .send
+321            .get_mut(&self.id)
+322            .map(get_or_insert_send(max_send_data))
+323            .ok_or(ClosedStream { _private: () })?;
+324
+325        if matches!(stream.state, SendState::ResetSent) {
+326            // Redundant reset call
+327            return Err(ClosedStream { _private: () });
+328        }
+329
+330        // Restore the portion of the send window consumed by the data that we aren't about to
+331        // send. We leave flow control alone because the peer's responsible for issuing additional
+332        // credit based on the final offset communicated in the RESET_STREAM frame we send.
+333        self.state.unacked_data -= stream.pending.unacked();
+334        stream.reset();
+335        self.pending.reset_stream.push((self.id, error_code));
+336
+337        // Don't reopen an already-closed stream we haven't forgotten yet
+338        Ok(())
+339    }
+340
+341    /// Set the priority of a stream
+342    ///
+343    /// # Panics
+344    /// - when applied to a receive stream
+345    pub fn set_priority(&mut self, priority: i32) -> Result<(), ClosedStream> {
+346        let max_send_data = self.state.max_send_data(self.id);
+347        let stream = self
+348            .state
+349            .send
+350            .get_mut(&self.id)
+351            .map(get_or_insert_send(max_send_data))
+352            .ok_or(ClosedStream { _private: () })?;
+353
+354        stream.priority = priority;
+355        Ok(())
+356    }
+357
+358    /// Get the priority of a stream
+359    ///
+360    /// # Panics
+361    /// - when applied to a receive stream
+362    pub fn priority(&self) -> Result<i32, ClosedStream> {
+363        let stream = self
+364            .state
+365            .send
+366            .get(&self.id)
+367            .ok_or(ClosedStream { _private: () })?;
+368
+369        Ok(stream.as_ref().map(|s| s.priority).unwrap_or_default())
+370    }
+371}
+372
+373/// A queue of streams with pending outgoing data, sorted by priority
+374struct PendingStreamsQueue {
+375    streams: BinaryHeap<PendingStream>,
+376    /// The next stream to write out. This is `Some` when `TransportConfig::send_fairness(false)` and writing a stream is
+377    /// interrupted while the stream still has some pending data. See `reinsert_pending()`.
+378    next: Option<PendingStream>,
+379    /// A monotonically decreasing counter, used to implement round-robin scheduling for streams of the same priority.
+380    /// Underflowing is not a practical concern, as it is initialized to u64::MAX and only decremented by 1 in `push_pending`
+381    recency: u64,
+382}
+383
+384impl PendingStreamsQueue {
+385    fn new() -> Self {
+386        Self {
+387            streams: BinaryHeap::new(),
+388            next: None,
+389            recency: u64::MAX,
+390        }
+391    }
+392
+393    /// Reinsert a stream that was pending and still contains unsent data.
+394    fn reinsert_pending(&mut self, id: StreamId, priority: i32) {
+395        assert!(self.next.is_none());
+396
+397        self.next = Some(PendingStream {
+398            priority,
+399            recency: self.recency, // the value here doesn't really matter
+400            id,
+401        });
+402    }
+403
+404    /// Push a pending stream ID with the given priority, queued after any already-queued streams for the priority
+405    fn push_pending(&mut self, id: StreamId, priority: i32) {
+406        // Note that in the case where fairness is disabled, if we have a reinserted stream we don't
+407        // bump it even if priority > next.priority. In order to minimize fragmentation we
+408        // always try to complete a stream once part of it has been written.
+409
+410        // As the recency counter is monotonically decreasing, we know that using its value to sort this stream will queue it
+411        // after all other queued streams of the same priority.
+412        // This is enough to implement round-robin scheduling for streams that are still pending even after being handled,
+413        // as in that case they are removed from the `BinaryHeap`, handled, and then immediately reinserted.
+414        self.recency -= 1;
+415        self.streams.push(PendingStream {
+416            priority,
+417            recency: self.recency,
+418            id,
+419        });
+420    }
+421
+422    fn pop(&mut self) -> Option<PendingStream> {
+423        self.next.take().or_else(|| self.streams.pop())
+424    }
+425
+426    fn clear(&mut self) {
+427        self.next = None;
+428        self.streams.clear();
+429    }
+430
+431    fn iter(&self) -> impl Iterator<Item = &PendingStream> {
+432        self.next.iter().chain(self.streams.iter())
+433    }
+434
+435    #[cfg(test)]
+436    fn len(&self) -> usize {
+437        self.streams.len() + self.next.is_some() as usize
+438    }
+439}
+440
+441/// The [`StreamId`] of a stream with pending data queued, ordered by its priority and recency
+442#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
+443struct PendingStream {
+444    /// The priority of the stream
+445    // Note that this field should be kept above the `recency` field, in order for the `Ord` derive to be correct
+446    // (See https://doc.rust-lang.org/stable/std/cmp/trait.Ord.html#derivable)
+447    priority: i32,
+448    /// A tie-breaker for streams of the same priority, used to improve fairness by implementing round-robin scheduling:
+449    /// Larger values are prioritized, so it is initialised to `u64::MAX`, and when a stream writes data, we know
+450    /// that it currently has the highest recency value, so it is deprioritized by setting its recency to 1 less than the
+451    /// previous lowest recency value, such that all other streams of this priority will get processed once before we get back
+452    /// round to this one
+453    recency: u64,
+454    /// The ID of the stream
+455    // The way this type is used ensures that every instance has a unique `recency` value, so this field should be kept below
+456    // the `priority` and `recency` fields, so that it does not interfere with the behaviour of the `Ord` derive
+457    id: StreamId,
+458}
+459
+460/// Application events about streams
+461#[derive(Debug, PartialEq, Eq)]
+462pub enum StreamEvent {
+463    /// One or more new streams has been opened and might be readable
+464    Opened {
+465        /// Directionality for which streams have been opened
+466        dir: Dir,
+467    },
+468    /// A currently open stream likely has data or errors waiting to be read
+469    Readable {
+470        /// Which stream is now readable
+471        id: StreamId,
+472    },
+473    /// A formerly write-blocked stream might be ready for a write or have been stopped
+474    ///
+475    /// Only generated for streams that are currently open.
+476    Writable {
+477        /// Which stream is now writable
+478        id: StreamId,
+479    },
+480    /// A finished stream has been fully acknowledged or stopped
+481    Finished {
+482        /// Which stream has been finished
+483        id: StreamId,
+484    },
+485    /// The peer asked us to stop sending on an outgoing stream
+486    Stopped {
+487        /// Which stream has been stopped
+488        id: StreamId,
+489        /// Error code supplied by the peer
+490        error_code: VarInt,
+491    },
+492    /// At least one new stream of a certain directionality may be opened
+493    Available {
+494        /// Directionality for which streams are newly available
+495        dir: Dir,
+496    },
+497}
+498
+499/// Indicates whether a frame needs to be transmitted
+500///
+501/// This type wraps around bool and uses the `#[must_use]` attribute in order
+502/// to prevent accidental loss of the frame transmission requirement.
+503#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
+504#[must_use = "A frame might need to be enqueued"]
+505pub struct ShouldTransmit(bool);
+506
+507impl ShouldTransmit {
+508    /// Returns whether a frame should be transmitted
+509    pub fn should_transmit(self) -> bool {
+510        self.0
+511    }
+512}
+513
+514/// Error indicating that a stream has not been opened or has already been finished or reset
+515#[derive(Debug, Default, Error, Clone, PartialEq, Eq)]
+516#[error("closed stream")]
+517pub struct ClosedStream {
+518    _private: (),
+519}
+520
+521impl From<ClosedStream> for io::Error {
+522    fn from(x: ClosedStream) -> Self {
+523        Self::new(io::ErrorKind::NotConnected, x)
+524    }
+525}
+526
+527#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+528enum StreamHalf {
+529    Send,
+530    Recv,
+531}
+532
+533/// A helper trait to unify Bytes, `Vec<u8>` and `&[u8]` as sources of bytes
+534pub(super) trait BytesOrSlice<'a>: AsRef<[u8]> + 'a {
+535    fn len(&self) -> usize {
+536        self.as_ref().len()
+537    }
+538    fn is_empty(&self) -> bool {
+539        self.as_ref().is_empty()
+540    }
+541    fn into_bytes(self) -> Bytes;
+542}
+543
+544impl BytesOrSlice<'_> for Bytes {
+545    fn into_bytes(self) -> Bytes {
+546        self
+547    }
+548}
+549
+550impl<'a> BytesOrSlice<'a> for &'a [u8] {
+551    fn into_bytes(self) -> Bytes {
+552        Bytes::copy_from_slice(self)
+553    }
+554}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/connection/streams/recv.rs.html b/pr/509/docs/src/noq_proto/connection/streams/recv.rs.html new file mode 100644 index 000000000..961b9961f --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/connection/streams/send.rs.html b/pr/509/docs/src/noq_proto/connection/streams/send.rs.html new file mode 100644 index 000000000..ba29956ef --- /dev/null +++ b/pr/509/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 struct Written {
+261    /// The amount of bytes which had been written
+262    pub bytes: usize,
+263    /// The amount of full chunks which had been written
+264    ///
+265    /// If a chunk was only partially written, it will not be counted by this field.
+266    pub chunks: usize,
+267}
+268
+269/// Errors triggered while writing to a send stream
+270#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+271pub enum WriteError {
+272    /// The peer is not able to accept additional data, or the connection is congested.
+273    ///
+274    /// If the peer issues additional flow control credit, a [`StreamEvent::Writable`] event will
+275    /// be generated, indicating that retrying the write might succeed.
+276    ///
+277    /// [`StreamEvent::Writable`]: crate::StreamEvent::Writable
+278    #[error("unable to accept further writes")]
+279    Blocked,
+280    /// The peer is no longer accepting data on this stream, and it has been implicitly reset. The
+281    /// stream cannot be finished or further written to.
+282    ///
+283    /// Carries an application-defined error code.
+284    ///
+285    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+286    #[error("stopped by peer: code {0}")]
+287    Stopped(VarInt),
+288    /// The stream has not been opened or has already been finished or reset
+289    #[error("closed stream")]
+290    ClosedStream,
+291}
+292
+293#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+294pub(super) enum SendState {
+295    /// Sending new data
+296    Ready,
+297    /// Stream was finished; now sending retransmits only
+298    DataSent { finish_acked: bool },
+299    /// Sent RESET
+300    ResetSent,
+301}
+302
+303/// Reasons why attempting to finish a stream might fail
+304#[derive(Debug, Error, Clone, PartialEq, Eq)]
+305pub enum FinishError {
+306    /// The peer is no longer accepting data on this stream. No
+307    /// [`StreamEvent::Finished`] event will be emitted for this stream.
+308    ///
+309    /// Carries an application-defined error code.
+310    ///
+311    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+312    #[error("stopped by peer: code {0}")]
+313    Stopped(VarInt),
+314    /// The stream has not been opened or was already finished or reset
+315    #[error("closed stream")]
+316    ClosedStream,
+317}
+318
+319#[cfg(test)]
+320mod tests {
+321    use super::*;
+322
+323    #[test]
+324    fn bytes_array() {
+325        let full = b"Hello World 123456789 ABCDEFGHJIJKLMNOPQRSTUVWXYZ".to_owned();
+326        for limit in 0..full.len() {
+327            let mut chunks = [
+328                Bytes::from_static(b""),
+329                Bytes::from_static(b"Hello "),
+330                Bytes::from_static(b"Wo"),
+331                Bytes::from_static(b""),
+332                Bytes::from_static(b"r"),
+333                Bytes::from_static(b"ld"),
+334                Bytes::from_static(b""),
+335                Bytes::from_static(b" 12345678"),
+336                Bytes::from_static(b"9 ABCDE"),
+337                Bytes::from_static(b"F"),
+338                Bytes::from_static(b"GHJIJKLMNOPQRSTUVWXYZ"),
+339            ];
+340            let num_chunks = chunks.len();
+341            let last_chunk_len = chunks[chunks.len() - 1].len();
+342
+343            let mut array = BytesArray::from_chunks(&mut chunks);
+344
+345            let mut buf = Vec::new();
+346            let mut chunks_popped = 0;
+347            let mut chunks_consumed = 0;
+348            let mut remaining = limit;
+349            loop {
+350                let (chunk, consumed) = array.pop_chunk(remaining);
+351                chunks_consumed += consumed;
+352
+353                if !chunk.is_empty() {
+354                    buf.extend_from_slice(chunk.as_ref());
+355                    remaining -= chunk.len();
+356                    chunks_popped += 1;
+357                } else {
+358                    break;
+359                }
+360            }
+361
+362            assert_eq!(&buf[..], &full[..limit]);
+363
+364            if limit == full.len() {
+365                // Full consumption of the last chunk
+366                assert_eq!(chunks_consumed, num_chunks);
+367                // Since there are empty chunks, we consume more than there are popped
+368                assert_eq!(chunks_consumed, chunks_popped + 3);
+369            } else if limit > full.len() - last_chunk_len {
+370                // Partial consumption of the last chunk
+371                assert_eq!(chunks_consumed, num_chunks - 1);
+372                assert_eq!(chunks_consumed, chunks_popped + 2);
+373            }
+374        }
+375    }
+376
+377    #[test]
+378    fn byte_slice() {
+379        let full = b"Hello World 123456789 ABCDEFGHJIJKLMNOPQRSTUVWXYZ".to_owned();
+380        for limit in 0..full.len() {
+381            let mut array = ByteSlice::from_slice(&full[..]);
+382
+383            let mut buf = Vec::new();
+384            let mut chunks_popped = 0;
+385            let mut chunks_consumed = 0;
+386            let mut remaining = limit;
+387            loop {
+388                let (chunk, consumed) = array.pop_chunk(remaining);
+389                chunks_consumed += consumed;
+390
+391                if !chunk.is_empty() {
+392                    buf.extend_from_slice(chunk.as_ref());
+393                    remaining -= chunk.len();
+394                    chunks_popped += 1;
+395                } else {
+396                    break;
+397                }
+398            }
+399
+400            assert_eq!(&buf[..], &full[..limit]);
+401            if limit != 0 {
+402                assert_eq!(chunks_popped, 1);
+403            } else {
+404                assert_eq!(chunks_popped, 0);
+405            }
+406
+407            if limit == full.len() {
+408                assert_eq!(chunks_consumed, 1);
+409            } else {
+410                assert_eq!(chunks_consumed, 0);
+411            }
+412        }
+413    }
+414}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/connection/streams/state.rs.html b/pr/509/docs/src/noq_proto/connection/streams/state.rs.html new file mode 100644 index 000000000..19576363d --- /dev/null +++ b/pr/509/docs/src/noq_proto/connection/streams/state.rs.html @@ -0,0 +1,2032 @@ +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}
+139
+140impl StreamsState {
+141    #[allow(unreachable_pub)] // fuzzing only
+142    pub fn new(
+143        side: Side,
+144        max_remote_uni: VarInt,
+145        max_remote_bi: VarInt,
+146        send_window: u64,
+147        receive_window: VarInt,
+148        stream_receive_window: VarInt,
+149    ) -> Self {
+150        let mut this = Self {
+151            side,
+152            send: FxHashMap::default(),
+153            recv: FxHashMap::default(),
+154            free_recv: Vec::new(),
+155            next: [0, 0],
+156            max: [0, 0],
+157            max_remote: [max_remote_bi.into(), max_remote_uni.into()],
+158            sent_max_remote: [max_remote_bi.into(), max_remote_uni.into()],
+159            allocated_remote_count: [max_remote_bi.into(), max_remote_uni.into()],
+160            max_concurrent_remote_count: [max_remote_bi.into(), max_remote_uni.into()],
+161            flow_control_adjusted: false,
+162            next_remote: [0, 0],
+163            opened: [false, false],
+164            next_reported_remote: [0, 0],
+165            send_streams: 0,
+166            pending: PendingStreamsQueue::new(),
+167            events: VecDeque::new(),
+168            connection_blocked: Vec::new(),
+169            max_data: 0,
+170            receive_window: receive_window.into(),
+171            local_max_data: receive_window.into(),
+172            sent_max_data: receive_window,
+173            data_sent: 0,
+174            data_recvd: 0,
+175            unacked_data: 0,
+176            send_window,
+177            stream_receive_window: stream_receive_window.into(),
+178            initial_max_stream_data_uni: 0u32.into(),
+179            initial_max_stream_data_bidi_local: 0u32.into(),
+180            initial_max_stream_data_bidi_remote: 0u32.into(),
+181            receive_window_shrink_debt: 0,
+182        };
+183
+184        for dir in Dir::iter() {
+185            for i in 0..this.max_remote[dir as usize] {
+186                this.insert(true, StreamId::new(!side, dir, i));
+187            }
+188        }
+189
+190        this
+191    }
+192
+193    pub(crate) fn set_params(&mut self, params: &TransportParameters) {
+194        self.initial_max_stream_data_uni = params.initial_max_stream_data_uni;
+195        self.initial_max_stream_data_bidi_local = params.initial_max_stream_data_bidi_local;
+196        self.initial_max_stream_data_bidi_remote = params.initial_max_stream_data_bidi_remote;
+197        self.max[Dir::Bi as usize] = params.initial_max_streams_bidi.into();
+198        self.max[Dir::Uni as usize] = params.initial_max_streams_uni.into();
+199        self.received_max_data(params.initial_max_data);
+200        for i in 0..self.max_remote[Dir::Bi as usize] {
+201            let id = StreamId::new(!self.side, Dir::Bi, i);
+202            if let Some(s) = self.send.get_mut(&id).and_then(|s| s.as_mut()) {
+203                s.max_data = params.initial_max_stream_data_bidi_local.into();
+204            }
+205        }
+206    }
+207
+208    /// Ensure we have space for at least a full flow control window of remotely-initiated streams
+209    /// to be open, and notify the peer if the window has moved
+210    fn ensure_remote_streams(&mut self, dir: Dir) {
+211        let new_count = self.max_concurrent_remote_count[dir as usize]
+212            .saturating_sub(self.allocated_remote_count[dir as usize]);
+213        for i in 0..new_count {
+214            let id = StreamId::new(!self.side, dir, self.max_remote[dir as usize] + i);
+215            self.insert(true, id);
+216        }
+217        self.allocated_remote_count[dir as usize] += new_count;
+218        self.max_remote[dir as usize] += new_count;
+219    }
+220
+221    pub(crate) fn zero_rtt_rejected(&mut self) {
+222        // Revert to initial state for outgoing streams
+223        for dir in Dir::iter() {
+224            for i in 0..self.next[dir as usize] {
+225                // We don't bother calling `stream_freed` here because we explicitly reset affected
+226                // counters below.
+227                let id = StreamId::new(self.side, dir, i);
+228                self.send.remove(&id).unwrap();
+229                if let Dir::Bi = dir {
+230                    self.recv.remove(&id).unwrap();
+231                }
+232            }
+233            self.next[dir as usize] = 0;
+234
+235            // If 0-RTT was rejected, any flow control frames we sent were lost.
+236            if self.flow_control_adjusted {
+237                // Conservative approximation of whatever we sent in transport parameters
+238                self.sent_max_remote[dir as usize] = 0;
+239            }
+240        }
+241
+242        self.pending.clear();
+243        self.send_streams = 0;
+244        self.data_sent = 0;
+245        self.connection_blocked.clear();
+246    }
+247
+248    /// Process incoming stream frame
+249    ///
+250    /// If successful, returns whether a `MAX_DATA` frame needs to be transmitted
+251    pub(crate) fn received(
+252        &mut self,
+253        frame: frame::Stream,
+254        payload_len: usize,
+255    ) -> Result<ShouldTransmit, TransportError> {
+256        let id = frame.id;
+257        self.validate_receive_id(id).inspect_err(|_e| {
+258            debug!("received illegal STREAM frame");
+259        })?;
+260
+261        let Some(rs) = self
+262            .recv
+263            .get_mut(&id)
+264            .map(get_or_insert_recv(self.stream_receive_window))
+265        else {
+266            trace!("dropping frame for closed stream");
+267            return Ok(ShouldTransmit(false));
+268        };
+269
+270        if !rs.is_receiving() {
+271            trace!("dropping frame for finished stream");
+272            return Ok(ShouldTransmit(false));
+273        }
+274
+275        let (new_bytes, closed) =
+276            rs.ingest(frame, payload_len, self.data_recvd, self.local_max_data)?;
+277        self.data_recvd = self.data_recvd.saturating_add(new_bytes);
+278
+279        if !rs.stopped {
+280            self.on_stream_frame(true, id);
+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        let Some(rs) = self
+312            .recv
+313            .get_mut(&id)
+314            .map(get_or_insert_recv(self.stream_receive_window))
+315        else {
+316            trace!("received RESET_STREAM on closed stream");
+317            return Ok(ShouldTransmit(false));
+318        };
+319
+320        // State transition
+321        if !rs.reset(
+322            error_code,
+323            final_offset,
+324            self.data_recvd,
+325            self.local_max_data,
+326        )? {
+327            // Redundant reset
+328            return Ok(ShouldTransmit(false));
+329        }
+330        let bytes_read = rs.assembler.bytes_read();
+331        let stopped = rs.stopped;
+332        let end = rs.end;
+333        if stopped {
+334            // Stopped streams should be disposed immediately on reset
+335            let rs = self.recv.remove(&id).flatten().unwrap();
+336            self.stream_recv_freed(id, rs);
+337        }
+338        self.on_stream_frame(!stopped, id);
+339
+340        // Update connection-level flow control
+341        Ok(if bytes_read != final_offset.into_inner() {
+342            // bytes_read is always <= end, so this won't underflow.
+343            self.data_recvd = self
+344                .data_recvd
+345                .saturating_add(u64::from(final_offset) - end);
+346            self.add_read_credits(u64::from(final_offset) - bytes_read)
+347        } else {
+348            ShouldTransmit(false)
+349        })
+350    }
+351
+352    /// Process incoming `STOP_SENDING` frame
+353    #[allow(unreachable_pub)] // fuzzing only
+354    pub fn received_stop_sending(&mut self, id: StreamId, error_code: VarInt) {
+355        let max_send_data = self.max_send_data(id);
+356        let Some(stream) = self
+357            .send
+358            .get_mut(&id)
+359            .map(get_or_insert_send(max_send_data))
+360        else {
+361            return;
+362        };
+363
+364        if stream.try_stop(error_code) {
+365            self.events
+366                .push_back(StreamEvent::Stopped { id, error_code });
+367            self.on_stream_frame(false, id);
+368        }
+369    }
+370
+371    pub(crate) fn reset_acked(&mut self, id: StreamId) {
+372        match self.send.entry(id) {
+373            hash_map::Entry::Vacant(_) => {}
+374            hash_map::Entry::Occupied(e) => {
+375                if let Some(SendState::ResetSent) = e.get().as_ref().map(|s| s.state) {
+376                    e.remove_entry();
+377                    self.stream_freed(id, StreamHalf::Send);
+378                }
+379            }
+380        }
+381    }
+382
+383    /// Whether any stream data is queued, regardless of control frames
+384    pub(crate) fn can_send_stream_data(&self) -> bool {
+385        // Reset streams may linger in the pending stream list, but will never produce stream frames
+386        self.pending.iter().any(|stream| {
+387            self.send
+388                .get(&stream.id)
+389                .and_then(|s| s.as_ref())
+390                .is_some_and(|s| !s.is_reset())
+391        })
+392    }
+393
+394    /// Whether MAX_STREAM_DATA frames could be sent for stream `id`
+395    pub(crate) fn can_send_flow_control(&self, id: StreamId) -> bool {
+396        self.recv
+397            .get(&id)
+398            .and_then(|s| s.as_ref())
+399            .and_then(|s| s.as_open_recv())
+400            .is_some_and(|s| s.can_send_flow_control())
+401    }
+402
+403    pub(in crate::connection) fn write_control_frames<'a, 'b>(
+404        &mut self,
+405        builder: &mut PacketBuilder<'a, 'b>,
+406        pending: &mut Retransmits,
+407        stats: &mut FrameStats,
+408    ) {
+409        // RESET_STREAM
+410        while builder.frame_space_remaining() > frame::ResetStream::SIZE_BOUND {
+411            let Some((id, error_code)) = pending.reset_stream.pop() else {
+412                break;
+413            };
+414            let Some(stream) = self.send.get_mut(&id).and_then(|s| s.as_mut()) else {
+415                continue;
+416            };
+417            let frame = frame::ResetStream {
+418                id,
+419                error_code,
+420                final_offset: VarInt::try_from(stream.offset()).expect("impossibly large offset"),
+421            };
+422            builder.write_frame(frame, stats);
+423        }
+424
+425        // STOP_SENDING
+426        while builder.frame_space_remaining() > frame::StopSending::SIZE_BOUND {
+427            let Some(frame) = pending.stop_sending.pop() else {
+428                break;
+429            };
+430            // We may need to transmit STOP_SENDING even for streams whose state we have discarded,
+431            // because we are able to discard local state for stopped streams immediately upon
+432            // receiving FIN, even if the peer still has arbitrarily large amounts of data to
+433            // (re)transmit due to loss or unconventional sending strategy. We could fine-tune this
+434            // a little by dropping the frame if we specifically know the stream's been reset by the
+435            // peer, but we discard that information as soon as the application consumes it, so it
+436            // can't be relied upon regardless.
+437            builder.write_frame(frame, stats);
+438        }
+439
+440        // MAX_DATA
+441        if pending.max_data && builder.frame_space_remaining() > 9 {
+442            pending.max_data = false;
+443
+444            // `local_max_data` can grow bigger than `VarInt`.
+445            // For transmission inside QUIC frames we need to clamp it to the
+446            // maximum allowed `VarInt` size.
+447            let max = VarInt::try_from(self.local_max_data).unwrap_or(VarInt::MAX);
+448
+449            if max > self.sent_max_data {
+450                // Record that a `MAX_DATA` announcing a certain window was sent. This will
+451                // suppress enqueuing further `MAX_DATA` frames unless either the previous
+452                // transmission was not acknowledged or the window further increased.
+453                self.sent_max_data = max;
+454            }
+455
+456            builder.write_frame(frame::MaxData(max), stats);
+457        }
+458
+459        // MAX_STREAM_DATA
+460        while builder.frame_space_remaining() > 17 {
+461            let id = match pending.max_stream_data.iter().next() {
+462                Some(x) => *x,
+463                None => break,
+464            };
+465            pending.max_stream_data.remove(&id);
+466            let Some(rs) = self
+467                .recv
+468                .get_mut(&id)
+469                .and_then(|s| s.as_mut())
+470                .and_then(|s| s.as_open_recv_mut())
+471            else {
+472                continue;
+473            };
+474            if !rs.can_send_flow_control() {
+475                continue;
+476            }
+477
+478            let (max, _) = rs.max_stream_data(self.stream_receive_window);
+479            rs.record_sent_max_stream_data(max);
+480            builder.write_frame(frame::MaxStreamData { id, offset: max }, stats);
+481        }
+482
+483        // MAX_STREAMS
+484        for dir in Dir::iter() {
+485            if !pending.max_stream_id[dir as usize] || builder.frame_space_remaining() <= 9 {
+486                continue;
+487            }
+488
+489            pending.max_stream_id[dir as usize] = false;
+490            self.sent_max_remote[dir as usize] = self.max_remote[dir as usize];
+491            let count = self.max_remote[dir as usize];
+492            builder.write_frame(frame::MaxStreams { dir, count }, stats);
+493        }
+494    }
+495
+496    pub(in crate::connection) fn write_stream_frames<'a, 'b>(
+497        &mut self,
+498        builder: &mut PacketBuilder<'a, 'b>,
+499        fair: bool,
+500        stats: &mut FrameStats,
+501    ) {
+502        while builder.frame_space_remaining() > frame::Stream::SIZE_BOUND {
+503            // Pop the stream of the highest priority that currently has pending data. If
+504            // the stream still has some pending data left after writing, it will be
+505            // reinserted, otherwise not
+506            let Some(stream) = self.pending.pop() else {
+507                break;
+508            };
+509
+510            let id = stream.id;
+511
+512            let Some(stream) = self.send.get_mut(&id).and_then(|s| s.as_mut()) else {
+513                // Stream was reset with pending data and the reset was acknowledged
+514                continue;
+515            };
+516
+517            // Reset streams aren't removed from the pending list and still exist while the peer
+518            // hasn't acknowledged the reset, but should not generate STREAM frames, so we need to
+519            // check for them explicitly.
+520            if stream.is_reset() {
+521                continue;
+522            }
+523
+524            // Now that we know the `StreamId`, we can better account for how many bytes
+525            // are required to encode it.
+526            let max_buf_size = builder.frame_space_remaining() - 1 - VarInt::size(id.into());
+527            let (offsets, encode_length) = stream.pending.poll_transmit(max_buf_size);
+528            let fin = offsets.end == stream.pending.offset()
+529                && matches!(stream.state, SendState::DataSent { .. });
+530            if fin {
+531                stream.fin_pending = false;
+532            }
+533
+534            if stream.is_pending() {
+535                // If the stream still has pending data, reinsert it, possibly with an updated priority value
+536                // Fairness with other streams is achieved by implementing round-robin scheduling,
+537                // so that the other streams will have a chance to write data
+538                // before we touch this stream again.
+539                if fair {
+540                    self.pending.push_pending(id, stream.priority);
+541                } else {
+542                    self.pending.reinsert_pending(id, stream.priority);
+543                }
+544            }
+545
+546            let range = offsets.clone();
+547            let meta = frame::StreamMeta { id, offsets, fin };
+548            builder.write_frame(meta.encoder(encode_length), stats);
+549            stream.pending.get_into(range, builder.buf);
+550        }
+551    }
+552
+553    #[cfg(test)]
+554    fn write_frames_for_test(&mut self, capacity: usize, fair: bool) -> frame::StreamMetaVec {
+555        let buf = &mut Vec::with_capacity(capacity);
+556        let mut tbuf = crate::connection::TransmitBuf::new(buf, std::num::NonZeroUsize::MIN, 1_200);
+557        tbuf.start_new_datagram_with_size(capacity);
+558        let builder = &mut PacketBuilder::simple_data_buf(&mut tbuf);
+559        let stats = &mut FrameStats::default();
+560        self.write_stream_frames(builder, fair, stats);
+561        builder.sent_frames().stream_frames.clone()
+562    }
+563
+564    /// Notify the application that new streams were opened or a stream became readable.
+565    fn on_stream_frame(&mut self, notify_readable: bool, stream: StreamId) {
+566        if stream.initiator() == self.side {
+567            // Notifying about the opening of locally-initiated streams would be redundant.
+568            if notify_readable {
+569                self.events.push_back(StreamEvent::Readable { id: stream });
+570            }
+571            return;
+572        }
+573        let next = &mut self.next_remote[stream.dir() as usize];
+574        if stream.index() >= *next {
+575            *next = stream.index() + 1;
+576            self.opened[stream.dir() as usize] = true;
+577        } else if notify_readable {
+578            self.events.push_back(StreamEvent::Readable { id: stream });
+579        }
+580    }
+581
+582    pub(crate) fn received_ack_of(&mut self, frame: frame::StreamMeta) {
+583        let mut entry = match self.send.entry(frame.id) {
+584            hash_map::Entry::Vacant(_) => return,
+585            hash_map::Entry::Occupied(e) => e,
+586        };
+587
+588        let Some(stream) = entry.get_mut().as_mut() else {
+589            // Because we only call this after sending data on this stream,
+590            // this closure should be unreachable. If we did somehow screw that up,
+591            // then we might hit an underflow below with unpredictable effects down
+592            // the line. Best to short-circuit.
+593            return;
+594        };
+595
+596        if stream.is_reset() {
+597            // We account for outstanding data on reset streams at time of reset
+598            return;
+599        }
+600        let id = frame.id;
+601        self.unacked_data -= frame.offsets.end - frame.offsets.start;
+602        if !stream.ack(frame) {
+603            // The stream is unfinished or may still need retransmits
+604            return;
+605        }
+606
+607        entry.remove_entry();
+608        self.stream_freed(id, StreamHalf::Send);
+609        self.events.push_back(StreamEvent::Finished { id });
+610    }
+611
+612    pub(crate) fn retransmit(&mut self, frame: frame::StreamMeta) {
+613        let Some(stream) = self.send.get_mut(&frame.id).and_then(|s| s.as_mut()) else {
+614            // Loss of data on a closed stream is a noop
+615            return;
+616        };
+617        if !stream.is_pending() {
+618            self.pending.push_pending(frame.id, stream.priority);
+619        }
+620        stream.fin_pending |= frame.fin;
+621        stream.pending.retransmit(frame.offsets);
+622    }
+623
+624    pub(crate) fn retransmit_all_for_0rtt(&mut self) {
+625        for dir in Dir::iter() {
+626            for index in 0..self.next[dir as usize] {
+627                let id = StreamId::new(Side::Client, dir, index);
+628                let Some(stream) = self.send.get_mut(&id).and_then(|s| s.as_mut()) else {
+629                    continue;
+630                };
+631                if stream.pending.is_fully_acked() && !stream.fin_pending {
+632                    // Stream data can't be acked in 0-RTT, so we must not have sent anything on
+633                    // this stream
+634                    continue;
+635                }
+636                if !stream.is_pending() {
+637                    self.pending.push_pending(id, stream.priority);
+638                }
+639                stream.pending.retransmit_all_for_0rtt();
+640            }
+641        }
+642    }
+643
+644    pub(crate) fn received_max_streams(
+645        &mut self,
+646        dir: Dir,
+647        count: u64,
+648    ) -> Result<(), TransportError> {
+649        if count > MAX_STREAM_COUNT {
+650            return Err(TransportError::FRAME_ENCODING_ERROR(
+651                "unrepresentable stream limit",
+652            ));
+653        }
+654
+655        let current = &mut self.max[dir as usize];
+656        if count > *current {
+657            *current = count;
+658            self.events.push_back(StreamEvent::Available { dir });
+659        }
+660
+661        Ok(())
+662    }
+663
+664    /// Handle increase to connection-level flow control limit
+665    pub(crate) fn received_max_data(&mut self, n: VarInt) {
+666        self.max_data = self.max_data.max(n.into());
+667    }
+668
+669    pub(crate) fn received_max_stream_data(
+670        &mut self,
+671        id: StreamId,
+672        offset: u64,
+673    ) -> Result<(), TransportError> {
+674        if id.initiator() != self.side && id.dir() == Dir::Uni {
+675            debug!("got MAX_STREAM_DATA on recv-only {}", id);
+676            return Err(TransportError::STREAM_STATE_ERROR(
+677                "MAX_STREAM_DATA on recv-only stream",
+678            ));
+679        }
+680
+681        let write_limit = self.write_limit();
+682        let max_send_data = self.max_send_data(id);
+683        if let Some(ss) = self
+684            .send
+685            .get_mut(&id)
+686            .map(get_or_insert_send(max_send_data))
+687        {
+688            if ss.increase_max_data(offset) {
+689                if write_limit > 0 {
+690                    self.events.push_back(StreamEvent::Writable { id });
+691                } else if !ss.connection_blocked {
+692                    // The stream is still blocked on the connection flow control
+693                    // window. In order to get unblocked when the window relaxes
+694                    // it needs to be in the connection blocked list.
+695                    ss.connection_blocked = true;
+696                    self.connection_blocked.push(id);
+697                }
+698            }
+699        } else if id.initiator() == self.side && self.is_local_unopened(id) {
+700            debug!("got MAX_STREAM_DATA on unopened {}", id);
+701            return Err(TransportError::STREAM_STATE_ERROR(
+702                "MAX_STREAM_DATA on unopened stream",
+703            ));
+704        }
+705
+706        self.on_stream_frame(false, id);
+707        Ok(())
+708    }
+709
+710    /// Returns the maximum amount of data this is allowed to be written on the connection
+711    pub(crate) fn write_limit(&self) -> u64 {
+712        (self.max_data - self.data_sent)
+713            // `send_window` can be set after construction to something *less* than `unacked_data`
+714            .min(self.send_window.saturating_sub(self.unacked_data))
+715    }
+716
+717    /// Yield stream events
+718    pub(crate) fn poll(&mut self) -> Option<StreamEvent> {
+719        if let Some(dir) = Dir::iter().find(|&i| mem::replace(&mut self.opened[i as usize], false))
+720        {
+721            return Some(StreamEvent::Opened { dir });
+722        }
+723
+724        if self.write_limit() > 0 {
+725            while let Some(id) = self.connection_blocked.pop() {
+726                let Some(stream) = self.send.get_mut(&id).and_then(|s| s.as_mut()) else {
+727                    continue;
+728                };
+729
+730                debug_assert!(stream.connection_blocked);
+731                stream.connection_blocked = false;
+732
+733                // If it's no longer sensible to write to a stream (even to detect an error) then don't
+734                // report it.
+735                if stream.is_writable() && stream.max_data > stream.offset() {
+736                    return Some(StreamEvent::Writable { id });
+737                }
+738            }
+739        }
+740
+741        self.events.pop_front()
+742    }
+743
+744    /// Queues MAX_STREAM_ID frames in `pending` if needed
+745    ///
+746    /// Returns whether any frames were queued.
+747    pub(crate) fn queue_max_stream_id(&mut self, pending: &mut Retransmits) -> bool {
+748        let mut queued = false;
+749        for dir in Dir::iter() {
+750            let diff = self.max_remote[dir as usize] - self.sent_max_remote[dir as usize];
+751            // To reduce traffic, only announce updates if at least 1/8 of the flow control window
+752            // has been consumed.
+753            if diff > self.max_concurrent_remote_count[dir as usize] / 8 {
+754                pending.max_stream_id[dir as usize] = true;
+755                queued = true;
+756            }
+757        }
+758        queued
+759    }
+760
+761    /// Check for errors entailed by the peer's use of `id` as a send stream
+762    fn validate_receive_id(&mut self, id: StreamId) -> Result<(), TransportError> {
+763        if self.side == id.initiator() {
+764            match id.dir() {
+765                Dir::Uni => {
+766                    return Err(TransportError::STREAM_STATE_ERROR(
+767                        "illegal operation on send-only stream",
+768                    ));
+769                }
+770                Dir::Bi if id.index() >= self.next[Dir::Bi as usize] => {
+771                    return Err(TransportError::STREAM_STATE_ERROR(
+772                        "operation on unopened stream",
+773                    ));
+774                }
+775                Dir::Bi => {}
+776            };
+777        } else {
+778            let limit = self.max_remote[id.dir() as usize];
+779            if id.index() >= limit {
+780                return Err(TransportError::STREAM_LIMIT_ERROR(""));
+781            }
+782        }
+783        Ok(())
+784    }
+785
+786    /// Whether a locally initiated stream has never been open
+787    pub(crate) fn is_local_unopened(&self, id: StreamId) -> bool {
+788        id.index() >= self.next[id.dir() as usize]
+789    }
+790
+791    pub(crate) fn set_max_concurrent(&mut self, dir: Dir, count: VarInt) {
+792        self.flow_control_adjusted = true;
+793        self.max_concurrent_remote_count[dir as usize] = count.into();
+794        self.ensure_remote_streams(dir);
+795    }
+796
+797    pub(crate) fn max_concurrent(&self, dir: Dir) -> u64 {
+798        self.allocated_remote_count[dir as usize]
+799    }
+800
+801    pub(crate) fn set_send_window(&mut self, send_window: u64) {
+802        self.send_window = send_window;
+803    }
+804
+805    /// Set the receive_window and returns whether the receive_window has been
+806    /// expanded or shrunk: true if expanded, false if shrunk.
+807    pub(crate) fn set_receive_window(&mut self, receive_window: VarInt) -> bool {
+808        let receive_window = receive_window.into();
+809        let mut expanded = false;
+810        if receive_window > self.receive_window {
+811            self.local_max_data = self
+812                .local_max_data
+813                .saturating_add(receive_window - self.receive_window);
+814            expanded = true;
+815        } else {
+816            let diff = self.receive_window - receive_window;
+817            self.receive_window_shrink_debt = self.receive_window_shrink_debt.saturating_add(diff);
+818        }
+819        self.receive_window = receive_window;
+820        expanded
+821    }
+822
+823    pub(super) fn insert(&mut self, remote: bool, id: StreamId) {
+824        let bi = id.dir() == Dir::Bi;
+825        // bidirectional OR (unidirectional AND NOT remote)
+826        if bi || !remote {
+827            assert!(self.send.insert(id, None).is_none());
+828        }
+829        // bidirectional OR (unidirectional AND remote)
+830        if bi || remote {
+831            let recv = self.free_recv.pop();
+832            assert!(self.recv.insert(id, recv).is_none());
+833        }
+834    }
+835
+836    /// Adds credits to the connection flow control window
+837    ///
+838    /// Returns whether a `MAX_DATA` frame should be enqueued as soon as possible.
+839    /// This will only be the case if the window update would is significant
+840    /// enough. As soon as a window update with a `MAX_DATA` frame has been
+841    /// queued, the [`Recv::record_sent_max_stream_data`] function should be called to
+842    /// suppress sending further updates until the window increases significantly
+843    /// again.
+844    pub(super) fn add_read_credits(&mut self, credits: u64) -> ShouldTransmit {
+845        if credits > self.receive_window_shrink_debt {
+846            let net_credits = credits - self.receive_window_shrink_debt;
+847            self.local_max_data = self.local_max_data.saturating_add(net_credits);
+848            self.receive_window_shrink_debt = 0;
+849        } else {
+850            self.receive_window_shrink_debt -= credits;
+851        }
+852
+853        if self.local_max_data > VarInt::MAX.into_inner() {
+854            return ShouldTransmit(false);
+855        }
+856
+857        // Only announce a window update if it's significant enough
+858        // to make it worthwhile sending a MAX_DATA frame.
+859        // We use a fraction of the configured connection receive window to make
+860        // the decision, to accommodate for connection using bigger windows requiring
+861        // less updates.
+862        let diff = self.local_max_data - self.sent_max_data.into_inner();
+863        ShouldTransmit(diff >= (self.receive_window / 8))
+864    }
+865
+866    /// Update counters for removal of a stream
+867    pub(super) fn stream_freed(&mut self, id: StreamId, half: StreamHalf) {
+868        if id.initiator() != self.side {
+869            let fully_free = id.dir() == Dir::Uni
+870                || match half {
+871                    StreamHalf::Send => !self.recv.contains_key(&id),
+872                    StreamHalf::Recv => !self.send.contains_key(&id),
+873                };
+874            if fully_free {
+875                self.allocated_remote_count[id.dir() as usize] -= 1;
+876                self.ensure_remote_streams(id.dir());
+877            }
+878        }
+879        if half == StreamHalf::Send {
+880            self.send_streams -= 1;
+881        }
+882    }
+883
+884    pub(super) fn stream_recv_freed(&mut self, id: StreamId, recv: StreamRecv) {
+885        self.free_recv.push(recv.free(self.stream_receive_window));
+886        self.stream_freed(id, StreamHalf::Recv);
+887    }
+888
+889    pub(super) fn max_send_data(&self, id: StreamId) -> VarInt {
+890        let remote = self.side != id.initiator();
+891        match id.dir() {
+892            Dir::Uni => self.initial_max_stream_data_uni,
+893            // Remote/local appear reversed here because the transport parameters are named from
+894            // the perspective of the peer.
+895            Dir::Bi if remote => self.initial_max_stream_data_bidi_local,
+896            Dir::Bi => self.initial_max_stream_data_bidi_remote,
+897        }
+898    }
+899}
+900
+901#[inline]
+902pub(super) fn get_or_insert_send(
+903    max_data: VarInt,
+904) -> impl Fn(&mut Option<Box<Send>>) -> &mut Box<Send> {
+905    move |opt| opt.get_or_insert_with(|| Send::new(max_data))
+906}
+907
+908#[inline]
+909pub(super) fn get_or_insert_recv(
+910    initial_max_data: u64,
+911) -> impl FnMut(&mut Option<StreamRecv>) -> &mut Recv {
+912    move |opt| {
+913        *opt = opt.take().map(|s| match s {
+914            StreamRecv::Free(recv) => StreamRecv::Open(recv),
+915            s => s,
+916        });
+917        opt.get_or_insert_with(|| StreamRecv::Open(Recv::new(initial_max_data)))
+918            .as_open_recv_mut()
+919            .unwrap()
+920    }
+921}
+922
+923#[cfg(test)]
+924mod tests {
+925    use super::*;
+926    use crate::{
+927        ReadableError, RecvStream, SendStream, TransportErrorCode, WriteError,
+928        connection::State as ConnState, connection::Streams,
+929    };
+930    use bytes::Bytes;
+931
+932    fn make(side: Side) -> StreamsState {
+933        StreamsState::new(
+934            side,
+935            128u32.into(),
+936            128u32.into(),
+937            1024 * 1024,
+938            (1024 * 1024u32).into(),
+939            (1024 * 1024u32).into(),
+940        )
+941    }
+942
+943    #[test]
+944    fn trivial_flow_control() {
+945        let mut client = StreamsState::new(
+946            Side::Client,
+947            1u32.into(),
+948            1u32.into(),
+949            1024 * 1024,
+950            (1024 * 1024u32).into(),
+951            (1024 * 1024u32).into(),
+952        );
+953        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+954        let initial_max = client.local_max_data;
+955        const MESSAGE_SIZE: usize = 2048;
+956        assert_eq!(
+957            client
+958                .received(
+959                    frame::Stream {
+960                        id,
+961                        offset: 0,
+962                        fin: true,
+963                        data: Bytes::from_static(&[0; MESSAGE_SIZE]),
+964                    },
+965                    2048
+966                )
+967                .unwrap(),
+968            ShouldTransmit(false)
+969        );
+970        assert_eq!(client.data_recvd, 2048);
+971        assert_eq!(client.local_max_data - initial_max, 0);
+972
+973        let mut pending = Retransmits::default();
+974        let mut recv = RecvStream {
+975            id,
+976            state: &mut client,
+977            pending: &mut pending,
+978        };
+979
+980        let mut chunks = recv.read(true).unwrap();
+981        assert_eq!(
+982            chunks.next(MESSAGE_SIZE).unwrap().unwrap().bytes.len(),
+983            MESSAGE_SIZE
+984        );
+985        assert!(chunks.next(0).unwrap().is_none());
+986        let should_transmit = chunks.finalize();
+987        assert!(should_transmit.0);
+988        assert!(pending.max_stream_id[Dir::Uni as usize]);
+989        assert_eq!(client.local_max_data - initial_max, MESSAGE_SIZE as u64);
+990    }
+991
+992    #[test]
+993    fn reset_flow_control() {
+994        let mut client = make(Side::Client);
+995        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+996        let initial_max = client.local_max_data;
+997        assert_eq!(
+998            client
+999                .received(
+1000                    frame::Stream {
+1001                        id,
+1002                        offset: 0,
+1003                        fin: false,
+1004                        data: Bytes::from_static(&[0; 2048]),
+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        chunks.next(1024).unwrap();
+1023        let _ = chunks.finalize();
+1024        assert_eq!(client.local_max_data - initial_max, 1024);
+1025        assert_eq!(
+1026            client
+1027                .received_reset(frame::ResetStream {
+1028                    id,
+1029                    error_code: 0u32.into(),
+1030                    final_offset: 4096u32.into(),
+1031                })
+1032                .unwrap(),
+1033            ShouldTransmit(false)
+1034        );
+1035
+1036        assert_eq!(client.data_recvd, 4096);
+1037        assert_eq!(client.local_max_data - initial_max, 4096);
+1038
+1039        // Ensure reading after a reset doesn't issue redundant credit
+1040        let mut recv = RecvStream {
+1041            id,
+1042            state: &mut client,
+1043            pending: &mut pending,
+1044        };
+1045        let mut chunks = recv.read(true).unwrap();
+1046        assert_eq!(
+1047            chunks.next(1024).unwrap_err(),
+1048            crate::ReadError::Reset(0u32.into())
+1049        );
+1050        let _ = chunks.finalize();
+1051        assert_eq!(client.data_recvd, 4096);
+1052        assert_eq!(client.local_max_data - initial_max, 4096);
+1053    }
+1054
+1055    #[test]
+1056    fn reset_after_empty_frame_flow_control() {
+1057        let mut client = make(Side::Client);
+1058        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1059        let initial_max = client.local_max_data;
+1060        assert_eq!(
+1061            client
+1062                .received(
+1063                    frame::Stream {
+1064                        id,
+1065                        offset: 4096,
+1066                        fin: false,
+1067                        data: Bytes::from_static(&[0; 0]),
+1068                    },
+1069                    0
+1070                )
+1071                .unwrap(),
+1072            ShouldTransmit(false)
+1073        );
+1074        assert_eq!(client.data_recvd, 4096);
+1075        assert_eq!(client.local_max_data - initial_max, 0);
+1076        assert_eq!(
+1077            client
+1078                .received_reset(frame::ResetStream {
+1079                    id,
+1080                    error_code: 0u32.into(),
+1081                    final_offset: 4096u32.into(),
+1082                })
+1083                .unwrap(),
+1084            ShouldTransmit(false)
+1085        );
+1086        assert_eq!(client.data_recvd, 4096);
+1087        assert_eq!(client.local_max_data - initial_max, 4096);
+1088    }
+1089
+1090    #[test]
+1091    fn duplicate_reset_flow_control() {
+1092        let mut client = make(Side::Client);
+1093        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1094        assert_eq!(
+1095            client
+1096                .received_reset(frame::ResetStream {
+1097                    id,
+1098                    error_code: 0u32.into(),
+1099                    final_offset: 4096u32.into(),
+1100                })
+1101                .unwrap(),
+1102            ShouldTransmit(false)
+1103        );
+1104        assert_eq!(client.data_recvd, 4096);
+1105        assert_eq!(
+1106            client
+1107                .received_reset(frame::ResetStream {
+1108                    id,
+1109                    error_code: 0u32.into(),
+1110                    final_offset: 4096u32.into(),
+1111                })
+1112                .unwrap(),
+1113            ShouldTransmit(false)
+1114        );
+1115        assert_eq!(client.data_recvd, 4096);
+1116    }
+1117
+1118    #[test]
+1119    fn recv_stopped() {
+1120        let mut client = make(Side::Client);
+1121        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1122        let initial_max = client.local_max_data;
+1123        assert_eq!(
+1124            client
+1125                .received(
+1126                    frame::Stream {
+1127                        id,
+1128                        offset: 0,
+1129                        fin: false,
+1130                        data: Bytes::from_static(&[0; 32]),
+1131                    },
+1132                    32
+1133                )
+1134                .unwrap(),
+1135            ShouldTransmit(false)
+1136        );
+1137        assert_eq!(client.local_max_data, initial_max);
+1138
+1139        let mut pending = Retransmits::default();
+1140        let mut recv = RecvStream {
+1141            id,
+1142            state: &mut client,
+1143            pending: &mut pending,
+1144        };
+1145
+1146        recv.stop(0u32.into()).unwrap();
+1147        assert_eq!(recv.pending.stop_sending.len(), 1);
+1148        assert!(!recv.pending.max_data);
+1149
+1150        assert!(recv.stop(0u32.into()).is_err());
+1151        assert_eq!(recv.read(true).err(), Some(ReadableError::ClosedStream));
+1152        assert_eq!(recv.read(false).err(), Some(ReadableError::ClosedStream));
+1153
+1154        assert_eq!(client.local_max_data - initial_max, 32);
+1155        assert_eq!(
+1156            client
+1157                .received(
+1158                    frame::Stream {
+1159                        id,
+1160                        offset: 32,
+1161                        fin: true,
+1162                        data: Bytes::from_static(&[0; 16]),
+1163                    },
+1164                    16
+1165                )
+1166                .unwrap(),
+1167            ShouldTransmit(false)
+1168        );
+1169        assert_eq!(client.local_max_data - initial_max, 48);
+1170        assert!(!client.recv.contains_key(&id));
+1171    }
+1172
+1173    #[test]
+1174    fn stopped_reset() {
+1175        let mut client = make(Side::Client);
+1176        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1177        // Server opens stream
+1178        assert_eq!(
+1179            client
+1180                .received(
+1181                    frame::Stream {
+1182                        id,
+1183                        offset: 0,
+1184                        fin: false,
+1185                        data: Bytes::from_static(&[0; 32])
+1186                    },
+1187                    32
+1188                )
+1189                .unwrap(),
+1190            ShouldTransmit(false)
+1191        );
+1192
+1193        let mut pending = Retransmits::default();
+1194        let mut recv = RecvStream {
+1195            id,
+1196            state: &mut client,
+1197            pending: &mut pending,
+1198        };
+1199
+1200        recv.stop(0u32.into()).unwrap();
+1201        assert_eq!(pending.stop_sending.len(), 1);
+1202        assert!(!pending.max_data);
+1203
+1204        // Server complies
+1205        let prev_max = client.max_remote[Dir::Uni as usize];
+1206        assert_eq!(
+1207            client
+1208                .received_reset(frame::ResetStream {
+1209                    id,
+1210                    error_code: 0u32.into(),
+1211                    final_offset: 32u32.into(),
+1212                })
+1213                .unwrap(),
+1214            ShouldTransmit(false)
+1215        );
+1216        assert!(!client.recv.contains_key(&id), "stream state is freed");
+1217        assert_eq!(client.max_remote[Dir::Uni as usize], prev_max + 1);
+1218    }
+1219
+1220    #[test]
+1221    fn send_stopped() {
+1222        let mut server = make(Side::Server);
+1223        server.set_params(&TransportParameters {
+1224            initial_max_streams_uni: 1u32.into(),
+1225            initial_max_data: 42u32.into(),
+1226            initial_max_stream_data_uni: 42u32.into(),
+1227            ..TransportParameters::default()
+1228        });
+1229
+1230        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1231        let id = Streams {
+1232            state: &mut server,
+1233            conn_state: &state,
+1234        }
+1235        .open(Dir::Uni)
+1236        .unwrap();
+1237
+1238        let mut stream = SendStream {
+1239            id,
+1240            state: &mut server,
+1241            pending: &mut pending,
+1242            conn_state: &state,
+1243        };
+1244
+1245        let error_code = 0u32.into();
+1246        stream.state.received_stop_sending(id, error_code);
+1247        assert!(
+1248            stream
+1249                .state
+1250                .events
+1251                .contains(&StreamEvent::Stopped { id, error_code })
+1252        );
+1253        stream.state.events.clear();
+1254
+1255        assert_eq!(stream.write(&[]), Err(WriteError::Stopped(error_code)));
+1256
+1257        stream.reset(0u32.into()).unwrap();
+1258        assert_eq!(stream.write(&[]), Err(WriteError::ClosedStream));
+1259
+1260        // A duplicate frame is a no-op
+1261        stream.state.received_stop_sending(id, error_code);
+1262        assert!(stream.state.events.is_empty());
+1263    }
+1264
+1265    #[test]
+1266    fn final_offset_flow_control() {
+1267        let mut client = make(Side::Client);
+1268        assert_eq!(
+1269            client
+1270                .received_reset(frame::ResetStream {
+1271                    id: StreamId::new(Side::Server, Dir::Uni, 0),
+1272                    error_code: 0u32.into(),
+1273                    final_offset: VarInt::MAX,
+1274                })
+1275                .unwrap_err()
+1276                .code,
+1277            TransportErrorCode::FLOW_CONTROL_ERROR
+1278        );
+1279    }
+1280
+1281    #[test]
+1282    fn stream_priority() {
+1283        let mut server = make(Side::Server);
+1284        server.set_params(&TransportParameters {
+1285            initial_max_streams_bidi: 3u32.into(),
+1286            initial_max_data: 10u32.into(),
+1287            initial_max_stream_data_bidi_remote: 10u32.into(),
+1288            ..TransportParameters::default()
+1289        });
+1290
+1291        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1292        let mut streams = Streams {
+1293            state: &mut server,
+1294            conn_state: &state,
+1295        };
+1296
+1297        let id_high = streams.open(Dir::Bi).unwrap();
+1298        let id_mid = streams.open(Dir::Bi).unwrap();
+1299        let id_low = streams.open(Dir::Bi).unwrap();
+1300
+1301        let mut mid = SendStream {
+1302            id: id_mid,
+1303            state: &mut server,
+1304            pending: &mut pending,
+1305            conn_state: &state,
+1306        };
+1307        mid.write(b"mid").unwrap();
+1308
+1309        let mut low = SendStream {
+1310            id: id_low,
+1311            state: &mut server,
+1312            pending: &mut pending,
+1313            conn_state: &state,
+1314        };
+1315        low.set_priority(-1).unwrap();
+1316        low.write(b"low").unwrap();
+1317
+1318        let mut high = SendStream {
+1319            id: id_high,
+1320            state: &mut server,
+1321            pending: &mut pending,
+1322            conn_state: &state,
+1323        };
+1324        high.set_priority(1).unwrap();
+1325        high.write(b"high").unwrap();
+1326
+1327        let meta = server.write_frames_for_test(40, true);
+1328        assert_eq!(meta[0].id, id_high);
+1329        assert_eq!(meta[1].id, id_mid);
+1330        assert_eq!(meta[2].id, id_low);
+1331
+1332        assert!(!server.can_send_stream_data());
+1333        assert_eq!(server.pending.len(), 0);
+1334    }
+1335
+1336    #[test]
+1337    fn requeue_stream_priority() {
+1338        let mut server = make(Side::Server);
+1339        server.set_params(&TransportParameters {
+1340            initial_max_streams_bidi: 3u32.into(),
+1341            initial_max_data: 1000u32.into(),
+1342            initial_max_stream_data_bidi_remote: 1000u32.into(),
+1343            ..TransportParameters::default()
+1344        });
+1345
+1346        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1347        let mut streams = Streams {
+1348            state: &mut server,
+1349            conn_state: &state,
+1350        };
+1351
+1352        let id_high = streams.open(Dir::Bi).unwrap();
+1353        let id_mid = streams.open(Dir::Bi).unwrap();
+1354
+1355        let mut mid = SendStream {
+1356            id: id_mid,
+1357            state: &mut server,
+1358            pending: &mut pending,
+1359            conn_state: &state,
+1360        };
+1361        assert_eq!(mid.write(b"mid").unwrap(), 3);
+1362        assert_eq!(server.pending.len(), 1);
+1363
+1364        let mut high = SendStream {
+1365            id: id_high,
+1366            state: &mut server,
+1367            pending: &mut pending,
+1368            conn_state: &state,
+1369        };
+1370        high.set_priority(1).unwrap();
+1371        assert_eq!(high.write(&[0; 200]).unwrap(), 200);
+1372        assert_eq!(server.pending.len(), 2);
+1373
+1374        // Requeue the high priority stream to lowest priority. The initial send
+1375        // still uses high priority since it's queued that way. After that it will
+1376        // switch to low priority
+1377        let mut high = SendStream {
+1378            id: id_high,
+1379            state: &mut server,
+1380            pending: &mut pending,
+1381            conn_state: &state,
+1382        };
+1383        high.set_priority(-1).unwrap();
+1384
+1385        let meta = server.write_frames_for_test(40, true);
+1386        assert_eq!(meta.len(), 1);
+1387        assert_eq!(meta[0].id, id_high);
+1388
+1389        // After requeuing we should end up with 2 priorities - not 3
+1390        assert_eq!(server.pending.len(), 2);
+1391
+1392        // Send the remaining data. The initial mid priority one should go first now
+1393        let meta = server.write_frames_for_test(1000 - 40, true);
+1394        assert_eq!(meta.len(), 2);
+1395        assert_eq!(meta[0].id, id_mid);
+1396        assert_eq!(meta[1].id, id_high);
+1397
+1398        assert!(!server.can_send_stream_data());
+1399        assert_eq!(server.pending.len(), 0);
+1400    }
+1401
+1402    #[test]
+1403    fn same_stream_priority() {
+1404        for fair in [true, false] {
+1405            let mut server = make(Side::Server);
+1406            server.set_params(&TransportParameters {
+1407                initial_max_streams_bidi: 3u32.into(),
+1408                initial_max_data: 300u32.into(),
+1409                initial_max_stream_data_bidi_remote: 300u32.into(),
+1410                ..TransportParameters::default()
+1411            });
+1412
+1413            let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1414            let mut streams = Streams {
+1415                state: &mut server,
+1416                conn_state: &state,
+1417            };
+1418
+1419            // a, b and c all have the same priority
+1420            let id_a = streams.open(Dir::Bi).unwrap();
+1421            let id_b = streams.open(Dir::Bi).unwrap();
+1422            let id_c = streams.open(Dir::Bi).unwrap();
+1423
+1424            let mut stream_a = SendStream {
+1425                id: id_a,
+1426                state: &mut server,
+1427                pending: &mut pending,
+1428                conn_state: &state,
+1429            };
+1430            stream_a.write(&[b'a'; 100]).unwrap();
+1431
+1432            let mut stream_b = SendStream {
+1433                id: id_b,
+1434                state: &mut server,
+1435                pending: &mut pending,
+1436                conn_state: &state,
+1437            };
+1438            stream_b.write(&[b'b'; 100]).unwrap();
+1439
+1440            let mut stream_c = SendStream {
+1441                id: id_c,
+1442                state: &mut server,
+1443                pending: &mut pending,
+1444                conn_state: &state,
+1445            };
+1446            stream_c.write(&[b'c'; 100]).unwrap();
+1447
+1448            let mut metas = vec![];
+1449
+1450            // loop until all the streams are written
+1451            loop {
+1452                let meta = server.write_frames_for_test(40, fair);
+1453                if meta.is_empty() {
+1454                    break;
+1455                }
+1456                metas.extend(meta);
+1457            }
+1458
+1459            assert!(!server.can_send_stream_data());
+1460            assert_eq!(server.pending.len(), 0);
+1461
+1462            let stream_ids = metas.iter().map(|m| m.id).collect::<Vec<_>>();
+1463            if fair {
+1464                // When fairness is enabled, if we run out of buffer space to write out a stream,
+1465                // the stream is re-queued after all the streams with the same priority.
+1466                assert_eq!(
+1467                    stream_ids,
+1468                    vec![id_a, id_b, id_c, id_a, id_b, id_c, id_a, id_b, id_c]
+1469                );
+1470            } else {
+1471                // When fairness is disabled the stream is re-queued before all the other streams
+1472                // with the same priority.
+1473                assert_eq!(
+1474                    stream_ids,
+1475                    vec![id_a, id_a, id_a, id_b, id_b, id_b, id_c, id_c, id_c]
+1476                );
+1477            }
+1478        }
+1479    }
+1480
+1481    #[test]
+1482    fn unfair_priority_bump() {
+1483        let mut server = make(Side::Server);
+1484        server.set_params(&TransportParameters {
+1485            initial_max_streams_bidi: 3u32.into(),
+1486            initial_max_data: 300u32.into(),
+1487            initial_max_stream_data_bidi_remote: 300u32.into(),
+1488            ..TransportParameters::default()
+1489        });
+1490
+1491        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1492        let mut streams = Streams {
+1493            state: &mut server,
+1494            conn_state: &state,
+1495        };
+1496
+1497        // a, and b have the same priority, c has higher priority
+1498        let id_a = streams.open(Dir::Bi).unwrap();
+1499        let id_b = streams.open(Dir::Bi).unwrap();
+1500        let id_c = streams.open(Dir::Bi).unwrap();
+1501
+1502        let mut stream_a = SendStream {
+1503            id: id_a,
+1504            state: &mut server,
+1505            pending: &mut pending,
+1506            conn_state: &state,
+1507        };
+1508        stream_a.write(&[b'a'; 100]).unwrap();
+1509
+1510        let mut stream_b = SendStream {
+1511            id: id_b,
+1512            state: &mut server,
+1513            pending: &mut pending,
+1514            conn_state: &state,
+1515        };
+1516        stream_b.write(&[b'b'; 100]).unwrap();
+1517
+1518        let mut metas = vec![];
+1519
+1520        // Write the first chunk of stream_a
+1521        let meta = server.write_frames_for_test(40, false);
+1522        assert!(!meta.is_empty());
+1523        metas.extend(meta);
+1524
+1525        // Queue stream_c which has higher priority
+1526        let mut stream_c = SendStream {
+1527            id: id_c,
+1528            state: &mut server,
+1529            pending: &mut pending,
+1530            conn_state: &state,
+1531        };
+1532        stream_c.set_priority(1).unwrap();
+1533        stream_c.write(&[b'b'; 100]).unwrap();
+1534
+1535        // loop until all the streams are written
+1536        loop {
+1537            let meta = server.write_frames_for_test(40, false);
+1538            if meta.is_empty() {
+1539                break;
+1540            }
+1541            metas.extend(meta);
+1542        }
+1543
+1544        assert!(!server.can_send_stream_data());
+1545        assert_eq!(server.pending.len(), 0);
+1546
+1547        let stream_ids = metas.iter().map(|m| m.id).collect::<Vec<_>>();
+1548        assert_eq!(
+1549            stream_ids,
+1550            // stream_c bumps stream_b but doesn't bump stream_a which had already been partly
+1551            // written out
+1552            vec![id_a, id_a, id_a, id_c, id_c, id_c, id_b, id_b, id_b]
+1553        );
+1554    }
+1555
+1556    #[test]
+1557    fn stop_finished() {
+1558        let mut client = make(Side::Client);
+1559        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1560        // Server finishes stream
+1561        let _ = client
+1562            .received(
+1563                frame::Stream {
+1564                    id,
+1565                    offset: 0,
+1566                    fin: true,
+1567                    data: Bytes::from_static(&[0; 32]),
+1568                },
+1569                32,
+1570            )
+1571            .unwrap();
+1572        let mut pending = Retransmits::default();
+1573        let mut stream = RecvStream {
+1574            id,
+1575            state: &mut client,
+1576            pending: &mut pending,
+1577        };
+1578        stream.stop(0u32.into()).unwrap();
+1579        assert!(client.recv.get_mut(&id).is_none(), "stream is freed");
+1580    }
+1581
+1582    // Verify that a stream that's been reset doesn't cause the appearance of pending data
+1583    #[test]
+1584    fn reset_stream_cannot_send() {
+1585        let mut server = make(Side::Server);
+1586        server.set_params(&TransportParameters {
+1587            initial_max_streams_uni: 1u32.into(),
+1588            initial_max_data: 42u32.into(),
+1589            initial_max_stream_data_uni: 42u32.into(),
+1590            ..TransportParameters::default()
+1591        });
+1592        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1593        let mut streams = Streams {
+1594            state: &mut server,
+1595            conn_state: &state,
+1596        };
+1597
+1598        let id = streams.open(Dir::Uni).unwrap();
+1599        let mut stream = SendStream {
+1600            id,
+1601            state: &mut server,
+1602            pending: &mut pending,
+1603            conn_state: &state,
+1604        };
+1605        stream.write(b"hello").unwrap();
+1606        stream.reset(0u32.into()).unwrap();
+1607
+1608        assert_eq!(pending.reset_stream, &[(id, 0u32.into())]);
+1609        assert!(!server.can_send_stream_data());
+1610    }
+1611
+1612    #[test]
+1613    fn stream_limit_fixed() {
+1614        let mut client = make(Side::Client);
+1615        // Open streams 0-127
+1616        assert_eq!(
+1617            client.received(
+1618                frame::Stream {
+1619                    id: StreamId::new(Side::Server, Dir::Uni, 127),
+1620                    offset: 0,
+1621                    fin: true,
+1622                    data: Bytes::from_static(&[]),
+1623                },
+1624                0
+1625            ),
+1626            Ok(ShouldTransmit(false))
+1627        );
+1628        // Try to open stream 128, exceeding limit
+1629        assert_eq!(
+1630            client
+1631                .received(
+1632                    frame::Stream {
+1633                        id: StreamId::new(Side::Server, Dir::Uni, 128),
+1634                        offset: 0,
+1635                        fin: true,
+1636                        data: Bytes::from_static(&[]),
+1637                    },
+1638                    0
+1639                )
+1640                .unwrap_err()
+1641                .code,
+1642            TransportErrorCode::STREAM_LIMIT_ERROR
+1643        );
+1644
+1645        // Free stream 127
+1646        let mut pending = Retransmits::default();
+1647        let mut stream = RecvStream {
+1648            id: StreamId::new(Side::Server, Dir::Uni, 127),
+1649            state: &mut client,
+1650            pending: &mut pending,
+1651        };
+1652        stream.stop(0u32.into()).unwrap();
+1653
+1654        // Open stream 128
+1655        assert_eq!(
+1656            client.received(
+1657                frame::Stream {
+1658                    id: StreamId::new(Side::Server, Dir::Uni, 128),
+1659                    offset: 0,
+1660                    fin: true,
+1661                    data: Bytes::from_static(&[]),
+1662                },
+1663                0
+1664            ),
+1665            Ok(ShouldTransmit(false))
+1666        );
+1667    }
+1668
+1669    #[test]
+1670    fn stream_limit_grows() {
+1671        let mut client = make(Side::Client);
+1672        // Open streams 0-127
+1673        assert_eq!(
+1674            client.received(
+1675                frame::Stream {
+1676                    id: StreamId::new(Side::Server, Dir::Uni, 127),
+1677                    offset: 0,
+1678                    fin: true,
+1679                    data: Bytes::from_static(&[]),
+1680                },
+1681                0
+1682            ),
+1683            Ok(ShouldTransmit(false))
+1684        );
+1685        // Try to open stream 128, exceeding limit
+1686        assert_eq!(
+1687            client
+1688                .received(
+1689                    frame::Stream {
+1690                        id: StreamId::new(Side::Server, Dir::Uni, 128),
+1691                        offset: 0,
+1692                        fin: true,
+1693                        data: Bytes::from_static(&[]),
+1694                    },
+1695                    0
+1696                )
+1697                .unwrap_err()
+1698                .code,
+1699            TransportErrorCode::STREAM_LIMIT_ERROR
+1700        );
+1701
+1702        // Relax limit by one
+1703        client.set_max_concurrent(Dir::Uni, 129u32.into());
+1704
+1705        // Open stream 128
+1706        assert_eq!(
+1707            client.received(
+1708                frame::Stream {
+1709                    id: StreamId::new(Side::Server, Dir::Uni, 128),
+1710                    offset: 0,
+1711                    fin: true,
+1712                    data: Bytes::from_static(&[]),
+1713                },
+1714                0
+1715            ),
+1716            Ok(ShouldTransmit(false))
+1717        );
+1718    }
+1719
+1720    #[test]
+1721    fn stream_limit_shrinks() {
+1722        let mut client = make(Side::Client);
+1723        // Open streams 0-127
+1724        assert_eq!(
+1725            client.received(
+1726                frame::Stream {
+1727                    id: StreamId::new(Side::Server, Dir::Uni, 127),
+1728                    offset: 0,
+1729                    fin: true,
+1730                    data: Bytes::from_static(&[]),
+1731                },
+1732                0
+1733            ),
+1734            Ok(ShouldTransmit(false))
+1735        );
+1736
+1737        // Tighten limit by one
+1738        client.set_max_concurrent(Dir::Uni, 127u32.into());
+1739
+1740        // Free stream 127
+1741        let mut pending = Retransmits::default();
+1742        let mut stream = RecvStream {
+1743            id: StreamId::new(Side::Server, Dir::Uni, 127),
+1744            state: &mut client,
+1745            pending: &mut pending,
+1746        };
+1747        stream.stop(0u32.into()).unwrap();
+1748
+1749        // Try to open stream 128, still exceeding limit
+1750        assert_eq!(
+1751            client
+1752                .received(
+1753                    frame::Stream {
+1754                        id: StreamId::new(Side::Server, Dir::Uni, 128),
+1755                        offset: 0,
+1756                        fin: true,
+1757                        data: Bytes::from_static(&[]),
+1758                    },
+1759                    0
+1760                )
+1761                .unwrap_err()
+1762                .code,
+1763            TransportErrorCode::STREAM_LIMIT_ERROR
+1764        );
+1765
+1766        // Free stream 126
+1767        assert_eq!(
+1768            client.received_reset(frame::ResetStream {
+1769                id: StreamId::new(Side::Server, Dir::Uni, 126),
+1770                error_code: 0u32.into(),
+1771                final_offset: 0u32.into(),
+1772            }),
+1773            Ok(ShouldTransmit(false))
+1774        );
+1775        let mut pending = Retransmits::default();
+1776        let mut stream = RecvStream {
+1777            id: StreamId::new(Side::Server, Dir::Uni, 126),
+1778            state: &mut client,
+1779            pending: &mut pending,
+1780        };
+1781        stream.stop(0u32.into()).unwrap();
+1782
+1783        // Open stream 128
+1784        assert_eq!(
+1785            client.received(
+1786                frame::Stream {
+1787                    id: StreamId::new(Side::Server, Dir::Uni, 128),
+1788                    offset: 0,
+1789                    fin: true,
+1790                    data: Bytes::from_static(&[]),
+1791                },
+1792                0
+1793            ),
+1794            Ok(ShouldTransmit(false))
+1795        );
+1796    }
+1797
+1798    #[test]
+1799    fn remote_stream_capacity() {
+1800        let mut client = make(Side::Client);
+1801        for _ in 0..2 {
+1802            client.set_max_concurrent(Dir::Uni, 200u32.into());
+1803            client.set_max_concurrent(Dir::Bi, 201u32.into());
+1804            assert_eq!(client.recv.len(), 200 + 201);
+1805            assert_eq!(client.max_remote[Dir::Uni as usize], 200);
+1806            assert_eq!(client.max_remote[Dir::Bi as usize], 201);
+1807        }
+1808    }
+1809
+1810    #[test]
+1811    fn expand_receive_window() {
+1812        let mut server = make(Side::Server);
+1813        let new_receive_window = 2 * server.receive_window as u32;
+1814        let expanded = server.set_receive_window(new_receive_window.into());
+1815        assert!(expanded);
+1816        assert_eq!(server.receive_window, new_receive_window as u64);
+1817        assert_eq!(server.local_max_data, new_receive_window as u64);
+1818        assert_eq!(server.receive_window_shrink_debt, 0);
+1819        let prev_local_max_data = server.local_max_data;
+1820
+1821        // credit, expecting all of them added to local_max_data
+1822        let credits = 1024u64;
+1823        let should_transmit = server.add_read_credits(credits);
+1824        assert_eq!(server.receive_window_shrink_debt, 0);
+1825        assert_eq!(server.local_max_data, prev_local_max_data + credits);
+1826        assert!(should_transmit.should_transmit());
+1827    }
+1828
+1829    #[test]
+1830    fn shrink_receive_window() {
+1831        let mut server = make(Side::Server);
+1832        let new_receive_window = server.receive_window as u32 / 2;
+1833        let prev_local_max_data = server.local_max_data;
+1834
+1835        // shrink the receive_winbow, local_max_data is not expected to be changed
+1836        let shrink_diff = server.receive_window - new_receive_window as u64;
+1837        let expanded = server.set_receive_window(new_receive_window.into());
+1838        assert!(!expanded);
+1839        assert_eq!(server.receive_window, new_receive_window as u64);
+1840        assert_eq!(server.local_max_data, prev_local_max_data);
+1841        assert_eq!(server.receive_window_shrink_debt, shrink_diff);
+1842        let prev_local_max_data = server.local_max_data;
+1843
+1844        // credit twice, local_max_data does not change as it is absorbed by receive_window_shrink_debt
+1845        let credits = 1024u64;
+1846        for _ in 0..2 {
+1847            let expected_receive_window_shrink_debt = server.receive_window_shrink_debt - credits;
+1848            let should_transmit = server.add_read_credits(credits);
+1849            assert_eq!(
+1850                server.receive_window_shrink_debt,
+1851                expected_receive_window_shrink_debt
+1852            );
+1853            assert_eq!(server.local_max_data, prev_local_max_data);
+1854            assert!(!should_transmit.should_transmit());
+1855        }
+1856
+1857        // credit again which exceeds all remaining expected_receive_window_shrink_debt
+1858        let credits = 1024 * 512;
+1859        let prev_local_max_data = server.local_max_data;
+1860        let expected_local_max_data =
+1861            server.local_max_data + (credits - server.receive_window_shrink_debt);
+1862        let _should_transmit = server.add_read_credits(credits);
+1863        assert_eq!(server.receive_window_shrink_debt, 0);
+1864        assert_eq!(server.local_max_data, expected_local_max_data);
+1865        assert!(server.local_max_data > prev_local_max_data);
+1866
+1867        // credit again, all should be added to local_max_data
+1868        let credits = 1024 * 512;
+1869        let expected_local_max_data = server.local_max_data + credits;
+1870        let should_transmit = server.add_read_credits(credits);
+1871        assert_eq!(server.receive_window_shrink_debt, 0);
+1872        assert_eq!(server.local_max_data, expected_local_max_data);
+1873        assert!(should_transmit.should_transmit());
+1874    }
+1875
+1876    #[test]
+1877    fn expand_send_window() {
+1878        let mut server = make(Side::Server);
+1879
+1880        let initial_send_window = server.send_window;
+1881        let larger_send_window = initial_send_window * 2;
+1882
+1883        // Set `initial_max_data` larger than `send_window` so we're limited by local flow control
+1884        server.set_params(&TransportParameters {
+1885            initial_max_data: VarInt::MAX,
+1886            initial_max_stream_data_uni: VarInt::MAX,
+1887            initial_max_streams_uni: VarInt::from_u32(100),
+1888            ..TransportParameters::default()
+1889        });
+1890
+1891        assert_eq!(server.write_limit(), initial_send_window);
+1892        assert_eq!(server.poll(), None);
+1893
+1894        let mut retransmits = Retransmits::default();
+1895        let conn_state = ConnState::established();
+1896
+1897        let stream_id = Streams {
+1898            state: &mut server,
+1899            conn_state: &conn_state,
+1900        }
+1901        .open(Dir::Uni)
+1902        .expect("should be able to open a stream");
+1903
+1904        let mut stream = SendStream {
+1905            id: stream_id,
+1906            state: &mut server,
+1907            pending: &mut retransmits,
+1908            conn_state: &conn_state,
+1909        };
+1910
+1911        // Check that the stream accepts `initial_send_window` bytes
+1912        let initial_send_len = initial_send_window as usize;
+1913        let data = vec![0xFFu8; initial_send_len];
+1914
+1915        assert_eq!(stream.write(&data), Ok(initial_send_len));
+1916
+1917        // Try to write the same data again, observe that it's blocked
+1918        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+1919
+1920        // Check that we get a `Writable` event after increasing the send window
+1921        stream.state.set_send_window(larger_send_window);
+1922        assert_eq!(
+1923            stream.state.poll(),
+1924            Some(StreamEvent::Writable { id: stream_id })
+1925        );
+1926
+1927        // Check that the stream accepts the exact same amount of data again
+1928        assert_eq!(stream.write(&data), Ok(initial_send_len));
+1929        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+1930
+1931        assert_eq!(stream.state.poll(), None);
+1932
+1933        // Ack the data
+1934        stream.state.received_ack_of(frame::StreamMeta {
+1935            id: stream_id,
+1936            offsets: 0..larger_send_window,
+1937            fin: false,
+1938        });
+1939
+1940        assert_eq!(
+1941            stream.state.poll(),
+1942            Some(StreamEvent::Writable { id: stream_id })
+1943        );
+1944
+1945        // Check that our full send window is available again
+1946        assert_eq!(stream.write(&data), Ok(initial_send_len));
+1947        assert_eq!(stream.write(&data), Ok(initial_send_len));
+1948        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+1949    }
+1950
+1951    #[test]
+1952    fn shrink_send_window() {
+1953        let mut server = make(Side::Server);
+1954
+1955        let initial_send_window = server.send_window;
+1956        let smaller_send_window = server.send_window / 2;
+1957
+1958        // Set `initial_max_data` larger than `send_window` so we're limited by local flow control
+1959        server.set_params(&TransportParameters {
+1960            initial_max_data: VarInt::MAX,
+1961            initial_max_stream_data_uni: VarInt::MAX,
+1962            initial_max_streams_uni: VarInt::from_u32(100),
+1963            ..TransportParameters::default()
+1964        });
+1965
+1966        assert_eq!(server.write_limit(), initial_send_window);
+1967        assert_eq!(server.poll(), None);
+1968
+1969        let mut retransmits = Retransmits::default();
+1970        let conn_state = ConnState::established();
+1971
+1972        let stream_id = Streams {
+1973            state: &mut server,
+1974            conn_state: &conn_state,
+1975        }
+1976        .open(Dir::Uni)
+1977        .expect("should be able to open a stream");
+1978
+1979        let mut stream = SendStream {
+1980            id: stream_id,
+1981            state: &mut server,
+1982            pending: &mut retransmits,
+1983            conn_state: &conn_state,
+1984        };
+1985
+1986        let initial_send_len = initial_send_window as usize;
+1987
+1988        let data = vec![0xFFu8; initial_send_len];
+1989
+1990        // Assert that the full send window is accepted
+1991        assert_eq!(stream.write(&data), Ok(initial_send_len));
+1992        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+1993
+1994        assert_eq!(stream.state.write_limit(), 0);
+1995        assert_eq!(stream.state.poll(), None);
+1996
+1997        // Shrink our send window, assert that it's still not writable
+1998        stream.state.set_send_window(smaller_send_window);
+1999        assert_eq!(stream.state.write_limit(), 0);
+2000        assert_eq!(stream.state.poll(), None);
+2001
+2002        // Assert that data is still not accepted
+2003        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2004
+2005        // Ack some data, assert that writes are still not accepted due to outstanding sends
+2006        stream.state.received_ack_of(frame::StreamMeta {
+2007            id: stream_id,
+2008            offsets: 0..smaller_send_window,
+2009            fin: false,
+2010        });
+2011
+2012        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2013
+2014        // Ack the rest of the data
+2015        stream.state.received_ack_of(frame::StreamMeta {
+2016            id: stream_id,
+2017            offsets: smaller_send_window..initial_send_window,
+2018            fin: false,
+2019        });
+2020
+2021        // This should generate a `Writable` event
+2022        assert_eq!(
+2023            stream.state.poll(),
+2024            Some(StreamEvent::Writable { id: stream_id })
+2025        );
+2026        assert_eq!(stream.state.write_limit(), smaller_send_window);
+2027
+2028        // Assert that only `smaller_send_window` bytes are accepted
+2029        assert_eq!(stream.write(&data), Ok(smaller_send_window as usize));
+2030        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2031    }
+2032}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/connection/timer.rs.html b/pr/509/docs/src/noq_proto/connection/timer.rs.html new file mode 100644 index 000000000..2bc17531b --- /dev/null +++ b/pr/509/docs/src/noq_proto/connection/timer.rs.html @@ -0,0 +1,498 @@ +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}
+31
+32impl ConnTimer {
+33    const VALUES: [Self; 5] = [
+34        Self::Idle,
+35        Self::Close,
+36        Self::KeyDiscard,
+37        Self::KeepAlive,
+38        Self::PushNewCid,
+39    ];
+40}
+41
+42#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
+43pub(crate) enum PathTimer {
+44    /// When to send an ack-eliciting probe packet or declare unacked packets lost
+45    LossDetection = 0,
+46    /// When to abandon a path after no activity
+47    PathIdle = 1,
+48    /// When to give up on validating a new path from RFC9000 migration
+49    PathValidation = 2,
+50    /// When to resend an on-path path challenge deemed lost
+51    PathChallengeLost = 3,
+52    /// When to give up on validating a new (multi)path
+53    PathOpen = 4,
+54    /// When to send a `PING` frame to keep the path alive
+55    PathKeepAlive = 5,
+56    /// When pacing will allow us to send a packet
+57    Pacing = 6,
+58    /// When to send an immediate ACK if there are unacked ack-eliciting packets of the peer
+59    MaxAckDelay = 7,
+60    /// When to clean up state for an abandoned path
+61    DiscardPath = 8,
+62}
+63
+64impl PathTimer {
+65    pub(super) const VALUES: [Self; 9] = [
+66        Self::LossDetection,
+67        Self::PathIdle,
+68        Self::PathValidation,
+69        Self::PathChallengeLost,
+70        Self::PathOpen,
+71        Self::PathKeepAlive,
+72        Self::Pacing,
+73        Self::MaxAckDelay,
+74        Self::DiscardPath,
+75    ];
+76}
+77
+78/// Keeps track of the nearest timeout for each `Timer`
+79///
+80/// The [`TimerTable`] is advanced with [`TimerTable::expire_before`].
+81#[derive(Debug, Clone, Default)]
+82pub(crate) struct TimerTable {
+83    generic: [Option<Instant>; ConnTimer::VALUES.len()],
+84    path_timers: SmallMap<PathId, PathTimerTable, STACK_TIMERS>,
+85}
+86
+87/// For how many paths we keep the timers on the stack, before spilling onto the heap.
+88const STACK_TIMERS: usize = 4;
+89
+90/// Works like a `HashMap` but stores up to `SIZE` items on the stack.
+91#[derive(Debug, Clone)]
+92struct SmallMap<K, V, const SIZE: usize> {
+93    stack: [Option<(K, V)>; SIZE],
+94    heap: Option<IntMap<K, V>>,
+95}
+96
+97impl<K, V, const SIZE: usize> Default for SmallMap<K, V, SIZE> {
+98    fn default() -> Self {
+99        Self {
+100            stack: [const { None }; SIZE],
+101            heap: None,
+102        }
+103    }
+104}
+105
+106impl<K, V, const SIZE: usize> SmallMap<K, V, SIZE>
+107where
+108    K: std::cmp::Eq + std::hash::Hash + IdentityHashable,
+109{
+110    fn insert(&mut self, key: K, value: V) -> Option<V> {
+111        // check stack for space
+112        for el in self.stack.iter_mut() {
+113            match el {
+114                Some((k, v)) => {
+115                    if *k == key {
+116                        let old_value = std::mem::replace(v, value);
+117                        return Some(old_value);
+118                    }
+119                }
+120                None => {
+121                    // make sure to remove a potentially old value from the heap
+122                    let old_heap = self.heap.as_mut().and_then(|h| h.remove(&key));
+123                    *el = Some((key, value));
+124
+125                    return old_heap;
+126                }
+127            }
+128        }
+129
+130        // No space on the stack, use the heap
+131        let heap = self.heap.get_or_insert_default();
+132        heap.insert(key, value)
+133    }
+134
+135    #[cfg(test)]
+136    fn remove(&mut self, key: &K) -> Option<V> {
+137        for el in self.stack.iter_mut() {
+138            if let Some((k, _)) = el
+139                && key == k
+140            {
+141                return el.take().map(|(_, v)| v);
+142            }
+143        }
+144
+145        self.heap.as_mut().and_then(|h| h.remove(key))
+146    }
+147
+148    #[cfg(test)]
+149    fn get(&self, key: &K) -> Option<&V> {
+150        for (k, v) in self.stack.iter().filter_map(|v| v.as_ref()) {
+151            if k == key {
+152                return Some(v);
+153            }
+154        }
+155
+156        self.heap.as_ref().and_then(|h| h.get(key))
+157    }
+158
+159    fn get_mut(&mut self, key: &K) -> Option<&mut V> {
+160        for (k, v) in self.stack.iter_mut().filter_map(|v| v.as_mut()) {
+161            if k == key {
+162                return Some(v);
+163            }
+164        }
+165
+166        self.heap.as_mut().and_then(|h| h.get_mut(key))
+167    }
+168
+169    #[cfg(test)]
+170    fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
+171        let a = self
+172            .stack
+173            .iter()
+174            .filter_map(|v| v.as_ref().map(|(k, v)| (k, v)));
+175        let b = self.heap.iter().flat_map(|h| h.iter());
+176        a.chain(b)
+177    }
+178
+179    fn values(&self) -> impl Iterator<Item = &V> {
+180        let a = self.stack.iter().filter_map(|v| v.as_ref().map(|(_, v)| v));
+181        let b = self.heap.iter().flat_map(|h| h.values());
+182        a.chain(b)
+183    }
+184
+185    fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
+186        let a = self
+187            .stack
+188            .iter_mut()
+189            .filter_map(|v| v.as_mut().map(|(k, v)| (&*k, v)));
+190        let b = self.heap.iter_mut().flat_map(|h| h.iter_mut());
+191        a.chain(b)
+192    }
+193
+194    fn retain<F>(&mut self, mut f: F)
+195    where
+196        F: FnMut(&K, &mut V) -> bool,
+197    {
+198        let mut to_remove = [false; SIZE];
+199        for (i, el) in self.stack.iter_mut().enumerate() {
+200            if let Some((key, value)) = el {
+201                to_remove[i] = !f(key, value);
+202            }
+203        }
+204        for (i, to_remove) in to_remove.into_iter().enumerate() {
+205            if to_remove {
+206                self.stack[i] = None;
+207            }
+208        }
+209
+210        if let Some(ref mut heap) = self.heap {
+211            heap.retain(f);
+212        }
+213    }
+214
+215    fn clear(&mut self) {
+216        for el in self.stack.iter_mut() {
+217            *el = None;
+218        }
+219        self.heap = None;
+220    }
+221}
+222
+223#[derive(Debug, Clone, Copy, Default)]
+224struct PathTimerTable {
+225    timers: [Option<Instant>; PathTimer::VALUES.len()],
+226}
+227
+228impl PathTimerTable {
+229    fn set(&mut self, timer: PathTimer, time: Instant) {
+230        self.timers[timer as usize] = Some(time);
+231    }
+232
+233    fn stop(&mut self, timer: PathTimer) {
+234        self.timers[timer as usize] = None;
+235    }
+236
+237    /// Remove the next timer up until `now`, including it
+238    fn expire_before(&mut self, now: Instant) -> Option<(PathTimer, Instant)> {
+239        for timer in PathTimer::VALUES {
+240            if self.timers[timer as usize].is_some()
+241                && self.timers[timer as usize].expect("checked") <= now
+242            {
+243                return self.timers[timer as usize].take().map(|time| (timer, time));
+244            }
+245        }
+246
+247        None
+248    }
+249}
+250
+251impl TimerTable {
+252    /// Sets the timer unconditionally.
+253    ///
+254    /// If the timer is already set, this will change the timer's value.
+255    pub(super) fn set(&mut self, timer: Timer, time: Instant, qlog: QlogSinkWithTime<'_>) {
+256        match timer {
+257            Timer::Conn(timer) => {
+258                self.generic[timer as usize] = Some(time);
+259            }
+260            Timer::PerPath(path_id, timer) => match self.path_timers.get_mut(&path_id) {
+261                None => {
+262                    let mut table = PathTimerTable::default();
+263                    table.set(timer, time);
+264                    self.path_timers.insert(path_id, table);
+265                }
+266                Some(table) => {
+267                    table.set(timer, time);
+268                }
+269            },
+270        }
+271        qlog.emit_timer_set(timer, time);
+272    }
+273
+274    pub(super) fn set_or_stop(
+275        &mut self,
+276        timer: Timer,
+277        time: Option<Instant>,
+278        qlog: QlogSinkWithTime<'_>,
+279    ) {
+280        match time {
+281            Some(time) => self.set(timer, time, qlog),
+282            None => self.stop(timer, qlog),
+283        }
+284    }
+285
+286    pub(super) fn stop(&mut self, timer: Timer, qlog: QlogSinkWithTime<'_>) {
+287        match timer {
+288            Timer::Conn(timer) => {
+289                self.generic[timer as usize] = None;
+290            }
+291            Timer::PerPath(path_id, timer) => {
+292                if let Some(e) = self.path_timers.get_mut(&path_id) {
+293                    e.stop(timer);
+294                }
+295            }
+296        }
+297        qlog.emit_timer_stop(timer);
+298    }
+299
+300    /// Stops all per-path timers
+301    pub(super) fn stop_per_path(&mut self, path_id: PathId, qlog: QlogSinkWithTime<'_>) {
+302        for timer in PathTimer::VALUES {
+303            if let Some(e) = self.path_timers.get_mut(&path_id) {
+304                e.stop(timer);
+305                qlog.emit_timer_stop(Timer::PerPath(path_id, timer));
+306            }
+307        }
+308    }
+309
+310    /// Get the next queued timeout
+311    pub(super) fn peek(&mut self) -> Option<Instant> {
+312        // TODO: this is currently linear in the number of paths
+313
+314        let min_generic = self.generic.iter().filter_map(|&x| x).min();
+315        let min_path = self
+316            .path_timers
+317            .values()
+318            .flat_map(|p| p.timers.iter().filter_map(|&x| x))
+319            .min();
+320
+321        match (min_generic, min_path) {
+322            (None, None) => None,
+323            (Some(val), None) => Some(val),
+324            (Some(a), Some(b)) => Some(a.min(b)),
+325            (None, Some(val)) => Some(val),
+326        }
+327    }
+328
+329    /// Remove the next timer up until `now`, including it
+330    pub(super) fn expire_before(
+331        &mut self,
+332        now: Instant,
+333        qlog: &QlogSink,
+334    ) -> Option<(Timer, Instant)> {
+335        let (timer, instant) = self.expire_before_inner(now)?;
+336        qlog.with_time(now).emit_timer_expire(timer);
+337        Some((timer, instant))
+338    }
+339
+340    fn expire_before_inner(&mut self, now: Instant) -> Option<(Timer, Instant)> {
+341        // TODO: this is currently linear in the number of paths
+342
+343        for timer in ConnTimer::VALUES {
+344            if self.generic[timer as usize].is_some()
+345                && self.generic[timer as usize].expect("checked") <= now
+346            {
+347                return self.generic[timer as usize]
+348                    .take()
+349                    .map(|time| (Timer::Conn(timer), time));
+350            }
+351        }
+352
+353        let mut res = None;
+354        for (path_id, timers) in self.path_timers.iter_mut() {
+355            if let Some((timer, time)) = timers.expire_before(now) {
+356                res = Some((Timer::PerPath(*path_id, timer), time));
+357                break;
+358            }
+359        }
+360
+361        // clear out old timers
+362        self.path_timers
+363            .retain(|_path_id, timers| timers.timers.iter().any(|t| t.is_some()));
+364        res
+365    }
+366
+367    pub(super) fn reset(&mut self) {
+368        for timer in ConnTimer::VALUES {
+369            self.generic[timer as usize] = None;
+370        }
+371        self.path_timers.clear();
+372    }
+373
+374    #[cfg(test)]
+375    pub(super) fn values(&self) -> Vec<(Timer, Instant)> {
+376        let mut values = Vec::new();
+377
+378        for timer in ConnTimer::VALUES {
+379            if let Some(time) = self.generic[timer as usize] {
+380                values.push((Timer::Conn(timer), time));
+381            }
+382        }
+383
+384        for timer in PathTimer::VALUES {
+385            for (path_id, timers) in self.path_timers.iter() {
+386                if let Some(time) = timers.timers[timer as usize] {
+387                    values.push((Timer::PerPath(*path_id, timer), time));
+388                }
+389            }
+390        }
+391
+392        values
+393    }
+394}
+395
+396#[cfg(test)]
+397mod tests {
+398    use std::time::Duration;
+399
+400    use crate::connection::qlog::QlogSink;
+401
+402    use super::*;
+403
+404    #[test]
+405    fn timer_table() {
+406        let mut timers = TimerTable::default();
+407        let sec = Duration::from_secs(1);
+408        let now = Instant::now() + Duration::from_secs(10);
+409        timers.set(
+410            Timer::Conn(ConnTimer::Idle),
+411            now - 3 * sec,
+412            QlogSink::default().with_time(now),
+413        );
+414        timers.set(
+415            Timer::Conn(ConnTimer::Close),
+416            now - 2 * sec,
+417            QlogSink::default().with_time(now),
+418        );
+419
+420        assert_eq!(timers.peek(), Some(now - 3 * sec));
+421        assert_eq!(
+422            timers.expire_before(now, &QlogSink::default()),
+423            Some((Timer::Conn(ConnTimer::Idle), now - 3 * sec))
+424        );
+425        assert_eq!(
+426            timers.expire_before(now, &QlogSink::default()),
+427            Some((Timer::Conn(ConnTimer::Close), now - 2 * sec))
+428        );
+429        assert_eq!(timers.expire_before(now, &QlogSink::default()), None);
+430    }
+431
+432    #[test]
+433    fn test_small_map() {
+434        let mut map = SmallMap::<usize, usize, 2>::default();
+435
+436        // inserts only on the stack
+437        assert_eq!(map.insert(1, 1), None);
+438        assert!(map.heap.is_none());
+439        assert_eq!(map.insert(2, 2), None);
+440        assert!(map.heap.is_none());
+441
+442        // replace on the stack
+443        assert_eq!(map.insert(1, 2), Some(1));
+444
+445        assert_eq!(map.remove(&1), Some(2));
+446        assert_eq!(map.insert(3, 3), None);
+447        assert!(map.heap.is_none());
+448
+449        // spill
+450        assert_eq!(map.insert(4, 4), None);
+451        assert!(map.heap.is_some());
+452
+453        assert_eq!(
+454            map.iter()
+455                .map(|(&a, &b)| (a, b))
+456                .collect::<Vec<(usize, usize)>>(),
+457            vec![(3, 3), (2, 2), (4, 4)]
+458        );
+459        assert_eq!(
+460            map.iter()
+461                .map(|(a, b)| (*a, *b))
+462                .collect::<Vec<(usize, usize)>>(),
+463            map.iter_mut()
+464                .map(|(a, b)| (*a, *b))
+465                .collect::<Vec<(usize, usize)>>(),
+466        );
+467
+468        assert_eq!(map.heap.as_ref().unwrap().len(), 1);
+469
+470        for i in 0..10 {
+471            map.insert(10 + i, 10 + i);
+472        }
+473        assert_eq!(map.heap.as_ref().unwrap().len(), 11);
+474        map.retain(|k, _v| *k < 10);
+475
+476        assert_eq!(map.heap.as_ref().unwrap().len(), 1);
+477
+478        assert_eq!(
+479            map.iter()
+480                .map(|(&a, &b)| (a, b))
+481                .collect::<Vec<(usize, usize)>>(),
+482            vec![(3, 3), (2, 2), (4, 4)]
+483        );
+484
+485        assert_eq!(
+486            map.iter()
+487                .map(|(a, b)| (*a, *b))
+488                .collect::<Vec<(usize, usize)>>(),
+489            map.iter_mut()
+490                .map(|(a, b)| (*a, *b))
+491                .collect::<Vec<(usize, usize)>>(),
+492        );
+493
+494        map.clear();
+495        assert_eq!(map.iter().collect::<Vec<_>>(), Vec::new());
+496        assert!(map.heap.is_none());
+497    }
+498}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/connection/transmit_buf.rs.html b/pr/509/docs/src/noq_proto/connection/transmit_buf.rs.html new file mode 100644 index 000000000..2f08d5b01 --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/constant_time.rs.html b/pr/509/docs/src/noq_proto/constant_time.rs.html new file mode 100644 index 000000000..e31abcc37 --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/crypto.rs.html b/pr/509/docs/src/noq_proto/crypto.rs.html new file mode 100644 index 000000000..1ca777d86 --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/crypto/ring_like.rs.html b/pr/509/docs/src/noq_proto/crypto/ring_like.rs.html new file mode 100644 index 000000000..5176f2dc0 --- /dev/null +++ b/pr/509/docs/src/noq_proto/crypto/ring_like.rs.html @@ -0,0 +1,77 @@ +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, error, hkdf, hmac};
+3#[cfg(feature = "ring")]
+4use ring::{aead, error, hkdf, hmac};
+5
+6use crate::crypto::{self, CryptoError};
+7
+8impl crypto::HmacKey for hmac::Key {
+9    fn sign(&self, data: &[u8], out: &mut [u8]) {
+10        out.copy_from_slice(hmac::sign(self, data).as_ref());
+11    }
+12
+13    fn signature_len(&self) -> usize {
+14        32
+15    }
+16
+17    fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError> {
+18        Ok(hmac::verify(self, data, signature)?)
+19    }
+20}
+21
+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.seal_in_place_append_tag(nonce, aad, data)?;
+62        Ok(())
+63    }
+64
+65    fn open<'a>(&self, token_nonce: u128, data: &'a mut [u8]) -> Result<&'a [u8], CryptoError> {
+66        let aead_key = self.derive_aead(token_nonce);
+67        let aad = aead::Aad::empty();
+68        let nonce = aead::Nonce::assume_unique_for_key([0u8; 12]); // See docs for RetryTokenKey
+69        Ok(aead_key.open_in_place(nonce, aad, data)?)
+70    }
+71}
+72
+73impl From<error::Unspecified> for CryptoError {
+74    fn from(_: error::Unspecified) -> Self {
+75        Self
+76    }
+77}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/crypto/rustls.rs.html b/pr/509/docs/src/noq_proto/crypto/rustls.rs.html new file mode 100644 index 000000000..3d6c191dd --- /dev/null +++ b/pr/509/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            #[cfg(feature = "__rustls-post-quantum-test")]
+65            negotiated_key_exchange_group: self
+66                .inner
+67                .negotiated_key_exchange_group()
+68                .expect("key exchange group is negotiated")
+69                .name(),
+70        }))
+71    }
+72
+73    /// For the rustls `TlsSession`, the `Any` type is `Vec<rustls::pki_types::CertificateDer>`
+74    fn peer_identity(&self) -> Option<Box<dyn Any>> {
+75        self.inner.peer_certificates().map(|v| -> Box<dyn Any> {
+76            Box::new(
+77                v.iter()
+78                    .map(|v| v.clone().into_owned())
+79                    .collect::<Vec<CertificateDer<'static>>>(),
+80            )
+81        })
+82    }
+83
+84    fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn crypto::PacketKey>)> {
+85        let keys = self.inner.zero_rtt_keys()?;
+86        Some((Box::new(keys.header), Box::new(keys.packet)))
+87    }
+88
+89    fn early_data_accepted(&self) -> Option<bool> {
+90        match self.inner {
+91            Connection::Client(ref session) => Some(session.is_early_data_accepted()),
+92            _ => None,
+93        }
+94    }
+95
+96    fn is_handshaking(&self) -> bool {
+97        self.inner.is_handshaking()
+98    }
+99
+100    fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError> {
+101        self.inner.read_hs(buf).map_err(|e| {
+102            if let Some(alert) = self.inner.alert() {
+103                TransportError {
+104                    code: TransportErrorCode::crypto(alert.into()),
+105                    frame: crate::frame::MaybeFrame::None,
+106                    reason: e.to_string(),
+107                    crypto: Some(Arc::new(e)),
+108                }
+109            } else {
+110                TransportError::PROTOCOL_VIOLATION(format!("TLS error: {e}"))
+111            }
+112        })?;
+113        if !self.got_handshake_data {
+114            // Hack around the lack of an explicit signal from rustls to reflect ClientHello being
+115            // ready on incoming connections, or ALPN negotiation completing on outgoing
+116            // connections.
+117            let have_server_name = match self.inner {
+118                Connection::Client(_) => false,
+119                Connection::Server(ref session) => session.server_name().is_some(),
+120            };
+121            if self.inner.alpn_protocol().is_some() || have_server_name || !self.is_handshaking() {
+122                self.got_handshake_data = true;
+123                return Ok(true);
+124            }
+125        }
+126        Ok(false)
+127    }
+128
+129    fn transport_parameters(&self) -> Result<Option<TransportParameters>, TransportError> {
+130        match self.inner.quic_transport_parameters() {
+131            None => Ok(None),
+132            Some(buf) => match TransportParameters::read(self.side(), &mut io::Cursor::new(buf)) {
+133                Ok(params) => Ok(Some(params)),
+134                Err(e) => Err(e.into()),
+135            },
+136        }
+137    }
+138
+139    fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys> {
+140        let keys = match self.inner.write_hs(buf)? {
+141            KeyChange::Handshake { keys } => keys,
+142            KeyChange::OneRtt { keys, next } => {
+143                self.next_secrets = Some(next);
+144                keys
+145            }
+146        };
+147
+148        Some(Keys {
+149            header: KeyPair {
+150                local: Box::new(keys.local.header),
+151                remote: Box::new(keys.remote.header),
+152            },
+153            packet: KeyPair {
+154                local: Box::new(keys.local.packet),
+155                remote: Box::new(keys.remote.packet),
+156            },
+157        })
+158    }
+159
+160    fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn crypto::PacketKey>>> {
+161        let secrets = self.next_secrets.as_mut()?;
+162        let keys = secrets.next_packet_keys();
+163        Some(KeyPair {
+164            local: Box::new(keys.local),
+165            remote: Box::new(keys.remote),
+166        })
+167    }
+168
+169    fn is_valid_retry(&self, orig_dst_cid: ConnectionId, header: &[u8], payload: &[u8]) -> bool {
+170        if payload.len() < 16 {
+171            return false;
+172        }
+173
+174        let mut pseudo_packet =
+175            Vec::with_capacity(header.len() + payload.len() + orig_dst_cid.len() + 1);
+176        pseudo_packet.push(orig_dst_cid.len() as u8);
+177        pseudo_packet.extend_from_slice(&orig_dst_cid);
+178        pseudo_packet.extend_from_slice(header);
+179        pseudo_packet.extend_from_slice(payload);
+180
+181        let (nonce, key) = match self.version {
+182            Version::V1 => (&RETRY_INTEGRITY_NONCE_V1, &RETRY_INTEGRITY_KEY_V1),
+183            Version::V1Draft => (&RETRY_INTEGRITY_NONCE_DRAFT, &RETRY_INTEGRITY_KEY_DRAFT),
+184            _ => unreachable!(),
+185        };
+186
+187        let Some((aad, tag)) = pseudo_packet.split_last_chunk::<16>() else {
+188            return false; // But length is actually already checked above.
+189        };
+190
+191        // This implements https://www.rfc-editor.org/rfc/rfc9001#name-retry-packet-integrity
+192        let key = aes_gcm::Key::<aes_gcm::Aes128Gcm>::from_slice(key);
+193        let nonce = aes_gcm::Nonce::from_slice(nonce);
+194        let tag = aes_gcm::Tag::from_slice(tag);
+195        aes_gcm::Aes128Gcm::new(key)
+196            .decrypt_in_place_detached(nonce, aad, &mut [], tag)
+197            .is_ok()
+198    }
+199
+200    fn export_keying_material(
+201        &self,
+202        output: &mut [u8],
+203        label: &[u8],
+204        context: &[u8],
+205    ) -> Result<(), ExportKeyingMaterialError> {
+206        self.inner
+207            .export_keying_material(output, label, Some(context))
+208            .map_err(|_| ExportKeyingMaterialError)?;
+209        Ok(())
+210    }
+211}
+212
+213const RETRY_INTEGRITY_KEY_DRAFT: [u8; 16] = [
+214    0xcc, 0xce, 0x18, 0x7e, 0xd0, 0x9a, 0x09, 0xd0, 0x57, 0x28, 0x15, 0x5a, 0x6c, 0xb9, 0x6b, 0xe1,
+215];
+216const RETRY_INTEGRITY_NONCE_DRAFT: [u8; 12] = [
+217    0xe5, 0x49, 0x30, 0xf9, 0x7f, 0x21, 0x36, 0xf0, 0x53, 0x0a, 0x8c, 0x1c,
+218];
+219
+220const RETRY_INTEGRITY_KEY_V1: [u8; 16] = [
+221    0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57, 0x5a, 0x1d, 0x76, 0x6b, 0x54, 0xe3, 0x68, 0xc8, 0x4e,
+222];
+223const RETRY_INTEGRITY_NONCE_V1: [u8; 12] = [
+224    0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb,
+225];
+226
+227impl crypto::HeaderKey for Box<dyn HeaderProtectionKey> {
+228    fn decrypt(&self, pn_offset: usize, packet: &mut [u8]) {
+229        let (header, sample) = packet.split_at_mut(pn_offset + 4);
+230        let (first, rest) = header.split_at_mut(1);
+231        let pn_end = Ord::min(pn_offset + 3, rest.len());
+232        self.decrypt_in_place(
+233            &sample[..self.sample_size()],
+234            &mut first[0],
+235            &mut rest[pn_offset - 1..pn_end],
+236        )
+237        .unwrap();
+238    }
+239
+240    fn encrypt(&self, pn_offset: usize, packet: &mut [u8]) {
+241        let (header, sample) = packet.split_at_mut(pn_offset + 4);
+242        let (first, rest) = header.split_at_mut(1);
+243        let pn_end = Ord::min(pn_offset + 3, rest.len());
+244        self.encrypt_in_place(
+245            &sample[..self.sample_size()],
+246            &mut first[0],
+247            &mut rest[pn_offset - 1..pn_end],
+248        )
+249        .unwrap();
+250    }
+251
+252    fn sample_size(&self) -> usize {
+253        self.sample_len()
+254    }
+255}
+256
+257/// Authentication data for (rustls) TLS session
+258pub struct HandshakeData {
+259    /// The negotiated application protocol, if ALPN is in use
+260    ///
+261    /// Guaranteed to be set if a nonempty list of protocols was specified for this connection.
+262    pub protocol: Option<Vec<u8>>,
+263    /// The server name specified by the client, if any
+264    ///
+265    /// Always `None` for outgoing connections
+266    pub server_name: Option<String>,
+267    /// The key exchange group negotiated with the peer
+268    #[cfg(feature = "__rustls-post-quantum-test")]
+269    pub negotiated_key_exchange_group: 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/509/docs/src/noq_proto/endpoint.rs.html b/pr/509/docs/src/noq_proto/endpoint.rs.html new file mode 100644 index 000000000..12d3c7e08 --- /dev/null +++ b/pr/509/docs/src/noq_proto/endpoint.rs.html @@ -0,0 +1,1633 @@ +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, RngCore, SeedableRng, rngs::StdRng};
+12use rustc_hash::FxHashMap;
+13use slab::Slab;
+14use thiserror::Error;
+15use tracing::{debug, error, trace, warn};
+16
+17use crate::{
+18    Duration, FourTuple, INITIAL_MTU, Instant, MAX_CID_SIZE, MIN_INITIAL_SIZE, PathId,
+19    RESET_TOKEN_SIZE, ResetToken, Side, Transmit, TransportConfig, TransportError,
+20    cid_generator::ConnectionIdGenerator,
+21    coding::{BufMutExt, 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_os_rng, StdRng::from_seed),
+73            index: ConnectionIndex::default(),
+74            connections: Slab::new(),
+75            local_cid_generator: (config.connection_id_generator_factory.as_ref())(),
+76            config,
+77            server_config,
+78            allow_mtud,
+79            last_stateless_reset: None,
+80            incoming_buffers: Slab::new(),
+81            all_incoming_buffers_total_bytes: 0,
+82        }
+83    }
+84
+85    /// Replace the server configuration, affecting new incoming connections only
+86    pub fn set_server_config(&mut self, server_config: Option<Arc<ServerConfig>>) {
+87        self.server_config = server_config;
+88    }
+89
+90    /// Process `EndpointEvent`s emitted from related `Connection`s
+91    ///
+92    /// In turn, processing this event may return a `ConnectionEvent` for the same `Connection`.
+93    pub fn handle_event(
+94        &mut self,
+95        ch: ConnectionHandle,
+96        event: EndpointEvent,
+97    ) -> Option<ConnectionEvent> {
+98        use EndpointEventInner::*;
+99        match event.0 {
+100            NeedIdentifiers(path_id, now, n) => {
+101                return Some(self.send_new_identifiers(path_id, now, ch, n));
+102            }
+103            ResetToken(path_id, remote, token) => {
+104                if let Some(old) = self.connections[ch]
+105                    .reset_token
+106                    .insert(path_id, (remote, token))
+107                {
+108                    self.index.connection_reset_tokens.remove(old.0, old.1);
+109                }
+110                if self.index.connection_reset_tokens.insert(remote, token, ch) {
+111                    warn!("duplicate reset token");
+112                }
+113            }
+114            RetireResetToken(path_id) => {
+115                if let Some(old) = self.connections[ch].reset_token.remove(&path_id) {
+116                    self.index.connection_reset_tokens.remove(old.0, old.1);
+117                }
+118            }
+119            RetireConnectionId(now, path_id, seq, allow_more_cids) => {
+120                if let Some(cid) = self.connections[ch]
+121                    .local_cids
+122                    .get_mut(&path_id)
+123                    .and_then(|pcid| pcid.cids.remove(&seq))
+124                {
+125                    trace!(%path_id, "local CID retired {}: {}", seq, cid);
+126                    self.index.retire(cid);
+127                    if allow_more_cids {
+128                        return Some(self.send_new_identifiers(path_id, now, ch, 1));
+129                    }
+130                }
+131            }
+132            Drained => {
+133                if let Some(conn) = self.connections.try_remove(ch.0) {
+134                    self.index.remove(&conn);
+135                } else {
+136                    // This indicates a bug in downstream code, which could cause spurious
+137                    // connection loss instead of this error if the CID was (re)allocated prior to
+138                    // the illegal call.
+139                    error!(id = ch.0, "unknown connection drained");
+140                }
+141            }
+142        }
+143        None
+144    }
+145
+146    /// Process an incoming UDP datagram
+147    pub fn handle(
+148        &mut self,
+149        now: Instant,
+150        network_path: FourTuple,
+151        ecn: Option<EcnCodepoint>,
+152        data: BytesMut,
+153        buf: &mut Vec<u8>,
+154    ) -> Option<DatagramEvent> {
+155        // Partially decode packet or short-circuit if unable
+156        let datagram_len = data.len();
+157        let mut event = match PartialDecode::new(
+158            data,
+159            &FixedLengthConnectionIdParser::new(self.local_cid_generator.cid_len()),
+160            &self.config.supported_versions,
+161            self.config.grease_quic_bit,
+162        ) {
+163            Ok((first_decode, remaining)) => DatagramConnectionEvent {
+164                now,
+165                network_path,
+166                path_id: PathId::ZERO, // Corrected later for existing paths
+167                ecn,
+168                first_decode,
+169                remaining,
+170            },
+171            Err(PacketDecodeError::UnsupportedVersion {
+172                src_cid,
+173                dst_cid,
+174                version,
+175            }) => {
+176                if self.server_config.is_none() {
+177                    debug!("dropping packet with unsupported version");
+178                    return None;
+179                }
+180                trace!("sending version negotiation");
+181                // Negotiate versions
+182                Header::VersionNegotiate {
+183                    random: self.rng.random::<u8>() | 0x40,
+184                    src_cid: dst_cid,
+185                    dst_cid: src_cid,
+186                }
+187                .encode(buf);
+188                // Grease with a reserved version
+189                buf.write::<u32>(match version {
+190                    0x0a1a_2a3a => 0x0a1a_2a4a,
+191                    _ => 0x0a1a_2a3a,
+192                });
+193                for &version in &self.config.supported_versions {
+194                    buf.write(version);
+195                }
+196                return Some(DatagramEvent::Response(Transmit {
+197                    destination: network_path.remote,
+198                    ecn: None,
+199                    size: buf.len(),
+200                    segment_size: None,
+201                    src_ip: network_path.local_ip,
+202                }));
+203            }
+204            Err(e) => {
+205                trace!("malformed header: {}", e);
+206                return None;
+207            }
+208        };
+209
+210        let dst_cid = event.first_decode.dst_cid();
+211
+212        if let Some(route_to) = self.index.get(&network_path, &event.first_decode) {
+213            event.path_id = match route_to {
+214                RouteDatagramTo::Incoming(_) => PathId::ZERO,
+215                RouteDatagramTo::Connection(_, path_id) => path_id,
+216            };
+217            match route_to {
+218                RouteDatagramTo::Incoming(incoming_idx) => {
+219                    let incoming_buffer = &mut self.incoming_buffers[incoming_idx];
+220                    let config = &self.server_config.as_ref().unwrap();
+221
+222                    if incoming_buffer
+223                        .total_bytes
+224                        .checked_add(datagram_len as u64)
+225                        .is_some_and(|n| n <= config.incoming_buffer_size)
+226                        && self
+227                            .all_incoming_buffers_total_bytes
+228                            .checked_add(datagram_len as u64)
+229                            .is_some_and(|n| n <= config.incoming_buffer_size_total)
+230                    {
+231                        incoming_buffer.datagrams.push(event);
+232                        incoming_buffer.total_bytes += datagram_len as u64;
+233                        self.all_incoming_buffers_total_bytes += datagram_len as u64;
+234                    }
+235
+236                    None
+237                }
+238                RouteDatagramTo::Connection(ch, _path_id) => Some(DatagramEvent::ConnectionEvent(
+239                    ch,
+240                    ConnectionEvent(ConnectionEventInner::Datagram(event)),
+241                )),
+242            }
+243        } else if event.first_decode.initial_header().is_some() {
+244            // Potentially create a new connection
+245
+246            self.handle_first_packet(datagram_len, event, network_path, buf)
+247        } else if event.first_decode.has_long_header() {
+248            debug!(
+249                "ignoring non-initial packet for unknown connection {}",
+250                dst_cid
+251            );
+252            None
+253        } else if !event.first_decode.is_initial()
+254            && self.local_cid_generator.validate(dst_cid).is_err()
+255        {
+256            debug!("dropping packet with invalid CID");
+257            None
+258        } else if dst_cid.is_empty() {
+259            trace!("dropping unrecognized short packet without ID");
+260            None
+261        } else {
+262            // If we got this far, we're receiving a seemingly valid packet for an unknown
+263            // connection. Send a stateless reset if possible.
+264            self.stateless_reset(now, datagram_len, network_path, dst_cid, buf)
+265                .map(DatagramEvent::Response)
+266        }
+267    }
+268
+269    /// Builds a stateless reset packet to respond with
+270    fn stateless_reset(
+271        &mut self,
+272        now: Instant,
+273        inciting_dgram_len: usize,
+274        network_path: FourTuple,
+275        dst_cid: ConnectionId,
+276        buf: &mut Vec<u8>,
+277    ) -> Option<Transmit> {
+278        if self
+279            .last_stateless_reset
+280            .is_some_and(|last| last + self.config.min_reset_interval > now)
+281        {
+282            debug!("ignoring unexpected packet within minimum stateless reset interval");
+283            return None;
+284        }
+285
+286        /// Minimum amount of padding for the stateless reset to look like a short-header packet
+287        const MIN_PADDING_LEN: usize = 5;
+288
+289        // Prevent amplification attacks and reset loops by ensuring we pad to at most 1 byte
+290        // smaller than the inciting packet.
+291        let max_padding_len = match inciting_dgram_len.checked_sub(RESET_TOKEN_SIZE) {
+292            Some(headroom) if headroom > MIN_PADDING_LEN => headroom - 1,
+293            _ => {
+294                debug!(
+295                    "ignoring unexpected {} byte packet: not larger than minimum stateless reset size",
+296                    inciting_dgram_len
+297                );
+298                return None;
+299            }
+300        };
+301
+302        debug!(%dst_cid, %network_path.remote, "sending stateless reset");
+303        self.last_stateless_reset = Some(now);
+304        // Resets with at least this much padding can't possibly be distinguished from real packets
+305        const IDEAL_MIN_PADDING_LEN: usize = MIN_PADDING_LEN + MAX_CID_SIZE;
+306        let padding_len = if max_padding_len <= IDEAL_MIN_PADDING_LEN {
+307            max_padding_len
+308        } else {
+309            self.rng
+310                .random_range(IDEAL_MIN_PADDING_LEN..max_padding_len)
+311        };
+312        buf.reserve(padding_len + RESET_TOKEN_SIZE);
+313        buf.resize(padding_len, 0);
+314        self.rng.fill_bytes(&mut buf[0..padding_len]);
+315        buf[0] = 0b0100_0000 | (buf[0] >> 2);
+316        buf.extend_from_slice(&ResetToken::new(&*self.config.reset_key, dst_cid));
+317
+318        debug_assert!(buf.len() < inciting_dgram_len);
+319
+320        Some(Transmit {
+321            destination: network_path.remote,
+322            ecn: None,
+323            size: buf.len(),
+324            segment_size: None,
+325            src_ip: network_path.local_ip,
+326        })
+327    }
+328
+329    /// Initiate a connection
+330    pub fn connect(
+331        &mut self,
+332        now: Instant,
+333        config: ClientConfig,
+334        remote: SocketAddr,
+335        server_name: &str,
+336    ) -> Result<(ConnectionHandle, Connection), ConnectError> {
+337        if self.cids_exhausted() {
+338            return Err(ConnectError::CidsExhausted);
+339        }
+340        if remote.port() == 0 || remote.ip().is_unspecified() {
+341            return Err(ConnectError::InvalidRemoteAddress(remote));
+342        }
+343        if !self.config.supported_versions.contains(&config.version) {
+344            return Err(ConnectError::UnsupportedVersion);
+345        }
+346
+347        let remote_id = (config.initial_dst_cid_provider)();
+348        trace!(initial_dcid = %remote_id);
+349
+350        let ch = ConnectionHandle(self.connections.vacant_key());
+351        let local_cid = self.new_cid(ch, PathId::ZERO);
+352        let params = TransportParameters::new(
+353            &config.transport,
+354            &self.config,
+355            self.local_cid_generator.as_ref(),
+356            local_cid,
+357            None,
+358            &mut self.rng,
+359        );
+360        let tls = config
+361            .crypto
+362            .start_session(config.version, server_name, &params)?;
+363
+364        let conn = self.add_connection(
+365            ch,
+366            config.version,
+367            remote_id,
+368            local_cid,
+369            remote_id,
+370            FourTuple {
+371                remote,
+372                local_ip: None,
+373            },
+374            now,
+375            tls,
+376            config.transport,
+377            SideArgs::Client {
+378                token_store: config.token_store,
+379                server_name: server_name.into(),
+380            },
+381            &params,
+382        );
+383        Ok((ch, conn))
+384    }
+385
+386    /// Generates new CIDs and creates message to send to the connection state
+387    fn send_new_identifiers(
+388        &mut self,
+389        path_id: PathId,
+390        now: Instant,
+391        ch: ConnectionHandle,
+392        num: u64,
+393    ) -> ConnectionEvent {
+394        let mut ids = vec![];
+395        for _ in 0..num {
+396            let id = self.new_cid(ch, path_id);
+397            let cid_meta = self.connections[ch].local_cids.entry(path_id).or_default();
+398            let sequence = cid_meta.issued;
+399            cid_meta.issued += 1;
+400            cid_meta.cids.insert(sequence, id);
+401            ids.push(IssuedCid {
+402                path_id,
+403                sequence,
+404                id,
+405                reset_token: ResetToken::new(&*self.config.reset_key, id),
+406            });
+407        }
+408        ConnectionEvent(ConnectionEventInner::NewIdentifiers(
+409            ids,
+410            now,
+411            self.local_cid_generator.cid_len(),
+412            self.local_cid_generator.cid_lifetime(),
+413        ))
+414    }
+415
+416    /// Generate a connection ID for `ch`
+417    fn new_cid(&mut self, ch: ConnectionHandle, path_id: PathId) -> ConnectionId {
+418        loop {
+419            let cid = self.local_cid_generator.generate_cid();
+420            if cid.is_empty() {
+421                // Zero-length CID; nothing to track
+422                debug_assert_eq!(self.local_cid_generator.cid_len(), 0);
+423                return cid;
+424            }
+425            if let hash_map::Entry::Vacant(e) = self.index.connection_ids.entry(cid) {
+426                e.insert((ch, path_id));
+427                break cid;
+428            }
+429        }
+430    }
+431
+432    fn handle_first_packet(
+433        &mut self,
+434        datagram_len: usize,
+435        event: DatagramConnectionEvent,
+436        network_path: FourTuple,
+437        buf: &mut Vec<u8>,
+438    ) -> Option<DatagramEvent> {
+439        let dst_cid = event.first_decode.dst_cid();
+440        let header = event.first_decode.initial_header().unwrap();
+441
+442        let Some(server_config) = &self.server_config else {
+443            debug!("packet for unrecognized connection {}", dst_cid);
+444            return self
+445                .stateless_reset(event.now, datagram_len, network_path, dst_cid, buf)
+446                .map(DatagramEvent::Response);
+447        };
+448
+449        if datagram_len < MIN_INITIAL_SIZE as usize {
+450            debug!("ignoring short initial for connection {}", dst_cid);
+451            return None;
+452        }
+453
+454        let crypto = match server_config.crypto.initial_keys(header.version, dst_cid) {
+455            Ok(keys) => keys,
+456            Err(UnsupportedVersion) => {
+457                // This probably indicates that the user set supported_versions incorrectly in
+458                // `EndpointConfig`.
+459                debug!(
+460                    "ignoring initial packet version {:#x} unsupported by cryptographic layer",
+461                    header.version
+462                );
+463                return None;
+464            }
+465        };
+466
+467        if let Err(reason) = self.early_validate_first_packet(header) {
+468            return Some(DatagramEvent::Response(self.initial_close(
+469                header.version,
+470                network_path,
+471                &crypto,
+472                header.src_cid,
+473                reason,
+474                buf,
+475            )));
+476        }
+477
+478        let packet = match event.first_decode.finish(Some(&*crypto.header.remote)) {
+479            Ok(packet) => packet,
+480            Err(e) => {
+481                trace!("unable to decode initial packet: {}", e);
+482                return None;
+483            }
+484        };
+485
+486        if !packet.reserved_bits_valid() {
+487            debug!("dropping connection attempt with invalid reserved bits");
+488            return None;
+489        }
+490
+491        let Header::Initial(header) = packet.header else {
+492            panic!("non-initial packet in handle_first_packet()");
+493        };
+494
+495        let server_config = self.server_config.as_ref().unwrap().clone();
+496
+497        let token = match IncomingToken::from_header(&header, &server_config, network_path.remote) {
+498            Ok(token) => token,
+499            Err(InvalidRetryTokenError) => {
+500                debug!("rejecting invalid retry token");
+501                return Some(DatagramEvent::Response(self.initial_close(
+502                    header.version,
+503                    network_path,
+504                    &crypto,
+505                    header.src_cid,
+506                    TransportError::INVALID_TOKEN(""),
+507                    buf,
+508                )));
+509            }
+510        };
+511
+512        let incoming_idx = self.incoming_buffers.insert(IncomingBuffer::default());
+513        self.index
+514            .insert_initial_incoming(header.dst_cid, incoming_idx);
+515
+516        Some(DatagramEvent::NewConnection(Incoming {
+517            received_at: event.now,
+518            network_path,
+519            ecn: event.ecn,
+520            packet: InitialPacket {
+521                header,
+522                header_data: packet.header_data,
+523                payload: packet.payload,
+524            },
+525            rest: event.remaining,
+526            crypto,
+527            token,
+528            incoming_idx,
+529            improper_drop_warner: IncomingImproperDropWarner,
+530        }))
+531    }
+532
+533    /// Attempt to accept this incoming connection (an error may still occur)
+534    // box err to avoid clippy::result_large_err
+535    pub fn accept(
+536        &mut self,
+537        mut incoming: Incoming,
+538        now: Instant,
+539        buf: &mut Vec<u8>,
+540        server_config: Option<Arc<ServerConfig>>,
+541    ) -> Result<(ConnectionHandle, Connection), Box<AcceptError>> {
+542        let remote_address_validated = incoming.remote_address_validated();
+543        incoming.improper_drop_warner.dismiss();
+544        let incoming_buffer = self.incoming_buffers.remove(incoming.incoming_idx);
+545        self.all_incoming_buffers_total_bytes -= incoming_buffer.total_bytes;
+546
+547        let packet_number = incoming.packet.header.number.expand(0);
+548        let InitialHeader {
+549            src_cid,
+550            dst_cid,
+551            version,
+552            ..
+553        } = incoming.packet.header;
+554        let server_config =
+555            server_config.unwrap_or_else(|| self.server_config.as_ref().unwrap().clone());
+556
+557        if server_config
+558            .transport
+559            .max_idle_timeout
+560            .is_some_and(|timeout| {
+561                incoming.received_at + Duration::from_millis(timeout.into()) <= now
+562            })
+563        {
+564            debug!("abandoning accept of stale initial");
+565            self.index.remove_initial(dst_cid);
+566            return Err(Box::new(AcceptError {
+567                cause: ConnectionError::TimedOut,
+568                response: None,
+569            }));
+570        }
+571
+572        if self.cids_exhausted() {
+573            debug!("refusing connection");
+574            self.index.remove_initial(dst_cid);
+575            return Err(Box::new(AcceptError {
+576                cause: ConnectionError::CidsExhausted,
+577                response: Some(self.initial_close(
+578                    version,
+579                    incoming.network_path,
+580                    &incoming.crypto,
+581                    src_cid,
+582                    TransportError::CONNECTION_REFUSED(""),
+583                    buf,
+584                )),
+585            }));
+586        }
+587
+588        if incoming
+589            .crypto
+590            .packet
+591            .remote
+592            .decrypt(
+593                PathId::ZERO,
+594                packet_number,
+595                &incoming.packet.header_data,
+596                &mut incoming.packet.payload,
+597            )
+598            .is_err()
+599        {
+600            debug!(packet_number, "failed to authenticate initial packet");
+601            self.index.remove_initial(dst_cid);
+602            return Err(Box::new(AcceptError {
+603                cause: TransportError::PROTOCOL_VIOLATION("authentication failed").into(),
+604                response: None,
+605            }));
+606        };
+607
+608        let ch = ConnectionHandle(self.connections.vacant_key());
+609        let local_cid = self.new_cid(ch, PathId::ZERO);
+610        let mut params = TransportParameters::new(
+611            &server_config.transport,
+612            &self.config,
+613            self.local_cid_generator.as_ref(),
+614            local_cid,
+615            Some(&server_config),
+616            &mut self.rng,
+617        );
+618        params.stateless_reset_token = Some(ResetToken::new(&*self.config.reset_key, local_cid));
+619        params.original_dst_cid = Some(incoming.token.orig_dst_cid);
+620        params.retry_src_cid = incoming.token.retry_src_cid;
+621        let mut pref_addr_cid = None;
+622        if server_config.has_preferred_address() {
+623            let cid = self.new_cid(ch, PathId::ZERO);
+624            pref_addr_cid = Some(cid);
+625            params.preferred_address = Some(PreferredAddress {
+626                address_v4: server_config.preferred_address_v4,
+627                address_v6: server_config.preferred_address_v6,
+628                connection_id: cid,
+629                stateless_reset_token: ResetToken::new(&*self.config.reset_key, cid),
+630            });
+631        }
+632
+633        let tls = server_config.crypto.start_session(version, &params);
+634        let transport_config = server_config.transport.clone();
+635        let mut conn = self.add_connection(
+636            ch,
+637            version,
+638            dst_cid,
+639            local_cid,
+640            src_cid,
+641            incoming.network_path,
+642            incoming.received_at,
+643            tls,
+644            transport_config,
+645            SideArgs::Server {
+646                server_config,
+647                pref_addr_cid,
+648                path_validated: remote_address_validated,
+649            },
+650            &params,
+651        );
+652        self.index.insert_initial(dst_cid, ch);
+653
+654        match conn.handle_first_packet(
+655            incoming.received_at,
+656            incoming.network_path,
+657            incoming.ecn,
+658            packet_number,
+659            incoming.packet,
+660            incoming.rest,
+661        ) {
+662            Ok(()) => {
+663                trace!(id = ch.0, icid = %dst_cid, "new connection");
+664
+665                for event in incoming_buffer.datagrams {
+666                    conn.handle_event(ConnectionEvent(ConnectionEventInner::Datagram(event)))
+667                }
+668
+669                Ok((ch, conn))
+670            }
+671            Err(e) => {
+672                debug!("handshake failed: {}", e);
+673                self.handle_event(ch, EndpointEvent(EndpointEventInner::Drained));
+674                let response = match e {
+675                    ConnectionError::TransportError(ref e) => Some(self.initial_close(
+676                        version,
+677                        incoming.network_path,
+678                        &incoming.crypto,
+679                        src_cid,
+680                        e.clone(),
+681                        buf,
+682                    )),
+683                    _ => None,
+684                };
+685                Err(Box::new(AcceptError { cause: e, response }))
+686            }
+687        }
+688    }
+689
+690    /// Check if we should refuse a connection attempt regardless of the packet's contents
+691    fn early_validate_first_packet(
+692        &mut self,
+693        header: &ProtectedInitialHeader,
+694    ) -> Result<(), TransportError> {
+695        let config = &self.server_config.as_ref().unwrap();
+696        if self.cids_exhausted() || self.incoming_buffers.len() >= config.max_incoming {
+697            return Err(TransportError::CONNECTION_REFUSED(""));
+698        }
+699
+700        // RFC9000 §7.2 dictates that initial (client-chosen) destination CIDs must be at least 8
+701        // bytes. If this is a Retry packet, then the length must instead match our usual CID
+702        // length. If we ever issue non-Retry address validation tokens via `NEW_TOKEN`, then we'll
+703        // also need to validate CID length for those after decoding the token.
+704        if header.dst_cid.len() < 8
+705            && (header.token_pos.is_empty()
+706                || header.dst_cid.len() != self.local_cid_generator.cid_len())
+707        {
+708            debug!(
+709                "rejecting connection due to invalid DCID length {}",
+710                header.dst_cid.len()
+711            );
+712            return Err(TransportError::PROTOCOL_VIOLATION(
+713                "invalid destination CID length",
+714            ));
+715        }
+716
+717        Ok(())
+718    }
+719
+720    /// Reject this incoming connection attempt
+721    pub fn refuse(&mut self, incoming: Incoming, buf: &mut Vec<u8>) -> Transmit {
+722        self.clean_up_incoming(&incoming);
+723        incoming.improper_drop_warner.dismiss();
+724
+725        self.initial_close(
+726            incoming.packet.header.version,
+727            incoming.network_path,
+728            &incoming.crypto,
+729            incoming.packet.header.src_cid,
+730            TransportError::CONNECTION_REFUSED(""),
+731            buf,
+732        )
+733    }
+734
+735    /// Respond with a retry packet, requiring the client to retry with address validation
+736    ///
+737    /// Errors if `incoming.may_retry()` is false.
+738    pub fn retry(&mut self, incoming: Incoming, buf: &mut Vec<u8>) -> Result<Transmit, RetryError> {
+739        if !incoming.may_retry() {
+740            return Err(RetryError(Box::new(incoming)));
+741        }
+742
+743        self.clean_up_incoming(&incoming);
+744        incoming.improper_drop_warner.dismiss();
+745
+746        let server_config = self.server_config.as_ref().unwrap();
+747
+748        // First Initial
+749        // The peer will use this as the DCID of its following Initials. Initial DCIDs are
+750        // looked up separately from Handshake/Data DCIDs, so there is no risk of collision
+751        // with established connections. In the unlikely event that a collision occurs
+752        // between two connections in the initial phase, both will fail fast and may be
+753        // retried by the application layer.
+754        let local_cid = self.local_cid_generator.generate_cid();
+755
+756        let payload = TokenPayload::Retry {
+757            address: incoming.network_path.remote,
+758            orig_dst_cid: incoming.packet.header.dst_cid,
+759            issued: server_config.time_source.now(),
+760        };
+761        let token = Token::new(payload, &mut self.rng).encode(&*server_config.token_key);
+762
+763        let header = Header::Retry {
+764            src_cid: local_cid,
+765            dst_cid: incoming.packet.header.src_cid,
+766            version: incoming.packet.header.version,
+767        };
+768
+769        let encode = header.encode(buf);
+770        buf.put_slice(&token);
+771        buf.extend_from_slice(&server_config.crypto.retry_tag(
+772            incoming.packet.header.version,
+773            incoming.packet.header.dst_cid,
+774            buf,
+775        ));
+776        encode.finish(buf, &*incoming.crypto.header.local, None);
+777
+778        Ok(Transmit {
+779            destination: incoming.network_path.remote,
+780            ecn: None,
+781            size: buf.len(),
+782            segment_size: None,
+783            src_ip: incoming.network_path.local_ip,
+784        })
+785    }
+786
+787    /// Ignore this incoming connection attempt, not sending any packet in response
+788    ///
+789    /// Doing this actively, rather than merely dropping the [`Incoming`], is necessary to prevent
+790    /// memory leaks due to state within [`Endpoint`] tracking the incoming connection.
+791    pub fn ignore(&mut self, incoming: Incoming) {
+792        self.clean_up_incoming(&incoming);
+793        incoming.improper_drop_warner.dismiss();
+794    }
+795
+796    /// Clean up endpoint data structures associated with an `Incoming`.
+797    fn clean_up_incoming(&mut self, incoming: &Incoming) {
+798        self.index.remove_initial(incoming.packet.header.dst_cid);
+799        let incoming_buffer = self.incoming_buffers.remove(incoming.incoming_idx);
+800        self.all_incoming_buffers_total_bytes -= incoming_buffer.total_bytes;
+801    }
+802
+803    fn add_connection(
+804        &mut self,
+805        ch: ConnectionHandle,
+806        version: u32,
+807        init_cid: ConnectionId,
+808        local_cid: ConnectionId,
+809        remote_cid: ConnectionId,
+810        network_path: FourTuple,
+811        now: Instant,
+812        tls: Box<dyn crypto::Session>,
+813        transport_config: Arc<TransportConfig>,
+814        side_args: SideArgs,
+815        // Only used for qlog.
+816        params: &TransportParameters,
+817    ) -> Connection {
+818        let mut rng_seed = [0; 32];
+819        self.rng.fill_bytes(&mut rng_seed);
+820        let side = side_args.side();
+821        let pref_addr_cid = side_args.pref_addr_cid();
+822
+823        let qlog =
+824            transport_config.create_qlog_sink(side_args.side(), network_path.remote, init_cid, now);
+825
+826        qlog.emit_connection_started(
+827            now,
+828            local_cid,
+829            remote_cid,
+830            network_path.remote,
+831            network_path.local_ip,
+832            params,
+833        );
+834
+835        let conn = Connection::new(
+836            self.config.clone(),
+837            transport_config,
+838            init_cid,
+839            local_cid,
+840            remote_cid,
+841            network_path,
+842            tls,
+843            self.local_cid_generator.as_ref(),
+844            now,
+845            version,
+846            self.allow_mtud,
+847            rng_seed,
+848            side_args,
+849            qlog,
+850        );
+851
+852        let mut path_cids = PathLocalCids::default();
+853        path_cids.cids.insert(path_cids.issued, local_cid);
+854        path_cids.issued += 1;
+855
+856        if let Some(cid) = pref_addr_cid {
+857            debug_assert_eq!(path_cids.issued, 1, "preferred address cid seq must be 1");
+858            path_cids.cids.insert(path_cids.issued, cid);
+859            path_cids.issued += 1;
+860        }
+861
+862        let id = self.connections.insert(ConnectionMeta {
+863            init_cid,
+864            local_cids: FxHashMap::from_iter([(PathId::ZERO, path_cids)]),
+865            network_path,
+866            side,
+867            reset_token: Default::default(),
+868        });
+869        debug_assert_eq!(id, ch.0, "connection handle allocation out of sync");
+870
+871        self.index.insert_conn(network_path, local_cid, ch, side);
+872
+873        conn
+874    }
+875
+876    fn initial_close(
+877        &mut self,
+878        version: u32,
+879        network_path: FourTuple,
+880        crypto: &Keys,
+881        remote_id: ConnectionId,
+882        reason: TransportError,
+883        buf: &mut Vec<u8>,
+884    ) -> Transmit {
+885        // We don't need to worry about CID collisions in initial closes because the peer
+886        // shouldn't respond, and if it does, and the CID collides, we'll just drop the
+887        // unexpected response.
+888        let local_id = self.local_cid_generator.generate_cid();
+889        let number = PacketNumber::U8(0);
+890        let header = Header::Initial(InitialHeader {
+891            dst_cid: remote_id,
+892            src_cid: local_id,
+893            number,
+894            token: Bytes::new(),
+895            version,
+896        });
+897
+898        let partial_encode = header.encode(buf);
+899        let max_len =
+900            INITIAL_MTU as usize - partial_encode.header_len - crypto.packet.local.tag_len();
+901        frame::Close::from(reason).encoder(max_len).encode(buf);
+902        buf.resize(buf.len() + crypto.packet.local.tag_len(), 0);
+903        partial_encode.finish(
+904            buf,
+905            &*crypto.header.local,
+906            Some((0, Default::default(), &*crypto.packet.local)),
+907        );
+908        Transmit {
+909            destination: network_path.remote,
+910            ecn: None,
+911            size: buf.len(),
+912            segment_size: None,
+913            src_ip: network_path.local_ip,
+914        }
+915    }
+916
+917    /// Access the configuration used by this endpoint
+918    pub fn config(&self) -> &EndpointConfig {
+919        &self.config
+920    }
+921
+922    /// Number of connections that are currently open
+923    pub fn open_connections(&self) -> usize {
+924        self.connections.len()
+925    }
+926
+927    /// Counter for the number of bytes currently used
+928    /// in the buffers for Initial and 0-RTT messages for pending incoming connections
+929    pub fn incoming_buffer_bytes(&self) -> u64 {
+930        self.all_incoming_buffers_total_bytes
+931    }
+932
+933    #[cfg(test)]
+934    pub(crate) fn known_connections(&self) -> usize {
+935        let x = self.connections.len();
+936        debug_assert_eq!(x, self.index.connection_ids_initial.len());
+937        // Not all connections have known reset tokens
+938        debug_assert!(x >= self.index.connection_reset_tokens.0.len());
+939        // Not all connections have unique remotes, and 0-length CIDs might not be in use.
+940        debug_assert!(x >= self.index.incoming_connection_remotes.len());
+941        debug_assert!(x >= self.index.outgoing_connection_remotes.len());
+942        x
+943    }
+944
+945    #[cfg(test)]
+946    pub(crate) fn known_cids(&self) -> usize {
+947        self.index.connection_ids.len()
+948    }
+949
+950    /// Whether we've used up 3/4 of the available CID space
+951    ///
+952    /// We leave some space unused so that `new_cid` can be relied upon to finish quickly. We don't
+953    /// bother to check when CID longer than 4 bytes are used because 2^40 connections is a lot.
+954    fn cids_exhausted(&self) -> bool {
+955        self.local_cid_generator.cid_len() <= 4
+956            && self.local_cid_generator.cid_len() != 0
+957            && (2usize.pow(self.local_cid_generator.cid_len() as u32 * 8)
+958                - self.index.connection_ids.len())
+959                < 2usize.pow(self.local_cid_generator.cid_len() as u32 * 8 - 2)
+960    }
+961}
+962
+963impl fmt::Debug for Endpoint {
+964    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+965        fmt.debug_struct("Endpoint")
+966            .field("rng", &self.rng)
+967            .field("index", &self.index)
+968            .field("connections", &self.connections)
+969            .field("config", &self.config)
+970            .field("server_config", &self.server_config)
+971            // incoming_buffers too large
+972            .field("incoming_buffers.len", &self.incoming_buffers.len())
+973            .field(
+974                "all_incoming_buffers_total_bytes",
+975                &self.all_incoming_buffers_total_bytes,
+976            )
+977            .finish()
+978    }
+979}
+980
+981/// Buffered Initial and 0-RTT messages for a pending incoming connection
+982#[derive(Default)]
+983struct IncomingBuffer {
+984    datagrams: Vec<DatagramConnectionEvent>,
+985    total_bytes: u64,
+986}
+987
+988/// Part of protocol state incoming datagrams can be routed to
+989#[derive(Copy, Clone, Debug)]
+990enum RouteDatagramTo {
+991    Incoming(usize),
+992    Connection(ConnectionHandle, PathId),
+993}
+994
+995/// Maps packets to existing connections
+996#[derive(Default, Debug)]
+997struct ConnectionIndex {
+998    /// Identifies connections based on the initial DCID the peer utilized
+999    ///
+1000    /// Uses a standard `HashMap` to protect against hash collision attacks.
+1001    ///
+1002    /// Used by the server, not the client.
+1003    connection_ids_initial: HashMap<ConnectionId, RouteDatagramTo>,
+1004    /// Identifies connections based on locally created CIDs
+1005    ///
+1006    /// Uses a cheaper hash function since keys are locally created
+1007    connection_ids: FxHashMap<ConnectionId, (ConnectionHandle, PathId)>,
+1008    /// Identifies incoming connections with zero-length CIDs
+1009    ///
+1010    /// Uses a standard `HashMap` to protect against hash collision attacks.
+1011    incoming_connection_remotes: HashMap<FourTuple, ConnectionHandle>,
+1012    /// Identifies outgoing connections with zero-length CIDs
+1013    ///
+1014    /// We don't yet support explicit source addresses for client connections, and zero-length CIDs
+1015    /// require a unique 4-tuple, so at most one client connection with zero-length local CIDs
+1016    /// may be established per remote. We must omit the local address from the key because we don't
+1017    /// necessarily know what address we're sending from, and hence receiving at.
+1018    ///
+1019    /// Uses a standard `HashMap` to protect against hash collision attacks.
+1020    // TODO(matheus23): It's possible this could be changed now that we track the full 4-tuple on the client side, too.
+1021    outgoing_connection_remotes: HashMap<SocketAddr, ConnectionHandle>,
+1022    /// Reset tokens provided by the peer for the CID each connection is currently sending to
+1023    ///
+1024    /// Incoming stateless resets do not have correct CIDs, so we need this to identify the correct
+1025    /// recipient, if any.
+1026    connection_reset_tokens: ResetTokenTable,
+1027}
+1028
+1029impl ConnectionIndex {
+1030    /// Associate an incoming connection with its initial destination CID
+1031    fn insert_initial_incoming(&mut self, dst_cid: ConnectionId, incoming_key: usize) {
+1032        if dst_cid.is_empty() {
+1033            return;
+1034        }
+1035        self.connection_ids_initial
+1036            .insert(dst_cid, RouteDatagramTo::Incoming(incoming_key));
+1037    }
+1038
+1039    /// Remove an association with an initial destination CID
+1040    fn remove_initial(&mut self, dst_cid: ConnectionId) {
+1041        if dst_cid.is_empty() {
+1042            return;
+1043        }
+1044        let removed = self.connection_ids_initial.remove(&dst_cid);
+1045        debug_assert!(removed.is_some());
+1046    }
+1047
+1048    /// Associate a connection with its initial destination CID
+1049    fn insert_initial(&mut self, dst_cid: ConnectionId, connection: ConnectionHandle) {
+1050        if dst_cid.is_empty() {
+1051            return;
+1052        }
+1053        self.connection_ids_initial.insert(
+1054            dst_cid,
+1055            RouteDatagramTo::Connection(connection, PathId::ZERO),
+1056        );
+1057    }
+1058
+1059    /// Associate a connection with its first locally-chosen destination CID if used, or otherwise
+1060    /// its current 4-tuple
+1061    fn insert_conn(
+1062        &mut self,
+1063        network_path: FourTuple,
+1064        dst_cid: ConnectionId,
+1065        connection: ConnectionHandle,
+1066        side: Side,
+1067    ) {
+1068        match dst_cid.len() {
+1069            0 => match side {
+1070                Side::Server => {
+1071                    self.incoming_connection_remotes
+1072                        .insert(network_path, connection);
+1073                }
+1074                Side::Client => {
+1075                    self.outgoing_connection_remotes
+1076                        .insert(network_path.remote, connection);
+1077                }
+1078            },
+1079            _ => {
+1080                self.connection_ids
+1081                    .insert(dst_cid, (connection, PathId::ZERO));
+1082            }
+1083        }
+1084    }
+1085
+1086    /// Discard a connection ID
+1087    fn retire(&mut self, dst_cid: ConnectionId) {
+1088        self.connection_ids.remove(&dst_cid);
+1089    }
+1090
+1091    /// Remove all references to a connection
+1092    fn remove(&mut self, conn: &ConnectionMeta) {
+1093        if conn.side.is_server() {
+1094            self.remove_initial(conn.init_cid);
+1095        }
+1096        for cid in conn
+1097            .local_cids
+1098            .values()
+1099            .flat_map(|pcids| pcids.cids.values())
+1100        {
+1101            self.connection_ids.remove(cid);
+1102        }
+1103        self.incoming_connection_remotes.remove(&conn.network_path);
+1104        self.outgoing_connection_remotes
+1105            .remove(&conn.network_path.remote);
+1106        for (remote, token) in conn.reset_token.values() {
+1107            self.connection_reset_tokens.remove(*remote, *token);
+1108        }
+1109    }
+1110
+1111    /// Find the existing connection that `datagram` should be routed to, if any
+1112    fn get(&self, network_path: &FourTuple, datagram: &PartialDecode) -> Option<RouteDatagramTo> {
+1113        if !datagram.dst_cid().is_empty()
+1114            && let Some(&(ch, path_id)) = self.connection_ids.get(&datagram.dst_cid())
+1115        {
+1116            return Some(RouteDatagramTo::Connection(ch, path_id));
+1117        }
+1118        if (datagram.is_initial() || datagram.is_0rtt())
+1119            && let Some(&ch) = self.connection_ids_initial.get(&datagram.dst_cid())
+1120        {
+1121            return Some(ch);
+1122        }
+1123        if datagram.dst_cid().is_empty() {
+1124            if let Some(&ch) = self.incoming_connection_remotes.get(network_path) {
+1125                // Never multipath because QUIC-MULTIPATH 1.1 mandates the use of non-zero
+1126                // length CIDs.  So this is always PathId::ZERO.
+1127                return Some(RouteDatagramTo::Connection(ch, PathId::ZERO));
+1128            }
+1129            if let Some(&ch) = self.outgoing_connection_remotes.get(&network_path.remote) {
+1130                // Like above, QUIC-MULTIPATH 1.1 mandates the use of non-zero length CIDs.
+1131                return Some(RouteDatagramTo::Connection(ch, PathId::ZERO));
+1132            }
+1133        }
+1134        let data = datagram.data();
+1135        if data.len() < RESET_TOKEN_SIZE {
+1136            return None;
+1137        }
+1138        // For stateless resets the PathId is meaningless since it closes the entire
+1139        // connection regardless of path.  So use PathId::ZERO.
+1140        self.connection_reset_tokens
+1141            .get(network_path.remote, &data[data.len() - RESET_TOKEN_SIZE..])
+1142            .cloned()
+1143            .map(|ch| RouteDatagramTo::Connection(ch, PathId::ZERO))
+1144    }
+1145}
+1146
+1147#[derive(Debug)]
+1148pub(crate) struct ConnectionMeta {
+1149    init_cid: ConnectionId,
+1150    /// Locally issues CIDs for each path
+1151    local_cids: FxHashMap<PathId, PathLocalCids>,
+1152    /// Remote/local addresses the connection began with
+1153    ///
+1154    /// Only needed to support connections with zero-length CIDs, which cannot migrate, so we don't
+1155    /// bother keeping it up to date.
+1156    network_path: FourTuple,
+1157    side: Side,
+1158    /// Reset tokens provided by the peer for CIDs we're currently sending to
+1159    ///
+1160    /// Since each reset token is for a CID, it is also for a fixed remote address which is
+1161    /// also stored. This allows us to look up which reset tokens we might expect from a
+1162    /// given remote address, see [`ResetTokenTable`].
+1163    ///
+1164    /// Each path has its own active CID. We use the [`PathId`] as a unique index, allowing
+1165    /// us to retire the reset token when a path is abandoned.
+1166    // TODO(matheus23): Should be migrated to make reset tokens per 4-tuple instead of per remote addr
+1167    reset_token: FxHashMap<PathId, (SocketAddr, ResetToken)>,
+1168}
+1169
+1170/// Local connection IDs for a single path
+1171#[derive(Debug, Default)]
+1172struct PathLocalCids {
+1173    /// Number of connection IDs that have been issued in (PATH_)NEW_CONNECTION_ID frames
+1174    ///
+1175    /// Another way of saying this is that this is the next sequence number to be issued.
+1176    issued: u64,
+1177    /// Issues CIDs indexed by their sequence number.
+1178    cids: FxHashMap<u64, ConnectionId>,
+1179}
+1180
+1181/// Internal identifier for a `Connection` currently associated with an endpoint
+1182#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
+1183pub struct ConnectionHandle(pub usize);
+1184
+1185impl From<ConnectionHandle> for usize {
+1186    fn from(x: ConnectionHandle) -> Self {
+1187        x.0
+1188    }
+1189}
+1190
+1191impl Index<ConnectionHandle> for Slab<ConnectionMeta> {
+1192    type Output = ConnectionMeta;
+1193    fn index(&self, ch: ConnectionHandle) -> &ConnectionMeta {
+1194        &self[ch.0]
+1195    }
+1196}
+1197
+1198impl IndexMut<ConnectionHandle> for Slab<ConnectionMeta> {
+1199    fn index_mut(&mut self, ch: ConnectionHandle) -> &mut ConnectionMeta {
+1200        &mut self[ch.0]
+1201    }
+1202}
+1203
+1204/// Event resulting from processing a single datagram
+1205pub enum DatagramEvent {
+1206    /// The datagram is redirected to its `Connection`
+1207    ConnectionEvent(ConnectionHandle, ConnectionEvent),
+1208    /// The datagram may result in starting a new `Connection`
+1209    NewConnection(Incoming),
+1210    /// Response generated directly by the endpoint
+1211    Response(Transmit),
+1212}
+1213
+1214/// An incoming connection for which the server has not yet begun its part of the handshake.
+1215#[derive(derive_more::Debug)]
+1216pub struct Incoming {
+1217    #[debug(skip)]
+1218    received_at: Instant,
+1219    network_path: FourTuple,
+1220    ecn: Option<EcnCodepoint>,
+1221    #[debug(skip)]
+1222    packet: InitialPacket,
+1223    #[debug(skip)]
+1224    rest: Option<BytesMut>,
+1225    #[debug(skip)]
+1226    crypto: Keys,
+1227    token: IncomingToken,
+1228    incoming_idx: usize,
+1229    #[debug(skip)]
+1230    improper_drop_warner: IncomingImproperDropWarner,
+1231}
+1232
+1233impl Incoming {
+1234    /// The local IP address which was used when the peer established the connection
+1235    pub fn local_ip(&self) -> Option<IpAddr> {
+1236        self.network_path.local_ip
+1237    }
+1238
+1239    /// The peer's UDP address
+1240    pub fn remote_address(&self) -> SocketAddr {
+1241        self.network_path.remote
+1242    }
+1243
+1244    /// Whether the socket address that is initiating this connection has been validated
+1245    ///
+1246    /// This means that the sender of the initial packet has proved that they can receive traffic
+1247    /// sent to `self.remote_address()`.
+1248    ///
+1249    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
+1250    /// The inverse is not guaranteed.
+1251    pub fn remote_address_validated(&self) -> bool {
+1252        self.token.validated
+1253    }
+1254
+1255    /// Whether it is legal to respond with a retry packet
+1256    ///
+1257    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
+1258    /// The inverse is not guaranteed.
+1259    pub fn may_retry(&self) -> bool {
+1260        self.token.retry_src_cid.is_none()
+1261    }
+1262
+1263    /// The original destination connection ID sent by the client
+1264    pub fn orig_dst_cid(&self) -> ConnectionId {
+1265        self.token.orig_dst_cid
+1266    }
+1267
+1268    /// Decrypt the Initial packet payload
+1269    ///
+1270    /// This clones and decrypts the packet payload (~1200 bytes).
+1271    /// Can be used to extract information from the TLS ClientHello without completing the handshake.
+1272    pub fn decrypt(&self) -> Option<DecryptedInitial> {
+1273        let packet_number = self.packet.header.number.expand(0);
+1274        let mut payload = self.packet.payload.clone();
+1275        self.crypto
+1276            .packet
+1277            .remote
+1278            .decrypt(
+1279                PathId::ZERO,
+1280                packet_number,
+1281                &self.packet.header_data,
+1282                &mut payload,
+1283            )
+1284            .ok()?;
+1285        Some(DecryptedInitial(payload.freeze()))
+1286    }
+1287}
+1288
+1289/// Decrypted payload of a QUIC Initial packet
+1290///
+1291/// Obtained via [`Incoming::decrypt`]. Can be used to extract information from
+1292/// the TLS ClientHello without completing the handshake.
+1293pub struct DecryptedInitial(Bytes);
+1294
+1295impl DecryptedInitial {
+1296    /// Best-effort extraction of the ALPN protocols from the TLS ClientHello
+1297    ///
+1298    /// Parses the CRYPTO frames to extract the ALPN extension. This is intended
+1299    /// for routing and filtering; it is not guaranteed to succeed if the
+1300    /// ClientHello spans multiple packets. Returns `None` if parsing fails.
+1301    pub fn alpns(&self) -> Option<IncomingAlpns> {
+1302        let frames = frame::Iter::new(self.0.clone()).ok()?;
+1303        let mut first = None;
+1304        let mut rest = Vec::new();
+1305        for frame in frames {
+1306            match frame {
+1307                Ok(frame::Frame::Crypto(crypto)) => match first {
+1308                    None => first = Some(crypto),
+1309                    Some(_) => rest.push(crypto),
+1310                },
+1311                Err(_) => return None,
+1312                _ => {}
+1313            }
+1314        }
+1315        let first = first?;
+1316
+1317        // Fast path: single CRYPTO frame at offset 0 (no extra allocation)
+1318        if rest.is_empty() && first.offset == 0 {
+1319            let data = find_alpn_data(&first.data).ok()?;
+1320            return Some(IncomingAlpns { data, pos: 0 });
+1321        }
+1322
+1323        // Slow path: reassemble multiple CRYPTO frames
+1324        rest.push(first);
+1325        let source = assemble_crypto_frames(&mut rest)?;
+1326        let data = find_alpn_data(&source).ok()?;
+1327        Some(IncomingAlpns { data, pos: 0 })
+1328    }
+1329}
+1330
+1331/// TLS handshake type for ClientHello messages
+1332/// <https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2>
+1333const TLS_HANDSHAKE_TYPE_CLIENT_HELLO: u8 = 0x01;
+1334/// TLS extension type for Application-Layer Protocol Negotiation
+1335/// <https://www.rfc-editor.org/rfc/rfc7301#section-3.1>
+1336const TLS_EXTENSION_TYPE_ALPN: u16 = 0x0010;
+1337/// Size of the fixed-length fields in a ClientHello (client_version + random)
+1338/// <https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2>
+1339const TLS_CLIENT_HELLO_FIXED_LEN: usize = 2 + 32;
+1340
+1341/// Iterator over ALPN protocol names from a TLS ClientHello
+1342///
+1343/// Yields protocol names as [`Bytes`] slices. On the common fast path (single
+1344/// CRYPTO frame), the only allocation is the payload clone for decryption.
+1345pub struct IncomingAlpns {
+1346    data: Bytes,
+1347    pos: usize,
+1348}
+1349
+1350impl Iterator for IncomingAlpns {
+1351    type Item = Result<Bytes, UnexpectedEnd>;
+1352
+1353    fn next(&mut self) -> Option<Self::Item> {
+1354        if self.pos >= self.data.len() {
+1355            return None;
+1356        }
+1357        let len = self.data[self.pos] as usize;
+1358        self.pos += 1;
+1359        if self.pos + len > self.data.len() {
+1360            return Some(Err(UnexpectedEnd));
+1361        }
+1362        let proto = self.data.slice(self.pos..self.pos + len);
+1363        self.pos += len;
+1364        Some(Ok(proto))
+1365    }
+1366}
+1367
+1368/// Sort CRYPTO frames by offset and concatenate into a contiguous `Bytes`
+1369///
+1370/// Returns `None` if there are gaps in the stream.
+1371fn assemble_crypto_frames(frames: &mut [frame::Crypto]) -> Option<Bytes> {
+1372    frames.sort_by_key(|f| f.offset);
+1373    let capacity = frames.iter().map(|f| f.data.len()).sum();
+1374    let mut buf = Vec::with_capacity(capacity);
+1375    for f in frames.iter() {
+1376        let start = f.offset as usize;
+1377        if start > buf.len() {
+1378            return None;
+1379        }
+1380        let end = start + f.data.len();
+1381        if end > buf.len() {
+1382            buf.extend_from_slice(&f.data[buf.len() - start..]);
+1383        }
+1384    }
+1385    Some(Bytes::from(buf))
+1386}
+1387
+1388/// Locate the raw ALPN protocol list data within a TLS ClientHello message
+1389///
+1390/// Parses the ClientHello in `source` and returns a [`Bytes`] containing the
+1391/// u8-length-prefixed protocol names (after the outer ProtocolNameList u16
+1392/// length prefix). The returned `Bytes` is a zero-copy slice of `source`.
+1393fn find_alpn_data(source: &Bytes) -> Result<Bytes, UnexpectedEnd> {
+1394    let mut r = &**source;
+1395
+1396    if u8::decode(&mut r)? != TLS_HANDSHAKE_TYPE_CLIENT_HELLO {
+1397        return Err(UnexpectedEnd);
+1398    }
+1399
+1400    // Handshake message length (u24), scopes the remainder
+1401    let len = decode_u24(&mut r)?;
+1402    let mut body = take(&mut r, len)?;
+1403
+1404    // Client version + random
+1405    skip(&mut body, TLS_CLIENT_HELLO_FIXED_LEN)?;
+1406
+1407    // Session ID, cipher suites, compression methods
+1408    skip_u8_prefixed(&mut body)?;
+1409    skip_u16_prefixed(&mut body)?;
+1410    skip_u8_prefixed(&mut body)?;
+1411
+1412    // Extensions
+1413    let mut exts = take_u16_prefixed(&mut body)?;
+1414    while exts.has_remaining() {
+1415        let ext_type = u16::decode(&mut exts)?;
+1416        let ext_data = take_u16_prefixed(&mut exts)?;
+1417        if ext_type == TLS_EXTENSION_TYPE_ALPN {
+1418            let list = take_u16_prefixed(&mut &*ext_data)?;
+1419            return Ok(source.slice_ref(list));
+1420        }
+1421    }
+1422    Err(UnexpectedEnd)
+1423}
+1424
+1425/// Decode a big-endian u24 as usize
+1426fn decode_u24(r: &mut &[u8]) -> Result<usize, UnexpectedEnd> {
+1427    let a = u8::decode(r)?;
+1428    let b = u8::decode(r)?;
+1429    let c = u8::decode(r)?;
+1430    Ok(u32::from_be_bytes([0, a, b, c]) as usize)
+1431}
+1432
+1433/// Take `len` bytes from the front and return them as a sub-slice
+1434fn take<'a>(r: &mut &'a [u8], len: usize) -> Result<&'a [u8], UnexpectedEnd> {
+1435    if r.remaining() < len {
+1436        return Err(UnexpectedEnd);
+1437    }
+1438    let data = &r[..len];
+1439    r.advance(len);
+1440    Ok(data)
+1441}
+1442
+1443/// Read a u16 length prefix and return the sub-slice it covers
+1444fn take_u16_prefixed<'a>(r: &mut &'a [u8]) -> Result<&'a [u8], UnexpectedEnd> {
+1445    let len = u16::decode(r)? as usize;
+1446    take(r, len)
+1447}
+1448
+1449/// Advance past `n` bytes
+1450fn skip(r: &mut &[u8], len: usize) -> Result<(), UnexpectedEnd> {
+1451    take(r, len)?;
+1452    Ok(())
+1453}
+1454
+1455/// Skip a u8-length-prefixed field
+1456fn skip_u8_prefixed(r: &mut &[u8]) -> Result<(), UnexpectedEnd> {
+1457    let len = u8::decode(r)? as usize;
+1458    skip(r, len)
+1459}
+1460
+1461/// Skip a u16-length-prefixed field
+1462fn skip_u16_prefixed(r: &mut &[u8]) -> Result<(), UnexpectedEnd> {
+1463    let len = u16::decode(r)? as usize;
+1464    skip(r, len)
+1465}
+1466
+1467struct IncomingImproperDropWarner;
+1468
+1469impl IncomingImproperDropWarner {
+1470    fn dismiss(self) {
+1471        mem::forget(self);
+1472    }
+1473}
+1474
+1475impl Drop for IncomingImproperDropWarner {
+1476    fn drop(&mut self) {
+1477        warn!(
+1478            "noq_proto::Incoming dropped without passing to Endpoint::accept/refuse/retry/ignore \
+1479               (may cause memory leak and eventual inability to accept new connections)"
+1480        );
+1481    }
+1482}
+1483
+1484/// Errors in the parameters being used to create a new connection
+1485///
+1486/// These arise before any I/O has been performed.
+1487#[derive(Debug, Error, Clone, PartialEq, Eq)]
+1488pub enum ConnectError {
+1489    /// The endpoint can no longer create new connections
+1490    ///
+1491    /// Indicates that a necessary component of the endpoint has been dropped or otherwise disabled.
+1492    #[error("endpoint stopping")]
+1493    EndpointStopping,
+1494    /// The connection could not be created because not enough of the CID space is available
+1495    ///
+1496    /// Try using longer connection IDs
+1497    #[error("CIDs exhausted")]
+1498    CidsExhausted,
+1499    /// The given server name was malformed
+1500    #[error("invalid server name: {0}")]
+1501    InvalidServerName(String),
+1502    /// The remote [`SocketAddr`] supplied was malformed
+1503    ///
+1504    /// Examples include attempting to connect to port 0, or using an inappropriate address family.
+1505    #[error("invalid remote address: {0}")]
+1506    InvalidRemoteAddress(SocketAddr),
+1507    /// No default client configuration was set up
+1508    ///
+1509    /// Use `Endpoint::connect_with` to specify a client configuration.
+1510    #[error("no default client config")]
+1511    NoDefaultClientConfig,
+1512    /// The local endpoint does not support the QUIC version specified in the client configuration
+1513    #[error("unsupported QUIC version")]
+1514    UnsupportedVersion,
+1515}
+1516
+1517/// Error type for attempting to accept an [`Incoming`]
+1518#[derive(Debug)]
+1519pub struct AcceptError {
+1520    /// Underlying error describing reason for failure
+1521    pub cause: ConnectionError,
+1522    /// Optional response to transmit back
+1523    pub response: Option<Transmit>,
+1524}
+1525
+1526/// Error for attempting to retry an [`Incoming`] which already bears a token from a previous retry
+1527#[derive(Debug, Error)]
+1528#[error("retry() with validated Incoming")]
+1529pub struct RetryError(Box<Incoming>);
+1530
+1531impl RetryError {
+1532    /// Get the [`Incoming`]
+1533    pub fn into_incoming(self) -> Incoming {
+1534        *self.0
+1535    }
+1536}
+1537
+1538/// Reset Tokens which are associated with peer socket addresses
+1539///
+1540/// The standard `HashMap` is used since both `SocketAddr` and `ResetToken` are
+1541/// peer generated and might be usable for hash collision attacks.
+1542#[derive(Default, Debug)]
+1543struct ResetTokenTable(HashMap<SocketAddr, HashMap<ResetToken, ConnectionHandle>>);
+1544
+1545impl ResetTokenTable {
+1546    fn insert(&mut self, remote: SocketAddr, token: ResetToken, ch: ConnectionHandle) -> bool {
+1547        self.0
+1548            .entry(remote)
+1549            .or_default()
+1550            .insert(token, ch)
+1551            .is_some()
+1552    }
+1553
+1554    fn remove(&mut self, remote: SocketAddr, token: ResetToken) {
+1555        use std::collections::hash_map::Entry;
+1556        match self.0.entry(remote) {
+1557            Entry::Vacant(_) => {}
+1558            Entry::Occupied(mut e) => {
+1559                e.get_mut().remove(&token);
+1560                if e.get().is_empty() {
+1561                    e.remove_entry();
+1562                }
+1563            }
+1564        }
+1565    }
+1566
+1567    fn get(&self, remote: SocketAddr, token: &[u8]) -> Option<&ConnectionHandle> {
+1568        let token = ResetToken::from(<[u8; RESET_TOKEN_SIZE]>::try_from(token).ok()?);
+1569        self.0.get(&remote)?.get(&token)
+1570    }
+1571}
+1572
+1573#[cfg(test)]
+1574mod tests {
+1575    use super::*;
+1576
+1577    #[test]
+1578    fn assemble_contiguous() {
+1579        let data = b"hello world";
+1580        let mut frames = vec![
+1581            frame::Crypto {
+1582                offset: 0,
+1583                data: Bytes::from_static(&data[..5]),
+1584            },
+1585            frame::Crypto {
+1586                offset: 5,
+1587                data: Bytes::from_static(&data[5..]),
+1588            },
+1589        ];
+1590        assert_eq!(&assemble_crypto_frames(&mut frames).unwrap()[..], &data[..]);
+1591    }
+1592
+1593    #[test]
+1594    fn assemble_out_of_order() {
+1595        let data = b"hello world";
+1596        let mut frames = vec![
+1597            frame::Crypto {
+1598                offset: 5,
+1599                data: Bytes::from_static(&data[5..]),
+1600            },
+1601            frame::Crypto {
+1602                offset: 0,
+1603                data: Bytes::from_static(&data[..5]),
+1604            },
+1605        ];
+1606        assert_eq!(&assemble_crypto_frames(&mut frames).unwrap()[..], &data[..]);
+1607    }
+1608
+1609    #[test]
+1610    fn assemble_with_overlap() {
+1611        let data = b"hello world";
+1612        let mut frames = vec![
+1613            frame::Crypto {
+1614                offset: 0,
+1615                data: Bytes::from_static(&data[..7]),
+1616            },
+1617            frame::Crypto {
+1618                offset: 5,
+1619                data: Bytes::from_static(&data[5..]),
+1620            },
+1621        ];
+1622        assert_eq!(&assemble_crypto_frames(&mut frames).unwrap()[..], &data[..]);
+1623    }
+1624
+1625    #[test]
+1626    fn assemble_with_gap() {
+1627        let mut frames = vec![frame::Crypto {
+1628            offset: 10,
+1629            data: Bytes::from_static(b"world"),
+1630        }];
+1631        assert!(assemble_crypto_frames(&mut frames).is_none());
+1632    }
+1633}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/frame.rs.html b/pr/509/docs/src/noq_proto/frame.rs.html new file mode 100644 index 000000000..768cb6a18 --- /dev/null +++ b/pr/509/docs/src/noq_proto/frame.rs.html @@ -0,0 +1,2828 @@ +frame.rs - source

noq_proto/
frame.rs

1use std::{
+2    fmt::{self, Display},
+3    mem,
+4    net::{IpAddr, SocketAddr},
+5    ops::{Range, RangeInclusive},
+6};
+7
+8use bytes::{Buf, BufMut, Bytes};
+9use tinyvec::TinyVec;
+10
+11use crate::{
+12    Dir, MAX_CID_SIZE, RESET_TOKEN_SIZE, ResetToken, StreamId, TransportError, TransportErrorCode,
+13    VarInt,
+14    coding::{self, BufExt, BufMutExt, Decodable, Encodable, UnexpectedEnd},
+15    connection::PathId,
+16    range_set::ArrayRangeSet,
+17    shared::{ConnectionId, EcnCodepoint},
+18};
+19
+20#[cfg(feature = "qlog")]
+21use super::connection::qlog::ToQlog;
+22
+23#[cfg(test)]
+24use crate::varint::varint_u64;
+25#[cfg(test)]
+26use proptest::{collection, prelude::any, strategy::Strategy};
+27#[cfg(test)]
+28use test_strategy::Arbitrary;
+29
+30#[derive(
+31    Copy, Clone, Eq, PartialEq, derive_more::Debug, derive_more::Display, enum_assoc::Assoc,
+32)]
+33#[cfg_attr(test, derive(Arbitrary))]
+34#[display(rename_all = "SCREAMING_SNAKE_CASE")]
+35#[allow(missing_docs)]
+36#[func(
+37    pub(crate) const fn to_u64(self) -> u64,
+38    const fn from_u64(rev: u64) -> Option<Self>,
+39)]
+40pub enum FrameType {
+41    #[assoc(to_u64 = 0x00)]
+42    Padding,
+43    #[assoc(to_u64 = 0x01)]
+44    Ping,
+45    #[assoc(to_u64 = 0x02)]
+46    Ack,
+47    #[assoc(to_u64 = 0x03)]
+48    AckEcn,
+49    #[assoc(to_u64 = 0x04)]
+50    ResetStream,
+51    #[assoc(to_u64 = 0x05)]
+52    StopSending,
+53    #[assoc(to_u64 = 0x06)]
+54    Crypto,
+55    #[assoc(to_u64 = 0x07)]
+56    NewToken,
+57    // STREAM
+58    #[assoc(to_u64 = _0.to_u64())]
+59    Stream(StreamInfo),
+60    #[assoc(to_u64 = 0x10)]
+61    MaxData,
+62    #[assoc(to_u64 = 0x11)]
+63    MaxStreamData,
+64    #[assoc(to_u64 = 0x12)]
+65    MaxStreamsBidi,
+66    #[assoc(to_u64 = 0x13)]
+67    MaxStreamsUni,
+68    #[assoc(to_u64 = 0x14)]
+69    DataBlocked,
+70    #[assoc(to_u64 = 0x15)]
+71    StreamDataBlocked,
+72    #[assoc(to_u64 = 0x16)]
+73    StreamsBlockedBidi,
+74    #[assoc(to_u64 = 0x17)]
+75    StreamsBlockedUni,
+76    #[assoc(to_u64 = 0x18)]
+77    NewConnectionId,
+78    #[assoc(to_u64 = 0x19)]
+79    RetireConnectionId,
+80    #[assoc(to_u64 = 0x1a)]
+81    PathChallenge,
+82    #[assoc(to_u64 = 0x1b)]
+83    PathResponse,
+84    #[assoc(to_u64 = 0x1c)]
+85    ConnectionClose,
+86    #[assoc(to_u64 = 0x1d)]
+87    ApplicationClose,
+88    #[assoc(to_u64 = 0x1e)]
+89    HandshakeDone,
+90    // ACK Frequency
+91    #[assoc(to_u64 = 0xaf)]
+92    AckFrequency,
+93    #[assoc(to_u64 = 0x1f)]
+94    ImmediateAck,
+95    // DATAGRAM
+96    #[assoc(to_u64 = _0.to_u64())]
+97    Datagram(DatagramInfo),
+98    // ADDRESS DISCOVERY REPORT
+99    #[assoc(to_u64 = 0x9f81a6)]
+100    ObservedIpv4Addr,
+101    #[assoc(to_u64 = 0x9f81a7)]
+102    ObservedIpv6Addr,
+103    // Multipath
+104    #[assoc(to_u64 = 0x3e)]
+105    PathAck,
+106    #[assoc(to_u64 = 0x3f)]
+107    PathAckEcn,
+108    #[assoc(to_u64 = 0x3e75)]
+109    PathAbandon,
+110    #[assoc(to_u64 = 0x3e76)]
+111    PathStatusBackup,
+112    #[assoc(to_u64 = 0x3e77)]
+113    PathStatusAvailable,
+114    #[assoc(to_u64 = 0x3e78)]
+115    PathNewConnectionId,
+116    #[assoc(to_u64 = 0x3e79)]
+117    PathRetireConnectionId,
+118    #[assoc(to_u64 = 0x3e7a)]
+119    MaxPathId,
+120    #[assoc(to_u64 = 0x3e7b)]
+121    PathsBlocked,
+122    #[assoc(to_u64 = 0x3e7c)]
+123    PathCidsBlocked,
+124    // IROH'S NAT TRAVERSAL
+125    #[assoc(to_u64 = 0x3d7f90)]
+126    AddIpv4Address,
+127    #[assoc(to_u64 = 0x3d7f91)]
+128    AddIpv6Address,
+129    #[assoc(to_u64 = 0x3d7f92)]
+130    ReachOutAtIpv4,
+131    #[assoc(to_u64 = 0x3d7f93)]
+132    ReachOutAtIpv6,
+133    #[assoc(to_u64 = 0x3d7f94)]
+134    RemoveAddress,
+135}
+136
+137/// Encounter a frame ID that was not valid.
+138#[derive(Debug, PartialEq, Eq, thiserror::Error)]
+139#[error("Invalid frame identifier {_0:02x}")]
+140pub struct InvalidFrameId(u64);
+141
+142impl TryFrom<u64> for FrameType {
+143    type Error = InvalidFrameId;
+144    fn try_from(value: u64) -> Result<Self, Self::Error> {
+145        match Self::from_u64(value) {
+146            Some(t) => Ok(t),
+147            None => {
+148                if DatagramInfo::VALUES.contains(&value) {
+149                    return Ok(Self::Datagram(DatagramInfo(value as u8)));
+150                }
+151                if StreamInfo::VALUES.contains(&value) {
+152                    return Ok(Self::Stream(StreamInfo(value as u8)));
+153                }
+154                Err(InvalidFrameId(value))
+155            }
+156        }
+157    }
+158}
+159
+160impl FrameType {
+161    /// The encoded size of this [`FrameType`].
+162    const fn size(&self) -> usize {
+163        VarInt(self.to_u64()).size()
+164    }
+165}
+166
+167impl Decodable for FrameType {
+168    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+169        Self::try_from(buf.get_var()?).map_err(|_| coding::UnexpectedEnd)
+170    }
+171}
+172
+173impl Encodable for FrameType {
+174    fn encode<B: BufMut>(&self, buf: &mut B) {
+175        buf.write_var(self.to_u64());
+176    }
+177}
+178
+179/// Wrapper type for the encodable frames.
+180///
+181/// This includes some "encoder" types instead of the actual read frame, when writing directly to
+182/// a buffer is more efficient than building the Frame itself.
+183#[derive(derive_more::From, enum_assoc::Assoc, derive_more::Display)]
+184#[func(fn encode_inner<B: BufMut>(&self, buf: &mut B) {_0.encode(buf)})]
+185#[func(pub(crate) const fn get_type(&self) -> FrameType {_0.get_type()})]
+186#[cfg_attr(feature = "qlog", func(pub(crate) fn to_qlog(&self) -> qlog::events::quic::QuicFrame {_0.to_qlog()}))]
+187pub(super) enum EncodableFrame<'a> {
+188    PathAck(PathAckEncoder<'a>),
+189    Ack(AckEncoder<'a>),
+190    Close(CloseEncoder<'a>),
+191    PathResponse(PathResponse),
+192    HandshakeDone(HandshakeDone),
+193    ReachOut(ReachOut),
+194    ObservedAddr(ObservedAddr),
+195    Ping(Ping),
+196    ImmediateAck(ImmediateAck),
+197    AckFrequency(AckFrequency),
+198    PathChallenge(PathChallenge),
+199    Crypto(Crypto),
+200    PathAbandon(PathAbandon),
+201    PathStatusAvailable(PathStatusAvailable),
+202    PathStatusBackup(PathStatusBackup),
+203    MaxPathId(MaxPathId),
+204    PathsBlocked(PathsBlocked),
+205    PathCidsBlocked(PathCidsBlocked),
+206    ResetStream(ResetStream),
+207    StopSending(StopSending),
+208    NewConnectionId(NewConnectionId),
+209    RetireConnectionId(RetireConnectionId),
+210    Datagram(Datagram),
+211    NewToken(NewToken),
+212    AddAddress(AddAddress),
+213    RemoveAddress(RemoveAddress),
+214    StreamMeta(StreamMetaEncoder),
+215    MaxData(MaxData),
+216    MaxStreamData(MaxStreamData),
+217    MaxStreams(MaxStreams),
+218}
+219
+220impl<'a> Encodable for EncodableFrame<'a> {
+221    fn encode<B: BufMut>(&self, buf: &mut B) {
+222        self.encode_inner(buf)
+223    }
+224}
+225
+226pub(crate) trait FrameStruct {
+227    /// Smallest number of bytes this type of frame is guaranteed to fit within.
+228    const SIZE_BOUND: usize;
+229}
+230
+231/// The type used to refer to [`FrameType`]s in closing and transport errors.
+232#[derive(Copy, Clone, Eq, PartialEq, derive_more::Debug, derive_more::Display)]
+233pub enum MaybeFrame {
+234    /// Not attributed to any particular [`FrameType`].
+235    None,
+236    /// Attributed to some frame type this implementation does not recognize.
+237    #[display("UNKNOWN({:02x})", _0)]
+238    #[debug("Unknown({:02x})", _0)]
+239    Unknown(u64),
+240    /// Attributed to a specific [`FrameType`], never [`FrameType::Padding`].
+241    Known(FrameType),
+242}
+243
+244impl MaybeFrame {
+245    /// Encoded size of this [`MaybeFrame`].
+246    const fn size(&self) -> usize {
+247        match self {
+248            Self::None => VarInt(0).size(),
+249            Self::Unknown(other) => VarInt(*other).size(),
+250            Self::Known(frame_type) => frame_type.size(),
+251        }
+252    }
+253}
+254
+255impl Decodable for MaybeFrame {
+256    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+257        match FrameType::try_from(buf.get_var()?) {
+258            Ok(FrameType::Padding) => Ok(Self::None),
+259            Ok(other_frame) => Ok(Self::Known(other_frame)),
+260            Err(InvalidFrameId(other)) => Ok(Self::Unknown(other)),
+261        }
+262    }
+263}
+264
+265impl Encodable for MaybeFrame {
+266    fn encode<B: BufMut>(&self, buf: &mut B) {
+267        match self {
+268            Self::None => buf.write_var(0u64),
+269            Self::Unknown(frame_id) => buf.write_var(*frame_id),
+270            Self::Known(frame_type) => buf.write(*frame_type),
+271        }
+272    }
+273}
+274
+275#[cfg(test)]
+276impl proptest::arbitrary::Arbitrary for MaybeFrame {
+277    type Parameters = ();
+278    type Strategy = proptest::strategy::BoxedStrategy<Self>;
+279
+280    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
+281        use proptest::prelude::*;
+282        prop_oneof![
+283            Just(Self::None),
+284            any::<VarInt>().prop_map(|v| Self::Unknown(v.0)),
+285            // do not generate padding frames here, since they are not allowed in MaybeFrame::Known
+286            any::<FrameType>()
+287                .prop_filter("not Padding", |ft| *ft != FrameType::Padding)
+288                .prop_map(MaybeFrame::Known),
+289        ]
+290        .boxed()
+291    }
+292}
+293
+294#[derive(derive_more::Display)]
+295#[cfg_attr(test, derive(Arbitrary, Debug, Clone, PartialEq, Eq))]
+296#[display("HANDSHAKE_DONE")]
+297pub(crate) struct HandshakeDone;
+298
+299impl HandshakeDone {
+300    const fn get_type(&self) -> FrameType {
+301        FrameType::HandshakeDone
+302    }
+303}
+304
+305impl Encodable for HandshakeDone {
+306    fn encode<B: BufMut>(&self, buf: &mut B) {
+307        FrameType::HandshakeDone.encode(buf);
+308    }
+309}
+310
+311#[derive(derive_more::Display)]
+312#[cfg_attr(test, derive(Arbitrary, Debug, Clone, PartialEq, Eq))]
+313#[display("PING")]
+314pub(crate) struct Ping;
+315
+316impl Ping {
+317    const fn get_type(&self) -> FrameType {
+318        FrameType::Ping
+319    }
+320}
+321
+322impl Encodable for Ping {
+323    fn encode<B: BufMut>(&self, buf: &mut B) {
+324        FrameType::Ping.encode(buf);
+325    }
+326}
+327
+328#[derive(derive_more::Display)]
+329#[cfg_attr(test, derive(Arbitrary, Debug, Clone, PartialEq, Eq))]
+330#[display("IMMEDIATE_ACK")]
+331pub(crate) struct ImmediateAck;
+332
+333impl ImmediateAck {
+334    const fn get_type(&self) -> FrameType {
+335        FrameType::ImmediateAck
+336    }
+337}
+338
+339impl Encodable for ImmediateAck {
+340    fn encode<B: BufMut>(&self, buf: &mut B) {
+341        FrameType::ImmediateAck.encode(buf);
+342    }
+343}
+344
+345#[allow(missing_docs)]
+346#[derive(Debug, Copy, Clone, Eq, PartialEq, derive_more::Display)]
+347#[display("STREAM")]
+348#[cfg_attr(test, derive(Arbitrary))]
+349pub struct StreamInfo(#[cfg_attr(test, strategy(0x08u8..=0x0f))] u8);
+350
+351impl StreamInfo {
+352    const VALUES: RangeInclusive<u64> = RangeInclusive::new(0x08, 0x0f);
+353    fn fin(self) -> bool {
+354        self.0 & 0x01 != 0
+355    }
+356    fn len(self) -> bool {
+357        self.0 & 0x02 != 0
+358    }
+359    fn off(self) -> bool {
+360        self.0 & 0x04 != 0
+361    }
+362
+363    const fn to_u64(self) -> u64 {
+364        self.0 as u64
+365    }
+366}
+367
+368#[allow(missing_docs)]
+369#[derive(Debug, Copy, Clone, Eq, PartialEq, derive_more::Display)]
+370#[display("DATAGRAM")]
+371#[cfg_attr(test, derive(Arbitrary))]
+372pub struct DatagramInfo(#[cfg_attr(test, strategy(0x30u8..=0x31))] u8);
+373
+374impl DatagramInfo {
+375    const VALUES: RangeInclusive<u64> = RangeInclusive::new(0x30, 0x31);
+376
+377    fn len(self) -> bool {
+378        self.0 & 0x01 != 0
+379    }
+380
+381    const fn to_u64(self) -> u64 {
+382        self.0 as u64
+383    }
+384}
+385
+386#[derive(Debug, derive_more::Display)]
+387#[cfg_attr(test, derive(Arbitrary))]
+388pub(crate) enum Frame {
+389    #[display("PADDING")]
+390    Padding,
+391    #[display("PING")]
+392    Ping,
+393    Ack(Ack),
+394    PathAck(PathAck),
+395    ResetStream(ResetStream),
+396    StopSending(StopSending),
+397    Crypto(Crypto),
+398    NewToken(NewToken),
+399    Stream(Stream),
+400    MaxData(MaxData),
+401    MaxStreamData(MaxStreamData),
+402    MaxStreams(MaxStreams),
+403    DataBlocked(DataBlocked),
+404    StreamDataBlocked(StreamDataBlocked),
+405    StreamsBlocked(StreamsBlocked),
+406    NewConnectionId(NewConnectionId),
+407    RetireConnectionId(RetireConnectionId),
+408    PathChallenge(PathChallenge),
+409    PathResponse(PathResponse),
+410    Close(Close),
+411    Datagram(Datagram),
+412    AckFrequency(AckFrequency),
+413    #[display("IMMEDIATE_ACK")]
+414    ImmediateAck,
+415    #[display("HANDSHAKE_DONE")]
+416    HandshakeDone,
+417    ObservedAddr(ObservedAddr),
+418    PathAbandon(PathAbandon),
+419    PathStatusAvailable(PathStatusAvailable),
+420    PathStatusBackup(PathStatusBackup),
+421    MaxPathId(MaxPathId),
+422    PathsBlocked(PathsBlocked),
+423    PathCidsBlocked(PathCidsBlocked),
+424    AddAddress(AddAddress),
+425    ReachOut(ReachOut),
+426    RemoveAddress(RemoveAddress),
+427}
+428
+429impl Frame {
+430    pub(crate) fn ty(&self) -> FrameType {
+431        use Frame::*;
+432        match &self {
+433            Padding => FrameType::Padding,
+434            ResetStream(_) => FrameType::ResetStream,
+435            Close(self::Close::Connection(_)) => FrameType::ConnectionClose,
+436            Close(self::Close::Application(_)) => FrameType::ConnectionClose,
+437            MaxData(_) => FrameType::MaxData,
+438            MaxStreamData(_) => FrameType::MaxStreamData,
+439            MaxStreams(max_streams) => max_streams.get_type(),
+440            Ping => FrameType::Ping,
+441            DataBlocked(_) => FrameType::DataBlocked,
+442            StreamDataBlocked(sdb) => sdb.get_type(),
+443            StreamsBlocked(sb) => sb.get_type(),
+444            StopSending { .. } => FrameType::StopSending,
+445            RetireConnectionId(retire_frame) => retire_frame.get_type(),
+446            Ack(ack) => ack.get_type(),
+447            PathAck(path_ack) => path_ack.get_type(),
+448            Stream(x) => {
+449                let mut ty = *StreamInfo::VALUES.start() as u8;
+450                if x.fin {
+451                    ty |= 0x01;
+452                }
+453                if x.offset != 0 {
+454                    ty |= 0x04;
+455                }
+456                // TODO(@divma): move all this to getframetype for Stream
+457                FrameType::Stream(StreamInfo(ty))
+458            }
+459            PathChallenge(_) => FrameType::PathChallenge,
+460            PathResponse(_) => FrameType::PathResponse,
+461            NewConnectionId(cid) => cid.get_type(),
+462            Crypto(_) => FrameType::Crypto,
+463            NewToken(_) => FrameType::NewToken,
+464            Datagram(_) => FrameType::Datagram(DatagramInfo(*DatagramInfo::VALUES.start() as u8)),
+465            AckFrequency(_) => FrameType::AckFrequency,
+466            ImmediateAck => FrameType::ImmediateAck,
+467            HandshakeDone => FrameType::HandshakeDone,
+468            ObservedAddr(observed) => observed.get_type(),
+469            PathAbandon(_) => FrameType::PathAbandon,
+470            PathStatusAvailable(_) => FrameType::PathStatusAvailable,
+471            PathStatusBackup(_) => FrameType::PathStatusBackup,
+472            MaxPathId(_) => FrameType::MaxPathId,
+473            PathsBlocked(_) => FrameType::PathsBlocked,
+474            PathCidsBlocked(_) => FrameType::PathCidsBlocked,
+475            AddAddress(frame) => frame.get_type(),
+476            ReachOut(frame) => frame.get_type(),
+477            RemoveAddress(_) => self::RemoveAddress::TYPE,
+478        }
+479    }
+480
+481    pub(crate) fn is_ack_eliciting(&self) -> bool {
+482        !matches!(
+483            *self,
+484            Self::Ack(_) | Self::PathAck(_) | Self::Padding | Self::Close(_)
+485        )
+486    }
+487
+488    /// Returns `true` if this frame MUST be sent in 1-RTT space
+489    pub(crate) fn is_1rtt(&self) -> bool {
+490        // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-4-1:
+491        // > All frames defined in this document MUST only be sent in 1-RTT packets.
+492        // > If an endpoint receives a multipath-specific frame in a different packet type, it MUST close the
+493        // > connection with an error of type PROTOCOL_VIOLATION.
+494
+495        self.is_multipath_frame() || self.is_qad_frame()
+496    }
+497
+498    fn is_qad_frame(&self) -> bool {
+499        matches!(*self, Self::ObservedAddr(_))
+500    }
+501
+502    fn is_multipath_frame(&self) -> bool {
+503        matches!(
+504            *self,
+505            Self::PathAck(_)
+506                | Self::PathAbandon(_)
+507                | Self::PathStatusBackup(_)
+508                | Self::PathStatusAvailable(_)
+509                | Self::MaxPathId(_)
+510                | Self::PathsBlocked(_)
+511                | Self::PathCidsBlocked(_)
+512                | Self::NewConnectionId(NewConnectionId {
+513                    path_id: Some(_),
+514                    ..
+515                })
+516                | Self::RetireConnectionId(RetireConnectionId {
+517                    path_id: Some(_),
+518                    ..
+519                })
+520        )
+521    }
+522}
+523
+524#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display)]
+525#[cfg_attr(test, derive(Arbitrary))]
+526#[display("PATH_CHALLENGE({_0:08x})")]
+527pub(crate) struct PathChallenge(pub(crate) u64);
+528
+529impl PathChallenge {
+530    pub(crate) const SIZE_BOUND: usize = 9;
+531
+532    const fn get_type(&self) -> FrameType {
+533        FrameType::PathChallenge
+534    }
+535}
+536
+537impl Decodable for PathChallenge {
+538    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+539        Ok(Self(buf.get()?))
+540    }
+541}
+542
+543impl Encodable for PathChallenge {
+544    fn encode<B: BufMut>(&self, buf: &mut B) {
+545        buf.write(FrameType::PathChallenge);
+546        buf.write(self.0);
+547    }
+548}
+549
+550#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Display)]
+551#[cfg_attr(test, derive(Arbitrary))]
+552#[display("PATH_RESPONSE({_0:08x})")]
+553pub(crate) struct PathResponse(pub(crate) u64);
+554
+555impl PathResponse {
+556    pub(crate) const SIZE_BOUND: usize = 9;
+557
+558    const fn get_type(&self) -> FrameType {
+559        FrameType::PathResponse
+560    }
+561}
+562
+563impl Decodable for PathResponse {
+564    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+565        Ok(Self(buf.get()?))
+566    }
+567}
+568
+569impl Encodable for PathResponse {
+570    fn encode<B: BufMut>(&self, buf: &mut B) {
+571        buf.write(FrameType::PathResponse);
+572        buf.write(self.0);
+573    }
+574}
+575
+576#[derive(Debug, Clone, Copy, derive_more::Display)]
+577#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+578#[display("DATA_BLOCKED offset: {_0}")]
+579pub(crate) struct DataBlocked(#[cfg_attr(test, strategy(varint_u64()))] pub(crate) u64);
+580
+581impl Encodable for DataBlocked {
+582    fn encode<B: BufMut>(&self, buf: &mut B) {
+583        buf.write(FrameType::DataBlocked);
+584        buf.write_var(self.0);
+585    }
+586}
+587
+588#[derive(Debug, Clone, Copy, derive_more::Display)]
+589#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+590#[display("STREAM_DATA_BLOCKED id: {id} offset: {offset}")]
+591pub(crate) struct StreamDataBlocked {
+592    pub(crate) id: StreamId,
+593    #[cfg_attr(test, strategy(varint_u64()))]
+594    pub(crate) offset: u64,
+595}
+596
+597impl StreamDataBlocked {
+598    const fn get_type(&self) -> FrameType {
+599        FrameType::StreamDataBlocked
+600    }
+601}
+602
+603impl Encodable for StreamDataBlocked {
+604    fn encode<B: BufMut>(&self, buf: &mut B) {
+605        buf.write(FrameType::StreamDataBlocked);
+606        buf.write(self.id);
+607        buf.write_var(self.offset);
+608    }
+609}
+610
+611#[derive(Debug, Clone, Copy, derive_more::Display)]
+612#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+613#[display("STREAMS_BLOCKED dir: {:?} limit: {limit}", dir)]
+614pub(crate) struct StreamsBlocked {
+615    pub(crate) dir: Dir,
+616    #[cfg_attr(test, strategy(varint_u64()))]
+617    pub(crate) limit: u64,
+618}
+619
+620impl StreamsBlocked {
+621    const fn get_type(&self) -> FrameType {
+622        match self.dir {
+623            Dir::Bi => FrameType::StreamsBlockedBidi,
+624            Dir::Uni => FrameType::StreamsBlockedUni,
+625        }
+626    }
+627}
+628
+629impl Encodable for StreamsBlocked {
+630    fn encode<B: BufMut>(&self, buf: &mut B) {
+631        buf.write(self.get_type());
+632        buf.write_var(self.limit);
+633    }
+634}
+635
+636#[derive(Debug, Clone, Copy, derive_more::Display)]
+637#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+638#[display("MAX_DATA({_0})")]
+639pub(crate) struct MaxData(pub(crate) VarInt);
+640
+641impl MaxData {
+642    const fn get_type(&self) -> FrameType {
+643        FrameType::MaxData
+644    }
+645}
+646
+647impl Decodable for MaxData {
+648    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+649        Ok(Self(buf.get()?))
+650    }
+651}
+652
+653impl Encodable for MaxData {
+654    fn encode<B: BufMut>(&self, buf: &mut B) {
+655        buf.write(FrameType::MaxData);
+656        buf.write(self.0);
+657    }
+658}
+659
+660#[derive(Debug, Clone, Copy, derive_more::Display)]
+661#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+662#[display("MAX_STREAM_DATA id: {id} max: {offset}")]
+663pub(crate) struct MaxStreamData {
+664    pub(crate) id: StreamId,
+665    #[cfg_attr(test, strategy(varint_u64()))]
+666    pub(crate) offset: u64,
+667}
+668
+669impl MaxStreamData {
+670    const fn get_type(&self) -> FrameType {
+671        FrameType::MaxStreamData
+672    }
+673}
+674
+675impl Decodable for MaxStreamData {
+676    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+677        Ok(Self {
+678            id: buf.get()?,
+679            offset: buf.get_var()?,
+680        })
+681    }
+682}
+683
+684impl Encodable for MaxStreamData {
+685    fn encode<B: BufMut>(&self, buf: &mut B) {
+686        buf.write(FrameType::MaxStreamData);
+687        buf.write(self.id);
+688        buf.write_var(self.offset);
+689    }
+690}
+691
+692#[derive(Debug, Clone, Copy, derive_more::Display)]
+693#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+694#[display("{} count: {count}", self.get_type())]
+695pub(crate) struct MaxStreams {
+696    pub(crate) dir: Dir,
+697    #[cfg_attr(test, strategy(varint_u64()))]
+698    pub(crate) count: u64,
+699}
+700
+701impl MaxStreams {
+702    const fn get_type(&self) -> FrameType {
+703        match self.dir {
+704            Dir::Bi => FrameType::MaxStreamsBidi,
+705            Dir::Uni => FrameType::MaxStreamsUni,
+706        }
+707    }
+708}
+709
+710impl Encodable for MaxStreams {
+711    fn encode<B: BufMut>(&self, buf: &mut B) {
+712        buf.write(self.get_type());
+713        buf.write_var(self.count);
+714    }
+715}
+716
+717#[derive(Debug, PartialEq, Eq, derive_more::Display)]
+718#[cfg_attr(test, derive(Arbitrary))]
+719#[display("{} {} seq: {sequence}", self.get_type(), DisplayOption::new("path_id", path_id.as_ref()))]
+720pub(crate) struct RetireConnectionId {
+721    pub(crate) path_id: Option<PathId>,
+722    #[cfg_attr(test, strategy(varint_u64()))]
+723    pub(crate) sequence: u64,
+724}
+725
+726impl RetireConnectionId {
+727    /// Maximum size of this frame when the frame type is [`FrameType::RetireConnectionId`]
+728    pub(crate) const SIZE_BOUND: usize = {
+729        let type_len = FrameType::RetireConnectionId.size();
+730        let seq_max_len = 8usize;
+731        type_len + seq_max_len
+732    };
+733
+734    /// Maximum size of this frame when the frame type is [`FrameType::PathRetireConnectionId`]
+735    pub(crate) const SIZE_BOUND_MULTIPATH: usize = {
+736        let type_len = FrameType::PathRetireConnectionId.size();
+737        let path_id_len = VarInt::from_u32(u32::MAX).size();
+738        let seq_max_len = 8usize;
+739        type_len + path_id_len + seq_max_len
+740    };
+741
+742    /// Decode [`Self`] from the buffer, provided that the frame type has been verified (either
+743    /// [`FrameType::PathRetireConnectionId`], or [`FrameType::RetireConnectionId`])
+744    pub(crate) fn decode<R: Buf>(bytes: &mut R, read_path: bool) -> coding::Result<Self> {
+745        Ok(Self {
+746            path_id: if read_path { Some(bytes.get()?) } else { None },
+747            sequence: bytes.get_var()?,
+748        })
+749    }
+750
+751    /// Get the [`FrameType`] for this [`RetireConnectionId`]
+752    const fn get_type(&self) -> FrameType {
+753        if self.path_id.is_some() {
+754            FrameType::PathRetireConnectionId
+755        } else {
+756            FrameType::RetireConnectionId
+757        }
+758    }
+759
+760    /// Returns the maximum encoded size on the wire
+761    ///
+762    /// `path_retire_cid` determines whether this frame is a multipath frame. This is a rough upper
+763    /// estimate, does not squeeze every last byte out.
+764    pub(crate) const fn size_bound(path_retire_cid: bool) -> usize {
+765        match path_retire_cid {
+766            true => Self::SIZE_BOUND_MULTIPATH,
+767            false => Self::SIZE_BOUND,
+768        }
+769    }
+770}
+771
+772impl Encodable for RetireConnectionId {
+773    fn encode<W: BufMut>(&self, buf: &mut W) {
+774        buf.write(self.get_type());
+775        if let Some(id) = self.path_id {
+776            buf.write(id);
+777        }
+778        buf.write_var(self.sequence);
+779    }
+780}
+781
+782#[cfg_attr(test, derive(Arbitrary))]
+783#[derive(Clone, Debug, derive_more::Display)]
+784pub(crate) enum Close {
+785    Connection(ConnectionClose),
+786    Application(ApplicationClose),
+787}
+788
+789impl Close {
+790    pub(crate) fn encoder(&self, max_len: usize) -> CloseEncoder<'_> {
+791        CloseEncoder {
+792            close: self,
+793            max_len,
+794        }
+795    }
+796
+797    pub(crate) fn is_transport_layer(&self) -> bool {
+798        matches!(*self, Self::Connection(_))
+799    }
+800}
+801
+802#[derive(derive_more::Display)]
+803#[display("{close}")]
+804pub(crate) struct CloseEncoder<'a> {
+805    pub(crate) close: &'a Close,
+806    max_len: usize,
+807}
+808
+809impl<'a> CloseEncoder<'a> {
+810    const fn get_type(&self) -> FrameType {
+811        match self.close {
+812            Close::Connection(_) => FrameType::ConnectionClose,
+813            Close::Application(_) => FrameType::ApplicationClose,
+814        }
+815    }
+816}
+817
+818impl<'a> Encodable for CloseEncoder<'a> {
+819    fn encode<W: BufMut>(&self, out: &mut W) {
+820        match self.close {
+821            Close::Connection(x) => x.encode(out, self.max_len),
+822            Close::Application(x) => x.encode(out, self.max_len),
+823        }
+824    }
+825}
+826
+827impl From<TransportError> for Close {
+828    fn from(x: TransportError) -> Self {
+829        Self::Connection(x.into())
+830    }
+831}
+832impl From<ConnectionClose> for Close {
+833    fn from(x: ConnectionClose) -> Self {
+834        Self::Connection(x)
+835    }
+836}
+837impl From<ApplicationClose> for Close {
+838    fn from(x: ApplicationClose) -> Self {
+839        Self::Application(x)
+840    }
+841}
+842
+843/// Reason given by the transport for closing the connection
+844#[derive(Debug, Clone, PartialEq, Eq)]
+845#[cfg_attr(test, derive(Arbitrary))]
+846pub struct ConnectionClose {
+847    /// Class of error as encoded in the specification
+848    pub error_code: TransportErrorCode,
+849    /// Type of frame that caused the close
+850    pub frame_type: MaybeFrame,
+851    /// Human-readable reason for the close
+852    #[cfg_attr(test, strategy(proptest::collection::vec(any::<u8>(), 0..64).prop_map(Bytes::from)))]
+853    pub reason: Bytes,
+854}
+855
+856impl fmt::Display for ConnectionClose {
+857    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+858        self.error_code.fmt(f)?;
+859        if !self.reason.as_ref().is_empty() {
+860            f.write_str(": ")?;
+861            f.write_str(&String::from_utf8_lossy(&self.reason))?;
+862        }
+863        Ok(())
+864    }
+865}
+866
+867impl From<TransportError> for ConnectionClose {
+868    fn from(x: TransportError) -> Self {
+869        Self {
+870            error_code: x.code,
+871            frame_type: x.frame,
+872            reason: x.reason.into(),
+873        }
+874    }
+875}
+876
+877impl FrameStruct for ConnectionClose {
+878    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+879}
+880
+881impl ConnectionClose {
+882    pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) {
+883        out.write(FrameType::ConnectionClose); // 1 byte
+884        out.write(self.error_code); // <= 8 bytes
+885        out.write(self.frame_type); // <= 8 bytes
+886        let max_len = max_len
+887            - 3
+888            - self.frame_type.size()
+889            - VarInt::from_u64(self.reason.len() as u64).unwrap().size();
+890        let actual_len = self.reason.len().min(max_len);
+891        out.write_var(actual_len as u64); // <= 8 bytes
+892        out.put_slice(&self.reason[0..actual_len]); // whatever's left
+893    }
+894}
+895
+896/// Reason given by an application for closing the connection
+897#[derive(Debug, Clone, PartialEq, Eq)]
+898#[cfg_attr(test, derive(Arbitrary))]
+899pub struct ApplicationClose {
+900    /// Application-specific reason code
+901    pub error_code: VarInt,
+902    /// Human-readable reason for the close
+903    #[cfg_attr(test, strategy(proptest::collection::vec(any::<u8>(), 0..64).prop_map(Bytes::from)))]
+904    pub reason: Bytes,
+905}
+906
+907impl fmt::Display for ApplicationClose {
+908    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+909        if !self.reason.as_ref().is_empty() {
+910            f.write_str(&String::from_utf8_lossy(&self.reason))?;
+911            f.write_str(" (code ")?;
+912            self.error_code.fmt(f)?;
+913            f.write_str(")")?;
+914        } else {
+915            self.error_code.fmt(f)?;
+916        }
+917        Ok(())
+918    }
+919}
+920
+921impl FrameStruct for ApplicationClose {
+922    const SIZE_BOUND: usize = 1 + 8 + 8;
+923}
+924
+925impl ApplicationClose {
+926    pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) {
+927        out.write(FrameType::ApplicationClose); // 1 byte
+928        out.write(self.error_code); // <= 8 bytes
+929        let max_len = max_len - 3 - VarInt::from_u64(self.reason.len() as u64).unwrap().size();
+930        let actual_len = self.reason.len().min(max_len);
+931        out.write_var(actual_len as u64); // <= 8 bytes
+932        out.put_slice(&self.reason[0..actual_len]); // whatever's left
+933    }
+934}
+935
+936#[derive(Debug, Clone, Eq, PartialEq, derive_more::Display)]
+937#[display("{} path_id: {path_id} ranges: {ranges:?} delay: {delay}µs", self.get_type())]
+938pub(crate) struct PathAck {
+939    pub path_id: PathId,
+940    pub largest: u64,
+941    pub delay: u64,
+942    pub ranges: ArrayRangeSet,
+943    pub ecn: Option<EcnCounts>,
+944}
+945
+946#[cfg(test)]
+947impl proptest::arbitrary::Arbitrary for PathAck {
+948    type Parameters = ();
+949    type Strategy = proptest::strategy::BoxedStrategy<Self>;
+950
+951    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
+952        use proptest::prelude::*;
+953        (
+954            any::<PathId>(),
+955            varint_u64(),
+956            any::<ArrayRangeSet>()
+957                .prop_filter("ranges must be non empty", |ranges| !ranges.is_empty()),
+958            any::<Option<EcnCounts>>(),
+959        )
+960            .prop_map(|(path_id, delay, ranges, ecn)| Self {
+961                path_id,
+962                largest: ranges.max().expect("ranges must be non empty"),
+963                delay,
+964                ranges,
+965                ecn,
+966            })
+967            .boxed()
+968    }
+969}
+970
+971impl PathAck {
+972    pub(crate) fn into_ack(self) -> (Ack, PathId) {
+973        let ack = Ack {
+974            largest: self.largest,
+975            delay: self.delay,
+976            ranges: self.ranges,
+977            ecn: self.ecn,
+978        };
+979
+980        (ack, self.path_id)
+981    }
+982
+983    fn get_type(&self) -> FrameType {
+984        if self.ecn.is_some() {
+985            FrameType::PathAckEcn
+986        } else {
+987            FrameType::PathAck
+988        }
+989    }
+990
+991    pub(crate) fn encoder<'a>(
+992        path_id: PathId,
+993        delay: u64,
+994        ranges: &'a ArrayRangeSet,
+995        ecn: Option<&'a EcnCounts>,
+996    ) -> PathAckEncoder<'a> {
+997        PathAckEncoder {
+998            path_id,
+999            delay,
+1000            ranges,
+1001            ecn,
+1002        }
+1003    }
+1004}
+1005
+1006#[derive(derive_more::Display)]
+1007#[display("{} path_id: {path_id} ranges: {ranges:?} delay: {delay}µs", self.get_type())]
+1008pub(crate) struct PathAckEncoder<'a> {
+1009    pub(super) path_id: PathId,
+1010    pub(super) delay: u64,
+1011    pub(super) ranges: &'a ArrayRangeSet,
+1012    pub(super) ecn: Option<&'a EcnCounts>,
+1013}
+1014
+1015impl<'a> PathAckEncoder<'a> {
+1016    const fn get_type(&self) -> FrameType {
+1017        match self.ecn.is_some() {
+1018            true => FrameType::PathAckEcn,
+1019            false => FrameType::PathAck,
+1020        }
+1021    }
+1022}
+1023
+1024impl<'a> Encodable for PathAckEncoder<'a> {
+1025    /// Encode [`Self`] into the given buffer
+1026    ///
+1027    /// The [`FrameType`] will be either [`FrameType::PathAckEcn`] or [`FrameType::PathAck`]
+1028    /// depending on whether [`EcnCounts`] are provided.
+1029    ///
+1030    /// PANICS: if `ranges` is empty.
+1031    fn encode<W: BufMut>(&self, buf: &mut W) {
+1032        let PathAckEncoder {
+1033            path_id,
+1034            delay,
+1035            ranges,
+1036            ecn,
+1037        } = self;
+1038        let mut rest = ranges.iter().rev();
+1039        let first = rest
+1040            .next()
+1041            .expect("Caller has verified ranges is non empty");
+1042        let largest = first.end - 1;
+1043        let first_size = first.end - first.start;
+1044        let kind = match ecn.is_some() {
+1045            true => FrameType::PathAckEcn,
+1046            false => FrameType::PathAck,
+1047        };
+1048        buf.write(kind);
+1049        buf.write(*path_id);
+1050        buf.write_var(largest);
+1051        buf.write_var(*delay);
+1052        buf.write_var(ranges.len() as u64 - 1);
+1053        buf.write_var(first_size - 1);
+1054        let mut prev = first.start;
+1055        for block in rest {
+1056            let size = block.end - block.start;
+1057            buf.write_var(prev - block.end - 1);
+1058            buf.write_var(size - 1);
+1059            prev = block.start;
+1060        }
+1061        if let Some(x) = ecn {
+1062            x.encode(buf)
+1063        }
+1064    }
+1065}
+1066
+1067#[derive(Debug, Clone, Eq, PartialEq, derive_more::Display)]
+1068#[display("{} ranges: {ranges:?} delay: {delay}µs largest: {largest}", self.get_type())]
+1069pub(crate) struct Ack {
+1070    pub largest: u64,
+1071    pub delay: u64,
+1072    pub ranges: ArrayRangeSet,
+1073    pub ecn: Option<EcnCounts>,
+1074}
+1075
+1076#[cfg(test)]
+1077impl proptest::arbitrary::Arbitrary for Ack {
+1078    type Parameters = ();
+1079    type Strategy = proptest::strategy::BoxedStrategy<Self>;
+1080
+1081    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
+1082        use proptest::prelude::*;
+1083        (
+1084            varint_u64(),
+1085            any::<ArrayRangeSet>()
+1086                .prop_filter("ranges must be non empty", |ranges| !ranges.is_empty()),
+1087            any::<Option<EcnCounts>>(),
+1088        )
+1089            .prop_map(|(delay, ranges, ecn)| Self {
+1090                largest: ranges.max().expect("ranges must be non empty"),
+1091                delay,
+1092                ranges,
+1093                ecn,
+1094            })
+1095            .boxed()
+1096    }
+1097}
+1098
+1099impl Ack {
+1100    pub(crate) fn encoder<'a>(
+1101        delay: u64,
+1102        ranges: &'a ArrayRangeSet,
+1103        ecn: Option<&'a EcnCounts>,
+1104    ) -> AckEncoder<'a> {
+1105        AckEncoder { delay, ranges, ecn }
+1106    }
+1107
+1108    pub(crate) fn iter(&self) -> impl DoubleEndedIterator<Item = Range<u64>> + '_ {
+1109        self.ranges.iter()
+1110    }
+1111
+1112    pub(crate) const fn get_type(&self) -> FrameType {
+1113        if self.ecn.is_some() {
+1114            FrameType::AckEcn
+1115        } else {
+1116            FrameType::Ack
+1117        }
+1118    }
+1119}
+1120
+1121#[derive(derive_more::Display)]
+1122#[display("{} ranges: {ranges:?} delay: {delay}µs", self.get_type())]
+1123pub(crate) struct AckEncoder<'a> {
+1124    pub(crate) delay: u64,
+1125    pub(crate) ranges: &'a ArrayRangeSet,
+1126    pub(crate) ecn: Option<&'a EcnCounts>,
+1127}
+1128
+1129impl<'a> AckEncoder<'a> {
+1130    const fn get_type(&self) -> FrameType {
+1131        match self.ecn.is_some() {
+1132            true => FrameType::AckEcn,
+1133            false => FrameType::Ack,
+1134        }
+1135    }
+1136}
+1137
+1138impl<'a> Encodable for AckEncoder<'a> {
+1139    fn encode<W: BufMut>(&self, buf: &mut W) {
+1140        let AckEncoder { delay, ranges, ecn } = self;
+1141        let mut rest = ranges.iter().rev();
+1142        let first = rest.next().unwrap();
+1143        let largest = first.end - 1;
+1144        let first_size = first.end - first.start;
+1145        let kind = match ecn.is_some() {
+1146            true => FrameType::AckEcn,
+1147            false => FrameType::Ack,
+1148        };
+1149        buf.write(kind);
+1150        buf.write_var(largest);
+1151        buf.write_var(*delay);
+1152        buf.write_var(ranges.len() as u64 - 1);
+1153        buf.write_var(first_size - 1);
+1154        let mut prev = first.start;
+1155        for block in rest {
+1156            let size = block.end - block.start;
+1157            buf.write_var(prev - block.end - 1);
+1158            buf.write_var(size - 1);
+1159            prev = block.start;
+1160        }
+1161        if let Some(x) = ecn {
+1162            x.encode(buf)
+1163        }
+1164    }
+1165}
+1166
+1167#[cfg_attr(test, derive(Arbitrary))]
+1168#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+1169pub(crate) struct EcnCounts {
+1170    #[cfg_attr(test, strategy(varint_u64()))]
+1171    pub ect0: u64,
+1172    #[cfg_attr(test, strategy(varint_u64()))]
+1173    pub ect1: u64,
+1174    #[cfg_attr(test, strategy(varint_u64()))]
+1175    pub ce: u64,
+1176}
+1177
+1178impl std::ops::AddAssign<EcnCodepoint> for EcnCounts {
+1179    fn add_assign(&mut self, rhs: EcnCodepoint) {
+1180        match rhs {
+1181            EcnCodepoint::Ect0 => {
+1182                self.ect0 += 1;
+1183            }
+1184            EcnCodepoint::Ect1 => {
+1185                self.ect1 += 1;
+1186            }
+1187            EcnCodepoint::Ce => {
+1188                self.ce += 1;
+1189            }
+1190        }
+1191    }
+1192}
+1193
+1194impl EcnCounts {
+1195    pub(crate) const ZERO: Self = Self {
+1196        ect0: 0,
+1197        ect1: 0,
+1198        ce: 0,
+1199    };
+1200}
+1201
+1202impl Encodable for EcnCounts {
+1203    fn encode<W: BufMut>(&self, out: &mut W) {
+1204        out.write_var(self.ect0);
+1205        out.write_var(self.ect1);
+1206        out.write_var(self.ce);
+1207    }
+1208}
+1209
+1210#[derive(Debug, Clone, derive_more::Display)]
+1211#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+1212#[display("STREAM id: {id} off: {offset} len: {} fin: {fin}", data.len())]
+1213pub(crate) struct Stream {
+1214    pub(crate) id: StreamId,
+1215    #[cfg_attr(test, strategy(varint_u64()))]
+1216    pub(crate) offset: u64,
+1217    pub(crate) fin: bool,
+1218    #[cfg_attr(test, strategy(Strategy::prop_map(collection::vec(any::<u8>(), 0..100), Bytes::from)))]
+1219    pub(crate) data: Bytes,
+1220}
+1221
+1222impl FrameStruct for Stream {
+1223    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+1224}
+1225
+1226/// Metadata from a stream frame
+1227#[derive(Debug, Clone, derive_more::Display)]
+1228#[display("STREAM id: {id} off: {} len: {} fin: {fin}", offsets.start, offsets.end - offsets.start)]
+1229pub(crate) struct StreamMeta {
+1230    pub(crate) id: StreamId,
+1231    pub(crate) offsets: Range<u64>,
+1232    pub(crate) fin: bool,
+1233}
+1234
+1235// This manual implementation exists because `Default` is not implemented for `StreamId`
+1236impl Default for StreamMeta {
+1237    fn default() -> Self {
+1238        Self {
+1239            id: StreamId(0),
+1240            offsets: 0..0,
+1241            fin: false,
+1242        }
+1243    }
+1244}
+1245
+1246impl StreamMeta {
+1247    pub(crate) fn encoder(self, encode_length: bool) -> StreamMetaEncoder {
+1248        StreamMetaEncoder {
+1249            meta: self,
+1250            encode_length,
+1251        }
+1252    }
+1253
+1254    const fn get_type(&self, encode_length: bool) -> StreamInfo {
+1255        let mut ty = *StreamInfo::VALUES.start();
+1256        if self.offsets.start != 0 {
+1257            ty |= 0x04;
+1258        }
+1259        if encode_length {
+1260            ty |= 0x02;
+1261        }
+1262        if self.fin {
+1263            ty |= 0x01;
+1264        }
+1265        StreamInfo(ty as u8)
+1266    }
+1267}
+1268
+1269#[derive(derive_more::Display)]
+1270#[display("{meta}")]
+1271pub(crate) struct StreamMetaEncoder {
+1272    pub(crate) meta: StreamMeta,
+1273    encode_length: bool,
+1274}
+1275
+1276impl StreamMetaEncoder {
+1277    const fn get_type(&self) -> FrameType {
+1278        FrameType::Stream(self.meta.get_type(self.encode_length))
+1279    }
+1280}
+1281
+1282impl Encodable for StreamMetaEncoder {
+1283    fn encode<W: BufMut>(&self, out: &mut W) {
+1284        let Self {
+1285            meta,
+1286            encode_length,
+1287        } = self;
+1288        out.write_var(meta.get_type(*encode_length).0 as u64); // 1 byte
+1289        out.write(meta.id); // <=8 bytes
+1290        if meta.offsets.start != 0 {
+1291            out.write_var(meta.offsets.start); // <=8 bytes
+1292        }
+1293        if *encode_length {
+1294            out.write_var(meta.offsets.end - meta.offsets.start); // <=8 bytes
+1295        }
+1296    }
+1297}
+1298
+1299/// A vector of [`StreamMeta`] with optimization for the single element case
+1300pub(crate) type StreamMetaVec = TinyVec<[StreamMeta; 1]>;
+1301
+1302#[derive(Debug, Clone, derive_more::Display)]
+1303#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+1304#[display("CRYPTO off: {offset} len = {}", data.len())]
+1305pub(crate) struct Crypto {
+1306    #[cfg_attr(test, strategy(varint_u64()))]
+1307    pub(crate) offset: u64,
+1308    #[cfg_attr(test, strategy(Strategy::prop_map(collection::vec(any::<u8>(), 0..1024), Bytes::from)))]
+1309    pub(crate) data: Bytes,
+1310}
+1311
+1312impl Crypto {
+1313    pub(crate) const SIZE_BOUND: usize = 17;
+1314
+1315    const fn get_type(&self) -> FrameType {
+1316        FrameType::Crypto
+1317    }
+1318}
+1319
+1320impl Encodable for Crypto {
+1321    fn encode<W: BufMut>(&self, out: &mut W) {
+1322        out.write(FrameType::Crypto);
+1323        out.write_var(self.offset);
+1324        out.write_var(self.data.len() as u64);
+1325        out.put_slice(&self.data);
+1326    }
+1327}
+1328
+1329#[derive(Debug, Clone, derive_more::Display)]
+1330#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+1331#[display("NEW_TOKEN")]
+1332pub(crate) struct NewToken {
+1333    #[cfg_attr(test, strategy(Strategy::prop_map(collection::vec(any::<u8>(), 0..1024), Bytes::from)))]
+1334    pub(crate) token: Bytes,
+1335}
+1336
+1337impl Encodable for NewToken {
+1338    fn encode<W: BufMut>(&self, out: &mut W) {
+1339        out.write(FrameType::NewToken);
+1340        out.write_var(self.token.len() as u64);
+1341        out.put_slice(&self.token);
+1342    }
+1343}
+1344
+1345impl NewToken {
+1346    pub(crate) fn size(&self) -> usize {
+1347        1 + VarInt::from_u64(self.token.len() as u64).unwrap().size() + self.token.len()
+1348    }
+1349
+1350    const fn get_type(&self) -> FrameType {
+1351        FrameType::NewToken
+1352    }
+1353}
+1354
+1355#[derive(Debug, Clone, derive_more::Display)]
+1356#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+1357#[display("MAX_PATH_ID path_id: {_0}")]
+1358pub(crate) struct MaxPathId(pub(crate) PathId);
+1359
+1360impl MaxPathId {
+1361    pub(crate) const SIZE_BOUND: usize =
+1362        FrameType::MaxPathId.size() + VarInt(u32::MAX as u64).size();
+1363
+1364    const fn get_type(&self) -> FrameType {
+1365        FrameType::MaxPathId
+1366    }
+1367}
+1368
+1369impl Decodable for MaxPathId {
+1370    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+1371        Ok(Self(buf.get()?))
+1372    }
+1373}
+1374
+1375impl Encodable for MaxPathId {
+1376    fn encode<B: BufMut>(&self, buf: &mut B) {
+1377        buf.write(FrameType::MaxPathId);
+1378        buf.write(self.0);
+1379    }
+1380}
+1381
+1382#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
+1383#[cfg_attr(test, derive(Arbitrary))]
+1384#[display("PATHS_BLOCKED remote_max_path_id: {_0}")]
+1385pub(crate) struct PathsBlocked(pub(crate) PathId);
+1386
+1387impl PathsBlocked {
+1388    pub(crate) const SIZE_BOUND: usize =
+1389        FrameType::PathsBlocked.size() + VarInt(u32::MAX as u64).size();
+1390
+1391    const fn get_type(&self) -> FrameType {
+1392        FrameType::PathsBlocked
+1393    }
+1394}
+1395
+1396impl Encodable for PathsBlocked {
+1397    fn encode<B: BufMut>(&self, buf: &mut B) {
+1398        buf.write(FrameType::PathsBlocked);
+1399        buf.write(self.0);
+1400    }
+1401}
+1402
+1403impl Decodable for PathsBlocked {
+1404    /// Decode [`Self`] from the buffer, provided that the frame type has been verified
+1405    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+1406        Ok(Self(buf.get()?))
+1407    }
+1408}
+1409
+1410#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
+1411#[cfg_attr(test, derive(Arbitrary))]
+1412#[display("PATH_CIDS_BLOCKED path_id: {path_id} next_seq: {next_seq}")]
+1413pub(crate) struct PathCidsBlocked {
+1414    pub(crate) path_id: PathId,
+1415    pub(crate) next_seq: VarInt,
+1416}
+1417
+1418impl PathCidsBlocked {
+1419    pub(crate) const SIZE_BOUND: usize =
+1420        FrameType::PathCidsBlocked.size() + VarInt(u32::MAX as u64).size() + VarInt::MAX.size();
+1421
+1422    const fn get_type(&self) -> FrameType {
+1423        FrameType::PathCidsBlocked
+1424    }
+1425}
+1426
+1427impl Decodable for PathCidsBlocked {
+1428    fn decode<R: Buf>(buf: &mut R) -> coding::Result<Self> {
+1429        Ok(Self {
+1430            path_id: buf.get()?,
+1431            next_seq: buf.get()?,
+1432        })
+1433    }
+1434}
+1435
+1436impl Encodable for PathCidsBlocked {
+1437    fn encode<W: BufMut>(&self, buf: &mut W) {
+1438        buf.write(FrameType::PathCidsBlocked);
+1439        buf.write(self.path_id);
+1440        buf.write(self.next_seq);
+1441    }
+1442}
+1443
+1444pub(crate) struct Iter {
+1445    bytes: Bytes,
+1446    last_ty: MaybeFrame,
+1447}
+1448
+1449impl Iter {
+1450    pub(crate) fn new(payload: Bytes) -> Result<Self, TransportError> {
+1451        if payload.is_empty() {
+1452            // "An endpoint MUST treat receipt of a packet containing no frames as a
+1453            // connection error of type PROTOCOL_VIOLATION."
+1454            // https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types
+1455            return Err(TransportError::PROTOCOL_VIOLATION(
+1456                "packet payload is empty",
+1457            ));
+1458        }
+1459
+1460        Ok(Self {
+1461            bytes: payload,
+1462            last_ty: MaybeFrame::None,
+1463        })
+1464    }
+1465
+1466    fn take_len(&mut self) -> Result<Bytes, UnexpectedEnd> {
+1467        let len = self.bytes.get_var()?;
+1468        if len > self.bytes.remaining() as u64 {
+1469            return Err(UnexpectedEnd);
+1470        }
+1471        Ok(self.bytes.split_to(len as usize))
+1472    }
+1473
+1474    #[track_caller]
+1475    fn try_next(&mut self) -> Result<Frame, IterErr> {
+1476        self.last_ty = self.bytes.get()?;
+1477
+1478        let ty = match self.last_ty {
+1479            MaybeFrame::None => FrameType::Padding,
+1480            MaybeFrame::Unknown(_other) => return Err(IterErr::InvalidFrameId),
+1481            MaybeFrame::Known(frame_type) => frame_type,
+1482        };
+1483        Ok(match ty {
+1484            FrameType::Padding => Frame::Padding,
+1485            FrameType::ResetStream => Frame::ResetStream(ResetStream {
+1486                id: self.bytes.get()?,
+1487                error_code: self.bytes.get()?,
+1488                final_offset: self.bytes.get()?,
+1489            }),
+1490            FrameType::ConnectionClose => Frame::Close(Close::Connection(ConnectionClose {
+1491                error_code: self.bytes.get()?,
+1492                frame_type: self.bytes.get()?,
+1493                reason: self.take_len()?,
+1494            })),
+1495            FrameType::ApplicationClose => Frame::Close(Close::Application(ApplicationClose {
+1496                error_code: self.bytes.get()?,
+1497                reason: self.take_len()?,
+1498            })),
+1499            FrameType::MaxData => Frame::MaxData(self.bytes.get()?),
+1500            FrameType::MaxStreamData => Frame::MaxStreamData(self.bytes.get()?),
+1501            FrameType::MaxStreamsBidi => Frame::MaxStreams(MaxStreams {
+1502                dir: Dir::Bi,
+1503                count: self.bytes.get_var()?,
+1504            }),
+1505            FrameType::MaxStreamsUni => Frame::MaxStreams(MaxStreams {
+1506                dir: Dir::Uni,
+1507                count: self.bytes.get_var()?,
+1508            }),
+1509            FrameType::Ping => Frame::Ping,
+1510            FrameType::DataBlocked => Frame::DataBlocked(DataBlocked(self.bytes.get_var()?)),
+1511            FrameType::StreamDataBlocked => Frame::StreamDataBlocked(StreamDataBlocked {
+1512                id: self.bytes.get()?,
+1513                offset: self.bytes.get_var()?,
+1514            }),
+1515            FrameType::StreamsBlockedBidi => Frame::StreamsBlocked(StreamsBlocked {
+1516                dir: Dir::Bi,
+1517                limit: self.bytes.get_var()?,
+1518            }),
+1519            FrameType::StreamsBlockedUni => Frame::StreamsBlocked(StreamsBlocked {
+1520                dir: Dir::Uni,
+1521                limit: self.bytes.get_var()?,
+1522            }),
+1523            FrameType::StopSending => Frame::StopSending(StopSending {
+1524                id: self.bytes.get()?,
+1525                error_code: self.bytes.get()?,
+1526            }),
+1527            FrameType::RetireConnectionId | FrameType::PathRetireConnectionId => {
+1528                Frame::RetireConnectionId(RetireConnectionId::decode(
+1529                    &mut self.bytes,
+1530                    ty == FrameType::PathRetireConnectionId,
+1531                )?)
+1532            }
+1533            FrameType::Ack => {
+1534                let largest = self.bytes.get_var()?;
+1535                let delay = self.bytes.get_var()?;
+1536                let ranges = read_ack_blocks(&mut self.bytes, largest)?;
+1537                Frame::Ack(Ack {
+1538                    delay,
+1539                    largest,
+1540                    ranges,
+1541                    ecn: None,
+1542                })
+1543            }
+1544            FrameType::AckEcn => {
+1545                let largest = self.bytes.get_var()?;
+1546                let delay = self.bytes.get_var()?;
+1547                let ranges = read_ack_blocks(&mut self.bytes, largest)?;
+1548                let ecn = Some(EcnCounts {
+1549                    ect0: self.bytes.get_var()?,
+1550                    ect1: self.bytes.get_var()?,
+1551                    ce: self.bytes.get_var()?,
+1552                });
+1553
+1554                Frame::Ack(Ack {
+1555                    delay,
+1556                    largest,
+1557                    ranges,
+1558                    ecn,
+1559                })
+1560            }
+1561            FrameType::PathAck => {
+1562                let path_id = self.bytes.get()?;
+1563                let largest = self.bytes.get_var()?;
+1564                let delay = self.bytes.get_var()?;
+1565                let ranges = read_ack_blocks(&mut self.bytes, largest)?;
+1566                Frame::PathAck(PathAck {
+1567                    path_id,
+1568                    delay,
+1569                    largest,
+1570                    ranges,
+1571                    ecn: None,
+1572                })
+1573            }
+1574            FrameType::PathAckEcn => {
+1575                let path_id = self.bytes.get()?;
+1576                let largest = self.bytes.get_var()?;
+1577                let delay = self.bytes.get_var()?;
+1578                let ranges = read_ack_blocks(&mut self.bytes, largest)?;
+1579                let ecn = Some(EcnCounts {
+1580                    ect0: self.bytes.get_var()?,
+1581                    ect1: self.bytes.get_var()?,
+1582                    ce: self.bytes.get_var()?,
+1583                });
+1584                Frame::PathAck(PathAck {
+1585                    path_id,
+1586                    delay,
+1587                    largest,
+1588                    ranges,
+1589                    ecn,
+1590                })
+1591            }
+1592            FrameType::PathChallenge => Frame::PathChallenge(self.bytes.get()?),
+1593            FrameType::PathResponse => Frame::PathResponse(self.bytes.get()?),
+1594            FrameType::NewConnectionId | FrameType::PathNewConnectionId => {
+1595                let read_path = ty == FrameType::PathNewConnectionId;
+1596                Frame::NewConnectionId(NewConnectionId::read(&mut self.bytes, read_path)?)
+1597            }
+1598            FrameType::Crypto => Frame::Crypto(Crypto {
+1599                offset: self.bytes.get_var()?,
+1600                data: self.take_len()?,
+1601            }),
+1602            FrameType::NewToken => Frame::NewToken(NewToken {
+1603                token: self.take_len()?,
+1604            }),
+1605            FrameType::HandshakeDone => Frame::HandshakeDone,
+1606            FrameType::AckFrequency => Frame::AckFrequency(AckFrequency {
+1607                sequence: self.bytes.get()?,
+1608                ack_eliciting_threshold: self.bytes.get()?,
+1609                request_max_ack_delay: self.bytes.get()?,
+1610                reordering_threshold: self.bytes.get()?,
+1611            }),
+1612            FrameType::ImmediateAck => Frame::ImmediateAck,
+1613            FrameType::ObservedIpv4Addr | FrameType::ObservedIpv6Addr => {
+1614                let is_ipv6 = ty == FrameType::ObservedIpv6Addr;
+1615                let observed = ObservedAddr::read(&mut self.bytes, is_ipv6)?;
+1616                Frame::ObservedAddr(observed)
+1617            }
+1618            FrameType::PathAbandon => Frame::PathAbandon(PathAbandon::decode(&mut self.bytes)?),
+1619            FrameType::PathStatusAvailable => {
+1620                Frame::PathStatusAvailable(PathStatusAvailable::decode(&mut self.bytes)?)
+1621            }
+1622            FrameType::PathStatusBackup => {
+1623                Frame::PathStatusBackup(PathStatusBackup::decode(&mut self.bytes)?)
+1624            }
+1625            FrameType::MaxPathId => Frame::MaxPathId(MaxPathId::decode(&mut self.bytes)?),
+1626            FrameType::PathsBlocked => Frame::PathsBlocked(PathsBlocked::decode(&mut self.bytes)?),
+1627            FrameType::PathCidsBlocked => {
+1628                Frame::PathCidsBlocked(PathCidsBlocked::decode(&mut self.bytes)?)
+1629            }
+1630            FrameType::AddIpv4Address | FrameType::AddIpv6Address => {
+1631                let is_ipv6 = ty == FrameType::AddIpv6Address;
+1632                let add_address = AddAddress::read(&mut self.bytes, is_ipv6)?;
+1633                Frame::AddAddress(add_address)
+1634            }
+1635            FrameType::ReachOutAtIpv4 | FrameType::ReachOutAtIpv6 => {
+1636                let is_ipv6 = ty == FrameType::ReachOutAtIpv6;
+1637                let reach_out = ReachOut::read(&mut self.bytes, is_ipv6)?;
+1638                Frame::ReachOut(reach_out)
+1639            }
+1640            FrameType::RemoveAddress => Frame::RemoveAddress(RemoveAddress::read(&mut self.bytes)?),
+1641            FrameType::Stream(s) => Frame::Stream(Stream {
+1642                id: self.bytes.get()?,
+1643                offset: if s.off() { self.bytes.get_var()? } else { 0 },
+1644                fin: s.fin(),
+1645                data: if s.len() {
+1646                    self.take_len()?
+1647                } else {
+1648                    self.take_remaining()
+1649                },
+1650            }),
+1651            FrameType::Datagram(d) => Frame::Datagram(Datagram {
+1652                data: if d.len() {
+1653                    self.take_len()?
+1654                } else {
+1655                    self.take_remaining()
+1656                },
+1657            }),
+1658        })
+1659    }
+1660
+1661    pub(crate) fn take_remaining(&mut self) -> Bytes {
+1662        mem::take(&mut self.bytes)
+1663    }
+1664}
+1665
+1666impl Iterator for Iter {
+1667    type Item = Result<Frame, InvalidFrame>;
+1668    fn next(&mut self) -> Option<Self::Item> {
+1669        if !self.bytes.has_remaining() {
+1670            return None;
+1671        }
+1672        match self.try_next() {
+1673            Ok(x) => Some(Ok(x)),
+1674            Err(e) => {
+1675                // Corrupt frame, skip it and everything that follows
+1676                self.bytes.clear();
+1677                Some(Err(InvalidFrame {
+1678                    ty: self.last_ty,
+1679                    reason: e.reason(),
+1680                }))
+1681            }
+1682        }
+1683    }
+1684}
+1685
+1686#[derive(Debug)]
+1687pub(crate) struct InvalidFrame {
+1688    pub(crate) ty: MaybeFrame,
+1689    pub(crate) reason: &'static str,
+1690}
+1691
+1692impl From<InvalidFrame> for TransportError {
+1693    fn from(err: InvalidFrame) -> Self {
+1694        let mut te = Self::FRAME_ENCODING_ERROR(err.reason);
+1695        te.frame = err.ty;
+1696        te
+1697    }
+1698}
+1699
+1700/// Decodes the ACK Ranges from the given buffer.
+1701///  This means, reading these three values
+1702///
+1703/// - ACK Range Count
+1704/// - First ACK Range
+1705/// - ACK Ranges
+1706///
+1707/// Ref <https://www.rfc-editor.org/rfc/rfc9000.html#name-ack-ranges>
+1708fn read_ack_blocks(buf: &mut Bytes, mut largest: u64) -> Result<ArrayRangeSet, IterErr> {
+1709    // Ack Range Count
+1710    let num_blocks = buf.get_var()?;
+1711
+1712    let mut out = ArrayRangeSet::new();
+1713    let mut block_to_block;
+1714    let mut range;
+1715
+1716    for num_block in 0..num_blocks + 1 {
+1717        range = buf.get_var()?;
+1718        range += 1;
+1719
+1720        let start = (largest + 1).checked_sub(range).ok_or(IterErr::Malformed)?;
+1721
+1722        if range > 0 {
+1723            out.insert(start..largest + 1);
+1724        }
+1725
+1726        // no gap on the last "block"
+1727        if num_block < num_blocks {
+1728            // skip the gap
+1729            block_to_block = buf.get_var()?;
+1730            block_to_block += 1;
+1731            block_to_block += range;
+1732
+1733            largest = largest
+1734                .checked_sub(block_to_block)
+1735                .ok_or(IterErr::Malformed)?;
+1736        }
+1737    }
+1738
+1739    Ok(out)
+1740}
+1741
+1742#[derive(Debug)]
+1743enum IterErr {
+1744    UnexpectedEnd,
+1745    InvalidFrameId,
+1746    Malformed,
+1747}
+1748
+1749impl IterErr {
+1750    fn reason(&self) -> &'static str {
+1751        use IterErr::*;
+1752        match *self {
+1753            UnexpectedEnd => "unexpected end",
+1754            InvalidFrameId => "invalid frame ID",
+1755            Malformed => "malformed",
+1756        }
+1757    }
+1758}
+1759
+1760impl From<UnexpectedEnd> for IterErr {
+1761    fn from(_: UnexpectedEnd) -> Self {
+1762        Self::UnexpectedEnd
+1763    }
+1764}
+1765
+1766#[allow(unreachable_pub)] // fuzzing only
+1767#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
+1768#[cfg_attr(test, derive(Arbitrary))]
+1769#[derive(Debug, Copy, Clone, derive_more::Display)]
+1770#[display("RESET_STREAM id: {id}")]
+1771pub struct ResetStream {
+1772    pub(crate) id: StreamId,
+1773    pub(crate) error_code: VarInt,
+1774    pub(crate) final_offset: VarInt,
+1775}
+1776
+1777impl ResetStream {
+1778    const fn get_type(&self) -> FrameType {
+1779        FrameType::ResetStream
+1780    }
+1781}
+1782
+1783impl FrameStruct for ResetStream {
+1784    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+1785}
+1786
+1787impl Encodable for ResetStream {
+1788    fn encode<W: BufMut>(&self, out: &mut W) {
+1789        out.write(FrameType::ResetStream); // 1 byte
+1790        out.write(self.id); // <= 8 bytes
+1791        out.write(self.error_code); // <= 8 bytes
+1792        out.write(self.final_offset); // <= 8 bytes
+1793    }
+1794}
+1795
+1796#[cfg_attr(test, derive(Arbitrary))]
+1797#[derive(Debug, Copy, Clone, derive_more::Display)]
+1798#[display("STOP_SENDING id: {id}")]
+1799pub(crate) struct StopSending {
+1800    pub(crate) id: StreamId,
+1801    pub(crate) error_code: VarInt,
+1802}
+1803
+1804impl FrameStruct for StopSending {
+1805    const SIZE_BOUND: usize = 1 + 8 + 8;
+1806}
+1807
+1808impl StopSending {
+1809    const fn get_type(&self) -> FrameType {
+1810        FrameType::StopSending
+1811    }
+1812}
+1813
+1814impl Encodable for StopSending {
+1815    fn encode<W: BufMut>(&self, out: &mut W) {
+1816        out.write(FrameType::StopSending); // 1 byte
+1817        out.write(self.id); // <= 8 bytes
+1818        out.write(self.error_code) // <= 8 bytes
+1819    }
+1820}
+1821
+1822#[derive(Debug, Copy, Clone, PartialEq, Eq, derive_more::Display)]
+1823#[display("{} {} seq: {sequence} id: {id}", self.get_type(), DisplayOption::new("path_id", path_id.as_ref()))]
+1824pub(crate) struct NewConnectionId {
+1825    pub(crate) path_id: Option<PathId>,
+1826    pub(crate) sequence: u64,
+1827    pub(crate) retire_prior_to: u64,
+1828    pub(crate) id: ConnectionId,
+1829    pub(crate) reset_token: ResetToken,
+1830}
+1831
+1832#[cfg(test)]
+1833fn connection_id_and_reset_token() -> impl Strategy<Value = (crate::ConnectionId, ResetToken)> {
+1834    (any::<ConnectionId>(), any::<[u8; 64]>()).prop_map(|(id, reset_key)| {
+1835        #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+1836        use aws_lc_rs::hmac;
+1837        #[cfg(feature = "ring")]
+1838        use ring::hmac;
+1839        let key = hmac::Key::new(hmac::HMAC_SHA256, &reset_key);
+1840        (id, ResetToken::new(&key, id))
+1841    })
+1842}
+1843
+1844#[cfg(test)]
+1845impl proptest::arbitrary::Arbitrary for NewConnectionId {
+1846    type Parameters = ();
+1847    type Strategy = proptest::strategy::BoxedStrategy<Self>;
+1848
+1849    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
+1850        use proptest::prelude::*;
+1851        (
+1852            any::<Option<PathId>>(),
+1853            varint_u64(),
+1854            varint_u64(),
+1855            connection_id_and_reset_token(),
+1856        )
+1857            .prop_map(|(path_id, a, b, (id, reset_token))| {
+1858                let sequence = std::cmp::max(a, b);
+1859                let retire_prior_to = std::cmp::min(a, b);
+1860                Self {
+1861                    path_id,
+1862                    sequence,
+1863                    retire_prior_to,
+1864                    id,
+1865                    reset_token,
+1866                }
+1867            })
+1868            .boxed()
+1869    }
+1870}
+1871
+1872impl NewConnectionId {
+1873    /// Maximum size of this frame when the frame type is [`FrameType::NewConnectionId`],
+1874    pub(crate) const SIZE_BOUND: usize = {
+1875        let type_len = FrameType::NewConnectionId.size();
+1876        let seq_max_len = 8usize;
+1877        let retire_prior_to_max_len = 8usize;
+1878        let cid_len_len = 1;
+1879        let cid_len = 160;
+1880        let reset_token_len = 16;
+1881        type_len + seq_max_len + retire_prior_to_max_len + cid_len_len + cid_len + reset_token_len
+1882    };
+1883
+1884    /// Maximum size of this frame when the frame type is [`FrameType::PathNewConnectionId`],
+1885    pub(crate) const SIZE_BOUND_MULTIPATH: usize = {
+1886        let type_len = FrameType::PathNewConnectionId.size();
+1887        let path_id_len = VarInt::from_u32(u32::MAX).size();
+1888        let seq_max_len = 8usize;
+1889        let retire_prior_to_max_len = 8usize;
+1890        let cid_len_len = 1;
+1891        let cid_len = 160;
+1892        let reset_token_len = 16;
+1893        type_len
+1894            + path_id_len
+1895            + seq_max_len
+1896            + retire_prior_to_max_len
+1897            + cid_len_len
+1898            + cid_len
+1899            + reset_token_len
+1900    };
+1901
+1902    const fn get_type(&self) -> FrameType {
+1903        if self.path_id.is_some() {
+1904            FrameType::PathNewConnectionId
+1905        } else {
+1906            FrameType::NewConnectionId
+1907        }
+1908    }
+1909
+1910    /// Returns the maximum encoded size on the wire.
+1911    ///
+1912    /// This is a rough upper estimate, does not squeeze every last byte out.
+1913    pub(crate) const fn size_bound(path_new_cid: bool, cid_len: usize) -> usize {
+1914        let upper_bound = match path_new_cid {
+1915            true => Self::SIZE_BOUND_MULTIPATH,
+1916            false => Self::SIZE_BOUND,
+1917        };
+1918        // instead of using the maximum cid len, use the provided one
+1919        upper_bound - 160 + cid_len
+1920    }
+1921
+1922    fn read<R: Buf>(bytes: &mut R, read_path: bool) -> Result<Self, IterErr> {
+1923        let path_id = if read_path { Some(bytes.get()?) } else { None };
+1924        let sequence = bytes.get_var()?;
+1925        let retire_prior_to = bytes.get_var()?;
+1926        if retire_prior_to > sequence {
+1927            return Err(IterErr::Malformed);
+1928        }
+1929        let length = bytes.get::<u8>()? as usize;
+1930        if length > MAX_CID_SIZE || length == 0 {
+1931            return Err(IterErr::Malformed);
+1932        }
+1933        if length > bytes.remaining() {
+1934            return Err(IterErr::UnexpectedEnd);
+1935        }
+1936        let mut stage = [0; MAX_CID_SIZE];
+1937        bytes.copy_to_slice(&mut stage[0..length]);
+1938        let id = ConnectionId::new(&stage[..length]);
+1939        if bytes.remaining() < 16 {
+1940            return Err(IterErr::UnexpectedEnd);
+1941        }
+1942        let mut reset_token = [0; RESET_TOKEN_SIZE];
+1943        bytes.copy_to_slice(&mut reset_token);
+1944        Ok(Self {
+1945            path_id,
+1946            sequence,
+1947            retire_prior_to,
+1948            id,
+1949            reset_token: reset_token.into(),
+1950        })
+1951    }
+1952
+1953    pub(crate) fn issued(&self) -> crate::shared::IssuedCid {
+1954        crate::shared::IssuedCid {
+1955            path_id: self.path_id.unwrap_or_default(),
+1956            sequence: self.sequence,
+1957            id: self.id,
+1958            reset_token: self.reset_token,
+1959        }
+1960    }
+1961}
+1962
+1963impl Encodable for NewConnectionId {
+1964    fn encode<W: BufMut>(&self, out: &mut W) {
+1965        out.write(self.get_type());
+1966        if let Some(id) = self.path_id {
+1967            out.write(id);
+1968        }
+1969        out.write_var(self.sequence);
+1970        out.write_var(self.retire_prior_to);
+1971        out.write(self.id.len() as u8);
+1972        out.put_slice(&self.id);
+1973        out.put_slice(&self.reset_token);
+1974    }
+1975}
+1976
+1977impl FrameStruct for NewConnectionId {
+1978    const SIZE_BOUND: usize = 1 + 8 + 8 + 1 + MAX_CID_SIZE + RESET_TOKEN_SIZE;
+1979}
+1980
+1981/// An unreliable datagram
+1982#[derive(Debug, Clone, derive_more::Display)]
+1983#[cfg_attr(test, derive(Arbitrary))]
+1984#[display("DATAGRAM len: {}", data.len())]
+1985pub struct Datagram {
+1986    /// Payload
+1987    #[cfg_attr(test, strategy(Strategy::prop_map(collection::vec(any::<u8>(), 0..100), Bytes::from)))]
+1988    pub data: Bytes,
+1989}
+1990
+1991impl FrameStruct for Datagram {
+1992    const SIZE_BOUND: usize = 1 + 8;
+1993}
+1994
+1995impl Datagram {
+1996    pub(crate) fn size(&self, length: bool) -> usize {
+1997        1 + if length {
+1998            VarInt::from_u64(self.data.len() as u64).unwrap().size()
+1999        } else {
+2000            0
+2001        } + self.data.len()
+2002    }
+2003
+2004    const fn get_type(&self) -> FrameType {
+2005        FrameType::Datagram(DatagramInfo(*DatagramInfo::VALUES.start() as u8))
+2006    }
+2007}
+2008
+2009impl Encodable for Datagram {
+2010    fn encode<B: BufMut>(&self, out: &mut B) {
+2011        // A datagram is encoded only after this is verified.
+2012        const ENCODE_LEN: bool = true;
+2013        out.write(FrameType::Datagram(DatagramInfo(
+2014            *DatagramInfo::VALUES.start() as u8 | u8::from(ENCODE_LEN),
+2015        ))); // 1 byte
+2016        // Safe to unwrap because we check length sanity before queueing datagrams
+2017        out.write(VarInt::from_u64(self.data.len() as u64).unwrap()); // <= 8 bytes
+2018        out.put_slice(&self.data);
+2019    }
+2020}
+2021
+2022#[derive(Debug, Copy, Clone, PartialEq, Eq, derive_more::Display)]
+2023#[cfg_attr(test, derive(Arbitrary))]
+2024#[display("ACK_FREQUENCY max_ack_delay: {}µs", request_max_ack_delay.0)]
+2025pub(crate) struct AckFrequency {
+2026    pub(crate) sequence: VarInt,
+2027    pub(crate) ack_eliciting_threshold: VarInt,
+2028    pub(crate) request_max_ack_delay: VarInt,
+2029    pub(crate) reordering_threshold: VarInt,
+2030}
+2031
+2032impl AckFrequency {
+2033    const fn get_type(&self) -> FrameType {
+2034        FrameType::AckFrequency
+2035    }
+2036}
+2037
+2038impl Encodable for AckFrequency {
+2039    fn encode<W: BufMut>(&self, buf: &mut W) {
+2040        buf.write(FrameType::AckFrequency);
+2041        buf.write(self.sequence);
+2042        buf.write(self.ack_eliciting_threshold);
+2043        buf.write(self.request_max_ack_delay);
+2044        buf.write(self.reordering_threshold);
+2045    }
+2046}
+2047
+2048/* Address Discovery https://datatracker.ietf.org/doc/draft-seemann-quic-address-discovery/ */
+2049
+2050/// Conjunction of the information contained in the address discovery frames
+2051/// ([`FrameType::ObservedIpv4Addr`], [`FrameType::ObservedIpv6Addr`]).
+2052#[derive(Debug, PartialEq, Eq, Clone, derive_more::Display)]
+2053#[display("{} seq_no: {seq_no} addr: {}", self.get_type(), self.socket_addr())]
+2054#[cfg_attr(test, derive(Arbitrary))]
+2055pub(crate) struct ObservedAddr {
+2056    /// Monotonically increasing integer within the same connection.
+2057    pub(crate) seq_no: VarInt,
+2058    /// Reported observed address.
+2059    pub(crate) ip: IpAddr,
+2060    /// Reported observed port.
+2061    pub(crate) port: u16,
+2062}
+2063
+2064impl ObservedAddr {
+2065    pub(crate) fn new<N: Into<VarInt>>(remote: std::net::SocketAddr, seq_no: N) -> Self {
+2066        Self {
+2067            ip: remote.ip(),
+2068            port: remote.port(),
+2069            seq_no: seq_no.into(),
+2070        }
+2071    }
+2072
+2073    /// Get the [`FrameType`] for this frame.
+2074    const fn get_type(&self) -> FrameType {
+2075        if self.ip.is_ipv6() {
+2076            FrameType::ObservedIpv6Addr
+2077        } else {
+2078            FrameType::ObservedIpv4Addr
+2079        }
+2080    }
+2081
+2082    /// Compute the number of bytes needed to encode the frame.
+2083    pub(crate) fn size(&self) -> usize {
+2084        let type_size = self.get_type().size();
+2085        let req_id_bytes = self.seq_no.size();
+2086        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+2087        let port_bytes = 2;
+2088        type_size + req_id_bytes + ip_bytes + port_bytes
+2089    }
+2090
+2091    /// Reads the frame contents from the buffer.
+2092    ///
+2093    /// Should only be called when the frame type has been identified as
+2094    /// [`FrameType::ObservedIpv4Addr`] or [`FrameType::ObservedIpv6Addr`].
+2095    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+2096        let seq_no = bytes.get()?;
+2097        let ip = if is_ipv6 {
+2098            IpAddr::V6(bytes.get()?)
+2099        } else {
+2100            IpAddr::V4(bytes.get()?)
+2101        };
+2102        let port = bytes.get()?;
+2103        Ok(Self { seq_no, ip, port })
+2104    }
+2105
+2106    /// Gives the [`SocketAddr`] reported in the frame.
+2107    pub(crate) fn socket_addr(&self) -> SocketAddr {
+2108        (self.ip, self.port).into()
+2109    }
+2110}
+2111
+2112impl Encodable for ObservedAddr {
+2113    fn encode<W: BufMut>(&self, buf: &mut W) {
+2114        buf.write(self.get_type());
+2115        buf.write(self.seq_no);
+2116        match self.ip {
+2117            IpAddr::V4(ipv4_addr) => {
+2118                buf.write(ipv4_addr);
+2119            }
+2120            IpAddr::V6(ipv6_addr) => {
+2121                buf.write(ipv6_addr);
+2122            }
+2123        }
+2124        buf.write::<u16>(self.port);
+2125    }
+2126}
+2127
+2128/* Multipath <https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/> */
+2129
+2130#[derive(Debug, PartialEq, Eq, derive_more::Display)]
+2131#[cfg_attr(test, derive(Arbitrary))]
+2132#[display("PATH_ABANDON path_id: {path_id}")]
+2133pub(crate) struct PathAbandon {
+2134    pub(crate) path_id: PathId,
+2135    pub(crate) error_code: TransportErrorCode,
+2136}
+2137
+2138impl PathAbandon {
+2139    pub(crate) const SIZE_BOUND: usize = FrameType::PathAbandon.size() + 8 + 8;
+2140
+2141    const fn get_type(&self) -> FrameType {
+2142        FrameType::PathAbandon
+2143    }
+2144}
+2145
+2146impl Encodable for PathAbandon {
+2147    fn encode<W: BufMut>(&self, buf: &mut W) {
+2148        buf.write(FrameType::PathAbandon);
+2149        buf.write(self.path_id);
+2150        buf.write(self.error_code);
+2151    }
+2152}
+2153
+2154impl Decodable for PathAbandon {
+2155    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+2156        Ok(Self {
+2157            path_id: bytes.get()?,
+2158            error_code: bytes.get()?,
+2159        })
+2160    }
+2161}
+2162
+2163#[derive(Debug, PartialEq, Eq, derive_more::Display)]
+2164#[cfg_attr(test, derive(Arbitrary))]
+2165#[display("PATH_STATUS_AVAILABLE path_id: {path_id} seq_no: {status_seq_no}")]
+2166pub(crate) struct PathStatusAvailable {
+2167    pub(crate) path_id: PathId,
+2168    pub(crate) status_seq_no: VarInt,
+2169}
+2170
+2171impl PathStatusAvailable {
+2172    const TYPE: FrameType = FrameType::PathStatusAvailable;
+2173    pub(crate) const SIZE_BOUND: usize = FrameType::PathStatusAvailable.size() + 8 + 8;
+2174
+2175    const fn get_type(&self) -> FrameType {
+2176        FrameType::PathStatusAvailable
+2177    }
+2178}
+2179
+2180impl Encodable for PathStatusAvailable {
+2181    fn encode<W: BufMut>(&self, buf: &mut W) {
+2182        buf.write(Self::TYPE);
+2183        buf.write(self.path_id);
+2184        buf.write(self.status_seq_no);
+2185    }
+2186}
+2187
+2188impl Decodable for PathStatusAvailable {
+2189    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+2190        Ok(Self {
+2191            path_id: bytes.get()?,
+2192            status_seq_no: bytes.get()?,
+2193        })
+2194    }
+2195}
+2196
+2197#[derive(Debug, PartialEq, Eq, derive_more::Display)]
+2198#[cfg_attr(test, derive(Arbitrary))]
+2199#[display("PATH_STATUS_BACKUP path_id: {path_id} seq_no: {status_seq_no}")]
+2200pub(crate) struct PathStatusBackup {
+2201    pub(crate) path_id: PathId,
+2202    pub(crate) status_seq_no: VarInt,
+2203}
+2204
+2205impl PathStatusBackup {
+2206    const TYPE: FrameType = FrameType::PathStatusBackup;
+2207
+2208    const fn get_type(&self) -> FrameType {
+2209        FrameType::PathStatusBackup
+2210    }
+2211}
+2212
+2213impl Encodable for PathStatusBackup {
+2214    fn encode<W: BufMut>(&self, buf: &mut W) {
+2215        buf.write(Self::TYPE);
+2216        buf.write(self.path_id);
+2217        buf.write(self.status_seq_no);
+2218    }
+2219}
+2220
+2221impl Decodable for PathStatusBackup {
+2222    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+2223        Ok(Self {
+2224            path_id: bytes.get()?,
+2225            status_seq_no: bytes.get()?,
+2226        })
+2227    }
+2228}
+2229
+2230/* Nat traversal frames */
+2231
+2232/// Conjunction of the information contained in the add address frames
+2233/// ([`FrameType::AddIpv4Address`], [`FrameType::AddIpv6Address`]).
+2234#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord, derive_more::Display)]
+2235#[display("{} seq_no: {seq_no} addr: {}", self.get_type(), self.socket_addr())]
+2236#[cfg_attr(test, derive(Arbitrary))]
+2237pub(crate) struct AddAddress {
+2238    /// Monotonically increasing integer within the same connection
+2239    // TODO(@divma): both assumed, the draft has no mention of this but it's standard
+2240    pub(crate) seq_no: VarInt,
+2241    /// Address to include in the known set
+2242    pub(crate) ip: IpAddr,
+2243    /// Port to use with this address
+2244    pub(crate) port: u16,
+2245}
+2246
+2247// TODO(@divma): remove
+2248#[allow(dead_code)]
+2249impl AddAddress {
+2250    /// Smallest number of bytes this type of frame is guaranteed to fit within.
+2251    pub(crate) const SIZE_BOUND: usize = Self {
+2252        ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST),
+2253        port: u16::MAX,
+2254        seq_no: VarInt::MAX,
+2255    }
+2256    .size();
+2257
+2258    pub(crate) const fn new((ip, port): (IpAddr, u16), seq_no: VarInt) -> Self {
+2259        Self { ip, port, seq_no }
+2260    }
+2261
+2262    /// Get the [`FrameType`] for this frame.
+2263    const fn get_type(&self) -> FrameType {
+2264        if self.ip.is_ipv6() {
+2265            FrameType::AddIpv6Address
+2266        } else {
+2267            FrameType::AddIpv4Address
+2268        }
+2269    }
+2270
+2271    /// Compute the number of bytes needed to encode the frame
+2272    pub(crate) const fn size(&self) -> usize {
+2273        let type_size = self.get_type().size();
+2274        let seq_no_bytes = self.seq_no.size();
+2275        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+2276        let port_bytes = 2;
+2277        type_size + seq_no_bytes + ip_bytes + port_bytes
+2278    }
+2279
+2280    /// Read the frame contents from the buffer
+2281    ///
+2282    /// Should only be called when the frame type has been identified as
+2283    /// [`FrameType::AddIpv4Address`] or [`FrameType::AddIpv6Address`].
+2284    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+2285        let seq_no = bytes.get()?;
+2286        let ip = if is_ipv6 {
+2287            IpAddr::V6(bytes.get()?)
+2288        } else {
+2289            IpAddr::V4(bytes.get()?)
+2290        };
+2291        let port = bytes.get()?;
+2292        Ok(Self { seq_no, ip, port })
+2293    }
+2294
+2295    /// Give the [`SocketAddr`] encoded in the frame
+2296    pub(crate) fn socket_addr(&self) -> SocketAddr {
+2297        self.ip_port().into()
+2298    }
+2299
+2300    pub(crate) fn ip_port(&self) -> (IpAddr, u16) {
+2301        (self.ip, self.port)
+2302    }
+2303}
+2304
+2305impl Encodable for AddAddress {
+2306    fn encode<W: BufMut>(&self, buf: &mut W) {
+2307        buf.write(self.get_type());
+2308        buf.write(self.seq_no);
+2309        match self.ip {
+2310            IpAddr::V4(ipv4_addr) => {
+2311                buf.write(ipv4_addr);
+2312            }
+2313            IpAddr::V6(ipv6_addr) => {
+2314                buf.write(ipv6_addr);
+2315            }
+2316        }
+2317        buf.write::<u16>(self.port);
+2318    }
+2319}
+2320
+2321/// Conjunction of the information contained in the reach out frames
+2322/// ([`FrameType::ReachOutAtIpv4`], [`FrameType::ReachOutAtIpv6`])
+2323#[derive(Debug, PartialEq, Eq, Clone, derive_more::Display)]
+2324#[display("REACH_OUT round: {round} local_addr: {}", self.socket_addr())]
+2325#[cfg_attr(test, derive(Arbitrary))]
+2326pub(crate) struct ReachOut {
+2327    /// The sequence number of the NAT Traversal attempts
+2328    pub(crate) round: VarInt,
+2329    /// Address to use
+2330    pub(crate) ip: IpAddr,
+2331    /// Port to use with this address
+2332    pub(crate) port: u16,
+2333}
+2334
+2335// TODO(@divma): remove
+2336#[allow(dead_code)]
+2337impl ReachOut {
+2338    /// Smallest number of bytes this type of frame is guaranteed to fit within
+2339    pub(crate) const SIZE_BOUND: usize = Self {
+2340        round: VarInt::MAX,
+2341        ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST),
+2342        port: u16::MAX,
+2343    }
+2344    .size();
+2345
+2346    pub(crate) const fn new(round: VarInt, (ip, port): (IpAddr, u16)) -> Self {
+2347        Self { round, ip, port }
+2348    }
+2349
+2350    /// Get the [`FrameType`] for this frame
+2351    pub(crate) const fn get_type(&self) -> FrameType {
+2352        if self.ip.is_ipv6() {
+2353            FrameType::ReachOutAtIpv6
+2354        } else {
+2355            FrameType::ReachOutAtIpv4
+2356        }
+2357    }
+2358
+2359    /// Compute the number of bytes needed to encode the frame
+2360    pub(crate) const fn size(&self) -> usize {
+2361        let type_size = self.get_type().size();
+2362        let round_bytes = self.round.size();
+2363        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+2364        let port_bytes = 2;
+2365        type_size + round_bytes + ip_bytes + port_bytes
+2366    }
+2367
+2368    /// Read the frame contents from the buffer
+2369    ///
+2370    /// Should only be called when the frame type has been identified as
+2371    /// [`FrameType::ReachOutAtIpv4`] or [`FrameType::ReachOutAtIpv6`].
+2372    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+2373        let round = bytes.get()?;
+2374        let ip = if is_ipv6 {
+2375            IpAddr::V6(bytes.get()?)
+2376        } else {
+2377            IpAddr::V4(bytes.get()?)
+2378        };
+2379        let port = bytes.get()?;
+2380        Ok(Self { round, ip, port })
+2381    }
+2382
+2383    /// Give the [`SocketAddr`] encoded in the frame
+2384    pub(crate) fn socket_addr(&self) -> SocketAddr {
+2385        (self.ip, self.port).into()
+2386    }
+2387}
+2388
+2389impl Encodable for ReachOut {
+2390    fn encode<W: BufMut>(&self, buf: &mut W) {
+2391        buf.write(self.get_type());
+2392        buf.write(self.round);
+2393        match self.ip {
+2394            IpAddr::V4(ipv4_addr) => {
+2395                buf.write(ipv4_addr);
+2396            }
+2397            IpAddr::V6(ipv6_addr) => {
+2398                buf.write(ipv6_addr);
+2399            }
+2400        }
+2401        buf.write::<u16>(self.port);
+2402    }
+2403}
+2404
+2405/// Frame signaling an address is no longer being advertised
+2406#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord, derive_more::Display)]
+2407#[cfg_attr(test, derive(Arbitrary))]
+2408#[display("REMOVE_ADDRESS seq_no: {seq_no}")]
+2409pub(crate) struct RemoveAddress {
+2410    /// The sequence number of the address advertisement to be removed
+2411    pub(crate) seq_no: VarInt,
+2412}
+2413
+2414// TODO(@divma): remove
+2415#[allow(dead_code)]
+2416impl RemoveAddress {
+2417    /// [`FrameType`] of this frame
+2418    pub(crate) const TYPE: FrameType = FrameType::RemoveAddress;
+2419
+2420    /// Smallest number of bytes this type of frame is guaranteed to fit within
+2421    pub(crate) const SIZE_BOUND: usize = Self::new(VarInt::MAX).size();
+2422
+2423    pub(crate) const fn new(seq_no: VarInt) -> Self {
+2424        Self { seq_no }
+2425    }
+2426
+2427    /// Compute the number of bytes needed to encode the frame
+2428    pub(crate) const fn size(&self) -> usize {
+2429        let type_size = Self::TYPE.size();
+2430        let seq_no_bytes = self.seq_no.size();
+2431        type_size + seq_no_bytes
+2432    }
+2433
+2434    /// Read the frame contents from the buffer
+2435    ///
+2436    /// Should only be called when the frame type has been identified as
+2437    /// [`FrameType::RemoveAddress`].
+2438    pub(crate) fn read<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+2439        Ok(Self {
+2440            seq_no: bytes.get()?,
+2441        })
+2442    }
+2443
+2444    const fn get_type(&self) -> FrameType {
+2445        FrameType::RemoveAddress
+2446    }
+2447}
+2448
+2449impl Encodable for RemoveAddress {
+2450    fn encode<W: BufMut>(&self, buf: &mut W) {
+2451        buf.write(Self::TYPE);
+2452        buf.write(self.seq_no);
+2453    }
+2454}
+2455
+2456/// Helper struct for display implementations.
+2457// NOTE: Due to lifetimes in fmt::Arguments it's not possible to make this a simple function that
+2458// avoids allocations.
+2459struct DisplayOption<T: Display> {
+2460    field_name: &'static str,
+2461    op: Option<T>,
+2462}
+2463
+2464impl<T: Display> DisplayOption<T> {
+2465    fn new(field_name: &'static str, op: Option<T>) -> Self {
+2466        Self { field_name, op }
+2467    }
+2468}
+2469
+2470impl<T: Display> Display for DisplayOption<T> {
+2471    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+2472        if let Some(x) = self.op.as_ref() {
+2473            write!(f, "{}: {x}", self.field_name)
+2474        } else {
+2475            fmt::Result::Ok(())
+2476        }
+2477    }
+2478}
+2479
+2480#[cfg(test)]
+2481mod test {
+2482    use super::*;
+2483    use crate::coding::Encodable;
+2484    use assert_matches::assert_matches;
+2485
+2486    #[test]
+2487    fn frame_type() {
+2488        assert_eq!(
+2489            FrameType::try_from(FrameType::Padding.to_u64()),
+2490            Ok(FrameType::Padding),
+2491        );
+2492
+2493        assert_eq!(
+2494            FrameType::try_from(FrameType::Datagram(DatagramInfo(0x30)).to_u64()),
+2495            Ok(FrameType::Datagram(DatagramInfo(0x30))),
+2496        );
+2497
+2498        assert_eq!(
+2499            FrameType::try_from(FrameType::Stream(StreamInfo(0x08)).to_u64()),
+2500            Ok(FrameType::Stream(StreamInfo(0x08))),
+2501        );
+2502    }
+2503
+2504    #[track_caller]
+2505    fn frames(buf: Vec<u8>) -> Vec<Frame> {
+2506        Iter::new(Bytes::from(buf))
+2507            .unwrap()
+2508            .collect::<Result<Vec<_>, _>>()
+2509            .unwrap()
+2510    }
+2511
+2512    #[test]
+2513    fn ack_coding() {
+2514        const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14];
+2515        let mut ranges = ArrayRangeSet::new();
+2516        for &packet in PACKETS {
+2517            ranges.insert(packet..packet + 1);
+2518        }
+2519        let mut buf = Vec::new();
+2520        const ECN: EcnCounts = EcnCounts {
+2521            ect0: 42,
+2522            ect1: 24,
+2523            ce: 12,
+2524        };
+2525        Ack::encoder(42, &ranges, Some(&ECN)).encode(&mut buf);
+2526        let frames = frames(buf);
+2527        assert_eq!(frames.len(), 1);
+2528        match frames[0] {
+2529            Frame::Ack(ref ack) => {
+2530                let mut packets = ack.iter().flatten().collect::<Vec<_>>();
+2531                packets.sort_unstable();
+2532                assert_eq!(&packets[..], PACKETS);
+2533                assert_eq!(ack.ecn, Some(ECN));
+2534            }
+2535            ref x => panic!("incorrect frame {x:?}"),
+2536        }
+2537    }
+2538
+2539    #[test]
+2540    #[allow(clippy::range_plus_one)]
+2541    fn path_ack_coding_with_ecn() {
+2542        const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14];
+2543        let mut ranges = ArrayRangeSet::new();
+2544        for &packet in PACKETS {
+2545            ranges.insert(packet..packet + 1);
+2546        }
+2547        let mut buf = Vec::new();
+2548        const ECN: EcnCounts = EcnCounts {
+2549            ect0: 42,
+2550            ect1: 24,
+2551            ce: 12,
+2552        };
+2553        const PATH_ID: PathId = PathId::MAX;
+2554        PathAck::encoder(PATH_ID, 42, &ranges, Some(&ECN)).encode(&mut buf);
+2555        let frames = frames(buf);
+2556        assert_eq!(frames.len(), 1);
+2557        match frames[0] {
+2558            Frame::PathAck(ref ack) => {
+2559                assert_eq!(ack.path_id, PATH_ID);
+2560                let mut packets = ack.ranges.iter().flatten().collect::<Vec<_>>();
+2561                packets.sort_unstable();
+2562                assert_eq!(&packets[..], PACKETS);
+2563                assert_eq!(ack.ecn, Some(ECN));
+2564            }
+2565            ref x => panic!("incorrect frame {x:?}"),
+2566        }
+2567    }
+2568
+2569    #[test]
+2570    #[allow(clippy::range_plus_one)]
+2571    fn path_ack_coding_no_ecn() {
+2572        const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14];
+2573        let mut ranges = ArrayRangeSet::new();
+2574        for &packet in PACKETS {
+2575            ranges.insert(packet..packet + 1);
+2576        }
+2577        let mut buf = Vec::new();
+2578        const PATH_ID: PathId = PathId::MAX;
+2579        PathAck::encoder(PATH_ID, 42, &ranges, None).encode(&mut buf);
+2580        let frames = frames(buf);
+2581        assert_eq!(frames.len(), 1);
+2582        match frames[0] {
+2583            Frame::PathAck(ref ack) => {
+2584                assert_eq!(ack.path_id, PATH_ID);
+2585                let mut packets = ack.ranges.iter().flatten().collect::<Vec<_>>();
+2586                packets.sort_unstable();
+2587                assert_eq!(&packets[..], PACKETS);
+2588                assert_eq!(ack.ecn, None);
+2589            }
+2590            ref x => panic!("incorrect frame {x:?}"),
+2591        }
+2592    }
+2593
+2594    #[test]
+2595    fn ack_frequency_coding() {
+2596        let mut buf = Vec::new();
+2597        let original = AckFrequency {
+2598            sequence: VarInt(42),
+2599            ack_eliciting_threshold: VarInt(20),
+2600            request_max_ack_delay: VarInt(50_000),
+2601            reordering_threshold: VarInt(1),
+2602        };
+2603        original.encode(&mut buf);
+2604        let frames = frames(buf);
+2605        assert_eq!(frames.len(), 1);
+2606        match &frames[0] {
+2607            Frame::AckFrequency(decoded) => assert_eq!(decoded, &original),
+2608            x => panic!("incorrect frame {x:?}"),
+2609        }
+2610    }
+2611
+2612    #[test]
+2613    fn immediate_ack_coding() {
+2614        let mut buf = Vec::new();
+2615        FrameType::ImmediateAck.encode(&mut buf);
+2616        let frames = frames(buf);
+2617        assert_eq!(frames.len(), 1);
+2618        assert_matches!(&frames[0], Frame::ImmediateAck);
+2619    }
+2620
+2621    /// Test that encoding and decoding [`ObservedAddr`] produces the same result.
+2622    #[test]
+2623    fn test_observed_addr_roundrip() {
+2624        let observed_addr = ObservedAddr {
+2625            seq_no: VarInt(42),
+2626            ip: std::net::Ipv4Addr::LOCALHOST.into(),
+2627            port: 4242,
+2628        };
+2629        let mut buf = Vec::with_capacity(observed_addr.size());
+2630        observed_addr.encode(&mut buf);
+2631
+2632        assert_eq!(
+2633            observed_addr.size(),
+2634            buf.len(),
+2635            "expected written bytes and actual size differ"
+2636        );
+2637
+2638        let mut decoded = frames(buf);
+2639        assert_eq!(decoded.len(), 1);
+2640        match decoded.pop().expect("non empty") {
+2641            Frame::ObservedAddr(decoded) => assert_eq!(decoded, observed_addr),
+2642            x => panic!("incorrect frame {x:?}"),
+2643        }
+2644    }
+2645
+2646    #[test]
+2647    fn test_path_abandon_roundtrip() {
+2648        let abandon = PathAbandon {
+2649            path_id: PathId(42),
+2650            error_code: TransportErrorCode::NO_ERROR,
+2651        };
+2652        let mut buf = Vec::new();
+2653        abandon.encode(&mut buf);
+2654
+2655        let mut decoded = frames(buf);
+2656        assert_eq!(decoded.len(), 1);
+2657        match decoded.pop().expect("non empty") {
+2658            Frame::PathAbandon(decoded) => assert_eq!(decoded, abandon),
+2659            x => panic!("incorrect frame {x:?}"),
+2660        }
+2661    }
+2662
+2663    #[test]
+2664    fn test_path_status_available_roundtrip() {
+2665        let path_status_available = PathStatusAvailable {
+2666            path_id: PathId(42),
+2667            status_seq_no: VarInt(73),
+2668        };
+2669        let mut buf = Vec::new();
+2670        path_status_available.encode(&mut buf);
+2671
+2672        let mut decoded = frames(buf);
+2673        assert_eq!(decoded.len(), 1);
+2674        match decoded.pop().expect("non empty") {
+2675            Frame::PathStatusAvailable(decoded) => assert_eq!(decoded, path_status_available),
+2676            x => panic!("incorrect frame {x:?}"),
+2677        }
+2678    }
+2679
+2680    #[test]
+2681    fn test_path_status_backup_roundtrip() {
+2682        let path_status_backup = PathStatusBackup {
+2683            path_id: PathId(42),
+2684            status_seq_no: VarInt(73),
+2685        };
+2686        let mut buf = Vec::new();
+2687        path_status_backup.encode(&mut buf);
+2688
+2689        let mut decoded = frames(buf);
+2690        assert_eq!(decoded.len(), 1);
+2691        match decoded.pop().expect("non empty") {
+2692            Frame::PathStatusBackup(decoded) => assert_eq!(decoded, path_status_backup),
+2693            x => panic!("incorrect frame {x:?}"),
+2694        }
+2695    }
+2696
+2697    #[test]
+2698    fn test_path_new_connection_id_roundtrip() {
+2699        let cid = NewConnectionId {
+2700            path_id: Some(PathId(22)),
+2701            sequence: 31,
+2702            retire_prior_to: 13,
+2703            id: ConnectionId::new(&[0xAB; 8]),
+2704            reset_token: ResetToken::from([0xCD; crate::RESET_TOKEN_SIZE]),
+2705        };
+2706        let mut buf = Vec::new();
+2707        cid.encode(&mut buf);
+2708
+2709        let mut decoded = frames(buf);
+2710        assert_eq!(decoded.len(), 1);
+2711        match decoded.pop().expect("non empty") {
+2712            Frame::NewConnectionId(decoded) => assert_eq!(decoded, cid),
+2713            x => panic!("incorrect frame {x:?}"),
+2714        }
+2715    }
+2716
+2717    #[test]
+2718    fn test_path_retire_connection_id_roundtrip() {
+2719        let retire_cid = RetireConnectionId {
+2720            path_id: Some(PathId(22)),
+2721            sequence: 31,
+2722        };
+2723        let mut buf = Vec::new();
+2724        retire_cid.encode(&mut buf);
+2725
+2726        let mut decoded = frames(buf);
+2727        assert_eq!(decoded.len(), 1);
+2728        match decoded.pop().expect("non empty") {
+2729            Frame::RetireConnectionId(decoded) => assert_eq!(decoded, retire_cid),
+2730            x => panic!("incorrect frame {x:?}"),
+2731        }
+2732    }
+2733
+2734    #[test]
+2735    fn test_paths_blocked_path_cids_blocked_roundtrip() {
+2736        let mut buf = Vec::new();
+2737
+2738        let frame0 = PathsBlocked(PathId(22));
+2739        frame0.encode(&mut buf);
+2740        let frame1 = PathCidsBlocked {
+2741            path_id: PathId(23),
+2742            next_seq: VarInt(32),
+2743        };
+2744        frame1.encode(&mut buf);
+2745
+2746        let mut decoded = frames(buf);
+2747        assert_eq!(decoded.len(), 2);
+2748        match decoded.pop().expect("non empty") {
+2749            Frame::PathCidsBlocked(decoded) => assert_eq!(decoded, frame1),
+2750            x => panic!("incorrect frame {x:?}"),
+2751        }
+2752        match decoded.pop().expect("non empty") {
+2753            Frame::PathsBlocked(decoded) => assert_eq!(decoded, frame0),
+2754            x => panic!("incorrect frame {x:?}"),
+2755        }
+2756    }
+2757
+2758    /// Test that encoding and decoding [`AddAddress`] produces the same result
+2759    #[test]
+2760    fn test_add_address_roundrip() {
+2761        let add_address = AddAddress {
+2762            seq_no: VarInt(42),
+2763            ip: std::net::Ipv4Addr::LOCALHOST.into(),
+2764            port: 4242,
+2765        };
+2766        let mut buf = Vec::with_capacity(add_address.size());
+2767        add_address.encode(&mut buf);
+2768
+2769        assert_eq!(
+2770            add_address.size(),
+2771            buf.len(),
+2772            "expected written bytes and actual size differ"
+2773        );
+2774
+2775        let mut decoded = frames(buf);
+2776        assert_eq!(decoded.len(), 1);
+2777        match decoded.pop().expect("non empty") {
+2778            Frame::AddAddress(decoded) => assert_eq!(decoded, add_address),
+2779            x => panic!("incorrect frame {x:?}"),
+2780        }
+2781    }
+2782
+2783    /// Test that encoding and decoding [`AddAddress`] produces the same result
+2784    #[test]
+2785    fn test_reach_out_roundrip() {
+2786        let reach_out = ReachOut {
+2787            round: VarInt(42),
+2788            ip: std::net::Ipv6Addr::LOCALHOST.into(),
+2789            port: 4242,
+2790        };
+2791        let mut buf = Vec::with_capacity(reach_out.size());
+2792        reach_out.encode(&mut buf);
+2793
+2794        assert_eq!(
+2795            reach_out.size(),
+2796            buf.len(),
+2797            "expected written bytes and actual size differ"
+2798        );
+2799
+2800        let mut decoded = frames(buf);
+2801        assert_eq!(decoded.len(), 1);
+2802        match decoded.pop().expect("non empty") {
+2803            Frame::ReachOut(decoded) => assert_eq!(decoded, reach_out),
+2804            x => panic!("incorrect frame {x:?}"),
+2805        }
+2806    }
+2807
+2808    /// Test that encoding and decoding [`RemoveAddress`] produces the same result
+2809    #[test]
+2810    fn test_remove_address_roundrip() {
+2811        let remove_addr = RemoveAddress::new(VarInt(10));
+2812        let mut buf = Vec::with_capacity(remove_addr.size());
+2813        remove_addr.encode(&mut buf);
+2814
+2815        assert_eq!(
+2816            remove_addr.size(),
+2817            buf.len(),
+2818            "expected written bytes and actual size differ"
+2819        );
+2820
+2821        let mut decoded = frames(buf);
+2822        assert_eq!(decoded.len(), 1);
+2823        match decoded.pop().expect("non empty") {
+2824            Frame::RemoveAddress(decoded) => assert_eq!(decoded, remove_addr),
+2825            x => panic!("incorrect frame {x:?}"),
+2826        }
+2827    }
+2828}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/lib.rs.html b/pr/509/docs/src/noq_proto/lib.rs.html new file mode 100644 index 000000000..f39bf4635 --- /dev/null +++ b/pr/509/docs/src/noq_proto/lib.rs.html @@ -0,0 +1,435 @@ +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, Written,
+51};
+52#[cfg(test)]
+53use test_strategy::Arbitrary;
+54
+55#[cfg(feature = "rustls")]
+56pub use rustls;
+57
+58mod config;
+59#[cfg(doc)]
+60pub use config::DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED;
+61pub use config::{
+62    AckFrequencyConfig, ClientConfig, ConfigError, EndpointConfig, IdleTimeout, MtuDiscoveryConfig,
+63    ServerConfig, StdSystemTime, TimeSource, TransportConfig, ValidationTokenConfig,
+64};
+65#[cfg(feature = "qlog")]
+66pub use config::{QlogConfig, QlogFactory, QlogFileFactory};
+67
+68pub mod crypto;
+69
+70mod frame;
+71pub use crate::frame::{
+72    ApplicationClose, ConnectionClose, Datagram, DatagramInfo, FrameType, InvalidFrameId,
+73    MaybeFrame, StreamInfo,
+74};
+75use crate::{
+76    coding::{Decodable, Encodable},
+77    frame::Frame,
+78};
+79
+80mod endpoint;
+81pub use crate::endpoint::{
+82    AcceptError, ConnectError, ConnectionHandle, DatagramEvent, DecryptedInitial, Endpoint,
+83    Incoming, IncomingAlpns, RetryError,
+84};
+85
+86mod packet;
+87pub use packet::{
+88    ConnectionIdParser, FixedLengthConnectionIdParser, LongType, PacketDecodeError, PartialDecode,
+89    ProtectedHeader, ProtectedInitialHeader,
+90};
+91
+92mod shared;
+93pub use crate::shared::{ConnectionEvent, ConnectionId, EcnCodepoint, EndpointEvent};
+94
+95mod transport_error;
+96pub use crate::transport_error::{Code as TransportErrorCode, Error as TransportError};
+97
+98pub mod congestion;
+99
+100mod cid_generator;
+101pub use crate::cid_generator::{
+102    ConnectionIdGenerator, HashedConnectionIdGenerator, InvalidCid, RandomConnectionIdGenerator,
+103};
+104
+105mod token;
+106use token::ResetToken;
+107pub use token::{NoneTokenLog, NoneTokenStore, TokenLog, TokenReuseError, TokenStore};
+108
+109mod address_discovery;
+110
+111mod token_memory_cache;
+112pub use token_memory_cache::TokenMemoryCache;
+113
+114pub mod n0_nat_traversal;
+115
+116// Deal with time
+117#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
+118pub(crate) use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
+119#[cfg(all(target_family = "wasm", target_os = "unknown"))]
+120pub(crate) use web_time::{Duration, Instant, SystemTime, UNIX_EPOCH};
+121
+122#[cfg(feature = "bench")]
+123pub mod bench_exports {
+124    //! Exports for benchmarks
+125    pub use crate::connection::send_buffer::send_buffer_benches;
+126}
+127
+128#[cfg(fuzzing)]
+129pub mod fuzzing {
+130    pub use crate::connection::{Retransmits, State as ConnectionState, StreamsState};
+131    pub use crate::frame::ResetStream;
+132    pub use crate::packet::PartialDecode;
+133    pub use crate::transport_parameters::TransportParameters;
+134    pub use bytes::{BufMut, BytesMut};
+135
+136    #[cfg(feature = "arbitrary")]
+137    use arbitrary::{Arbitrary, Result, Unstructured};
+138
+139    #[cfg(feature = "arbitrary")]
+140    impl<'arbitrary> Arbitrary<'arbitrary> for TransportParameters {
+141        fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self> {
+142            Ok(Self {
+143                initial_max_streams_bidi: u.arbitrary()?,
+144                initial_max_streams_uni: u.arbitrary()?,
+145                ack_delay_exponent: u.arbitrary()?,
+146                max_udp_payload_size: u.arbitrary()?,
+147                ..Self::default()
+148            })
+149        }
+150    }
+151
+152    #[derive(Debug)]
+153    pub struct PacketParams {
+154        pub local_cid_len: usize,
+155        pub buf: BytesMut,
+156        pub grease_quic_bit: bool,
+157    }
+158
+159    #[cfg(feature = "arbitrary")]
+160    impl<'arbitrary> Arbitrary<'arbitrary> for PacketParams {
+161        fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self> {
+162            let local_cid_len: usize = u.int_in_range(0..=crate::MAX_CID_SIZE)?;
+163            let bytes: Vec<u8> = Vec::arbitrary(u)?;
+164            let mut buf = BytesMut::new();
+165            buf.put_slice(&bytes[..]);
+166            Ok(Self {
+167                local_cid_len,
+168                buf,
+169                grease_quic_bit: bool::arbitrary(u)?,
+170            })
+171        }
+172    }
+173}
+174
+175/// The QUIC protocol version implemented.
+176pub const DEFAULT_SUPPORTED_VERSIONS: &[u32] = &[
+177    0x00000001,
+178    0xff00_001d,
+179    0xff00_001e,
+180    0xff00_001f,
+181    0xff00_0020,
+182    0xff00_0021,
+183    0xff00_0022,
+184];
+185
+186/// Whether an endpoint was the initiator of a connection
+187#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
+188#[cfg_attr(test, derive(Arbitrary))]
+189#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+190pub enum Side {
+191    /// The initiator of a connection
+192    Client = 0,
+193    /// The acceptor of a connection
+194    Server = 1,
+195}
+196
+197impl Side {
+198    #[inline]
+199    /// Shorthand for `self == Side::Client`
+200    pub fn is_client(self) -> bool {
+201        self == Self::Client
+202    }
+203
+204    #[inline]
+205    /// Shorthand for `self == Side::Server`
+206    pub fn is_server(self) -> bool {
+207        self == Self::Server
+208    }
+209}
+210
+211impl ops::Not for Side {
+212    type Output = Self;
+213    fn not(self) -> Self {
+214        match self {
+215            Self::Client => Self::Server,
+216            Self::Server => Self::Client,
+217        }
+218    }
+219}
+220
+221/// Whether a stream communicates data in both directions or only from the initiator
+222#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
+223#[cfg_attr(test, derive(Arbitrary))]
+224#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+225pub enum Dir {
+226    /// Data flows in both directions
+227    Bi = 0,
+228    /// Data flows only from the stream's initiator
+229    Uni = 1,
+230}
+231
+232impl Dir {
+233    fn iter() -> impl Iterator<Item = Self> {
+234        [Self::Bi, Self::Uni].iter().cloned()
+235    }
+236}
+237
+238impl fmt::Display for Dir {
+239    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+240        use Dir::*;
+241        f.pad(match *self {
+242            Bi => "bidirectional",
+243            Uni => "unidirectional",
+244        })
+245    }
+246}
+247
+248/// Identifier for a stream within a particular connection
+249#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+250#[cfg_attr(test, derive(Arbitrary))]
+251pub struct StreamId(#[cfg_attr(test, strategy(crate::varint::varint_u64()))] u64);
+252
+253impl fmt::Display for StreamId {
+254    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+255        let initiator = match self.initiator() {
+256            Side::Client => "client",
+257            Side::Server => "server",
+258        };
+259        let dir = match self.dir() {
+260            Dir::Uni => "uni",
+261            Dir::Bi => "bi",
+262        };
+263        write!(
+264            f,
+265            "{} {}directional stream {}",
+266            initiator,
+267            dir,
+268            self.index()
+269        )
+270    }
+271}
+272
+273impl StreamId {
+274    /// Create a new StreamId
+275    pub fn new(initiator: Side, dir: Dir, index: u64) -> Self {
+276        Self((index << 2) | ((dir as u64) << 1) | initiator as u64)
+277    }
+278    /// Which side of a connection initiated the stream
+279    pub fn initiator(self) -> Side {
+280        if self.0 & 0x1 == 0 {
+281            Side::Client
+282        } else {
+283            Side::Server
+284        }
+285    }
+286    /// Which directions data flows in
+287    pub fn dir(self) -> Dir {
+288        if self.0 & 0x2 == 0 { Dir::Bi } else { Dir::Uni }
+289    }
+290    /// Distinguishes streams of the same initiator and directionality
+291    pub fn index(self) -> u64 {
+292        self.0 >> 2
+293    }
+294}
+295
+296impl From<StreamId> for VarInt {
+297    fn from(x: StreamId) -> Self {
+298        unsafe { Self::from_u64_unchecked(x.0) }
+299    }
+300}
+301
+302impl From<VarInt> for StreamId {
+303    fn from(v: VarInt) -> Self {
+304        Self(v.0)
+305    }
+306}
+307
+308impl From<StreamId> for u64 {
+309    fn from(x: StreamId) -> Self {
+310        x.0
+311    }
+312}
+313
+314impl Decodable for StreamId {
+315    fn decode<B: bytes::Buf>(buf: &mut B) -> coding::Result<Self> {
+316        VarInt::decode(buf).map(|x| Self(x.into_inner()))
+317    }
+318}
+319
+320impl Encodable for StreamId {
+321    fn encode<B: bytes::BufMut>(&self, buf: &mut B) {
+322        VarInt::from_u64(self.0).unwrap().encode(buf);
+323    }
+324}
+325
+326#[cfg(feature = "arbitrary")]
+327impl<'arbitrary> arbitrary::Arbitrary<'arbitrary> for StreamId {
+328    fn arbitrary(u: &mut arbitrary::Unstructured<'arbitrary>) -> arbitrary::Result<Self> {
+329        Ok(VarInt::arbitrary(u)?.into())
+330    }
+331}
+332
+333/// An outgoing packet
+334#[derive(Debug)]
+335#[must_use]
+336pub struct Transmit {
+337    /// The socket this datagram should be sent to
+338    pub destination: SocketAddr,
+339    /// Explicit congestion notification bits to set on the packet
+340    pub ecn: Option<EcnCodepoint>,
+341    /// Amount of data written to the caller-supplied buffer
+342    pub size: usize,
+343    /// The segment size if this transmission contains multiple datagrams.
+344    /// This is `None` if the transmit only contains a single datagram
+345    pub segment_size: Option<usize>,
+346    /// Optional source IP address for the datagram
+347    pub src_ip: Option<IpAddr>,
+348}
+349
+350//
+351// Useful internal constants
+352//
+353
+354/// The maximum number of CIDs we bother to issue per path
+355const LOCAL_CID_COUNT: u64 = 12;
+356const RESET_TOKEN_SIZE: usize = 16;
+357const MAX_CID_SIZE: usize = 20;
+358const MIN_INITIAL_SIZE: u16 = 1200;
+359/// <https://www.rfc-editor.org/rfc/rfc9000.html#name-datagram-size>
+360const INITIAL_MTU: u16 = 1200;
+361const MAX_UDP_PAYLOAD: u16 = 65527;
+362const TIMER_GRANULARITY: Duration = Duration::from_millis(1);
+363/// Maximum number of streams that can be uniquely identified by a stream ID
+364const MAX_STREAM_COUNT: u64 = 1 << 60;
+365
+366/// Identifies a network path by the combination of remote and local addresses
+367///
+368/// Including the local ensures good behavior when the host has multiple IP addresses on the same
+369/// subnet and zero-length connection IDs are in use or when multipath is enabled and multiple
+370/// paths exist with the same remote, but different local IP interfaces.
+371#[derive(Hash, Eq, PartialEq, Copy, Clone)]
+372pub struct FourTuple {
+373    /// The remote side of this tuple
+374    pub 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    pub local_ip: Option<IpAddr>,
+381}
+382
+383impl FourTuple {
+384    /// Returns whether we think the other address probably represents the same path
+385    /// as ours.
+386    ///
+387    /// If we have a local IP set, then we're exact and only match if the 4-tuples are
+388    /// exactly equal.
+389    /// If we don't have a local IP set, then we only check the remote addresses for equality.
+390    ///
+391    /// Note that because of this, the following calls might differ:
+392    /// - `a.is_probably_same_path(b)`
+393    /// - `b.is_probably_same_path(a)`
+394    pub fn is_probably_same_path(&self, other: &Self) -> bool {
+395        self.remote == other.remote && (self.local_ip.is_none() || self.local_ip == other.local_ip)
+396    }
+397
+398    /// Updates this tuple's local address iff
+399    /// - it was unset before,
+400    /// - the other tuple has the same remote, and
+401    /// - the other tuple has a local address set.
+402    ///
+403    /// Returns whether this and the other remote are now fully equal.
+404    pub fn update_local_if_same_remote(&mut self, other: &Self) -> bool {
+405        if self.remote != other.remote {
+406            return false;
+407        }
+408        if self.local_ip.is_some() && self.local_ip != other.local_ip {
+409            return false;
+410        }
+411        self.local_ip = other.local_ip;
+412        true
+413    }
+414}
+415
+416impl fmt::Display for FourTuple {
+417    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+418        f.write_str("(local: ")?;
+419        if let Some(local_ip) = &self.local_ip {
+420            local_ip.fmt(f)?;
+421            f.write_str(", ")?;
+422        } else {
+423            f.write_str("<unspecified>, ")?;
+424        }
+425        f.write_str("remote: ")?;
+426        self.remote.fmt(f)?;
+427        f.write_str(")")
+428    }
+429}
+430
+431impl fmt::Debug for FourTuple {
+432    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+433        fmt::Display::fmt(&self, f)
+434    }
+435}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/n0_nat_traversal.rs.html b/pr/509/docs/src/noq_proto/n0_nat_traversal.rs.html new file mode 100644 index 000000000..68df2cd8a --- /dev/null +++ b/pr/509/docs/src/noq_proto/n0_nat_traversal.rs.html @@ -0,0 +1,578 @@ +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    net::{IpAddr, SocketAddr},
+6};
+7
+8use rustc_hash::{FxHashMap, FxHashSet};
+9use tracing::trace;
+10
+11use crate::{
+12    PathId, Side, VarInt,
+13    frame::{AddAddress, ReachOut, RemoveAddress},
+14};
+15
+16type IpPort = (IpAddr, u16);
+17
+18/// Errors that the nat traversal state might encounter.
+19#[derive(Debug, thiserror::Error)]
+20pub enum Error {
+21    /// An endpoint (local or remote) tried to add too many addresses to their advertised set
+22    #[error("Tried to add too many addresses to their advertised set")]
+23    TooManyAddresses,
+24    /// The operation is not allowed for this endpoint's connection side
+25    #[error("Not allowed for this endpoint's connection side")]
+26    WrongConnectionSide,
+27    /// The extension was not negotiated
+28    #[error("n0's nat traversal was not negotiated")]
+29    ExtensionNotNegotiated,
+30    /// Not enough addresses to complete the operation
+31    #[error("Not enough addresses")]
+32    NotEnoughAddresses,
+33    /// Nat traversal attempt failed due to a multipath error
+34    #[error("Failed to establish paths {0}")]
+35    Multipath(super::PathError),
+36    /// Attempted to initiate NAT traversal on a closed, or closing connection.
+37    #[error("The connection is already closed")]
+38    Closed,
+39}
+40
+41pub(crate) struct NatTraversalRound {
+42    /// Sequence number to use for the new reach out frames.
+43    pub(crate) new_round: VarInt,
+44    /// Addresses to use to send reach out frames.
+45    pub(crate) reach_out_at: FxHashSet<IpPort>,
+46    /// Remotes to probe by attempting to open new paths.
+47    ///
+48    /// The addresses include their Id, so that it can be used to signal these should be returned
+49    /// in a nat traversal continuation by calling [`ClientState::report_in_continuation`].
+50    ///
+51    /// These are filtered and mapped to the IP family the local socket supports.
+52    pub(crate) addresses_to_probe: Vec<(VarInt, IpPort)>,
+53    /// [`PathId`]s of the cancelled round.
+54    pub(crate) prev_round_path_ids: Vec<PathId>,
+55}
+56
+57/// Event emitted when the client receives ADD_ADDRESS or REMOVE_ADDRESS frames.
+58#[derive(Debug, Clone)]
+59pub enum Event {
+60    /// An ADD_ADDRESS frame was received.
+61    AddressAdded(SocketAddr),
+62    /// A REMOVE_ADDRESS frame was received.
+63    AddressRemoved(SocketAddr),
+64}
+65
+66/// State kept for n0's nat traversal
+67#[derive(Debug, Default)]
+68pub(crate) enum State {
+69    #[default]
+70    NotNegotiated,
+71    ClientSide(ClientState),
+72    ServerSide(ServerState),
+73}
+74
+75#[derive(Debug)]
+76pub(crate) struct ClientState {
+77    /// Max number of remote addresses we allow
+78    ///
+79    /// This is set by the local endpoint.
+80    max_remote_addresses: usize,
+81    /// Max number of local addresses allowed
+82    ///
+83    /// This is set by the remote endpoint.
+84    max_local_addresses: usize,
+85    /// Candidate addresses the remote server reports as potentially reachable, to use for nat
+86    /// traversal attempts.
+87    ///
+88    /// These are indexed by their advertised Id. For each address, whether the address should be
+89    /// reported in nat traversal continuations is kept.
+90    remote_addresses: FxHashMap<VarInt, (IpPort, bool)>,
+91    /// Candidate addresses the local client reports as potentially reachable, to use for nat
+92    /// traversal attempts.
+93    local_addresses: FxHashSet<IpPort>,
+94    /// Current nat traversal round.
+95    round: VarInt,
+96    /// [`PathId`]s used to probe remotes assigned to this round.
+97    round_path_ids: Vec<PathId>,
+98}
+99
+100impl ClientState {
+101    fn new(max_remote_addresses: usize, max_local_addresses: usize) -> Self {
+102        Self {
+103            max_remote_addresses,
+104            max_local_addresses,
+105            remote_addresses: Default::default(),
+106            local_addresses: Default::default(),
+107            round: Default::default(),
+108            round_path_ids: Default::default(),
+109        }
+110    }
+111
+112    fn add_local_address(&mut self, address: IpPort) -> Result<(), Error> {
+113        if self.local_addresses.len() < self.max_local_addresses {
+114            self.local_addresses.insert(address);
+115            Ok(())
+116        } else if self.local_addresses.contains(&address) {
+117            // at capacity, but the address is known, no issues here
+118            Ok(())
+119        } else {
+120            // at capacity and the address is new
+121            Err(Error::TooManyAddresses)
+122        }
+123    }
+124
+125    fn remove_local_address(&mut self, address: &IpPort) {
+126        self.local_addresses.remove(address);
+127    }
+128
+129    /// Initiates a new nat traversal round.
+130    ///
+131    /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT`
+132    /// frames, and initiating probing of the known remote addresses. When a new round is
+133    /// initiated, the previous one is cancelled, and paths that have not been opened should be
+134    /// closed.
+135    ///
+136    /// `ipv6` indicates if the connection runs on a socket that supports IPv6. If so, then all
+137    /// addresses returned in [`NatTraversalRound`] will be IPv6 addresses (and IPv4-mapped IPv6
+138    /// addresses if necessary). Otherwise they're all IPv4 addresses.
+139    /// See also [`map_to_local_socket_family`].
+140    pub(crate) fn initiate_nat_traversal_round(
+141        &mut self,
+142        ipv6: bool,
+143    ) -> Result<NatTraversalRound, Error> {
+144        if self.local_addresses.is_empty() {
+145            return Err(Error::NotEnoughAddresses);
+146        }
+147
+148        let prev_round_path_ids = std::mem::take(&mut self.round_path_ids);
+149        self.round = self.round.saturating_add(1u8);
+150        let mut addresses_to_probe = Vec::with_capacity(self.remote_addresses.len());
+151        for (id, ((ip, port), report_in_continuation)) in self.remote_addresses.iter_mut() {
+152            *report_in_continuation = false;
+153
+154            if let Some(ip) = map_to_local_socket_family(*ip, ipv6) {
+155                addresses_to_probe.push((*id, (ip, *port)));
+156            } else {
+157                trace!(?ip, "not using IPv6 nat candidate for IPv4 socket");
+158            }
+159        }
+160
+161        Ok(NatTraversalRound {
+162            new_round: self.round,
+163            reach_out_at: self.local_addresses.iter().copied().collect(),
+164            addresses_to_probe,
+165            prev_round_path_ids,
+166        })
+167    }
+168
+169    /// Mark a remote address to be reported back in a nat traversal continuation if the error is
+170    /// considered spurious from a nat traversal point of view.
+171    ///
+172    /// Ids not present are silently ignored.
+173    pub(crate) fn report_in_continuation(&mut self, id: VarInt, e: crate::PathError) {
+174        match e {
+175            crate::PathError::MaxPathIdReached | crate::PathError::RemoteCidsExhausted => {
+176                if let Some((_address, report_in_continuation)) = self.remote_addresses.get_mut(&id)
+177                {
+178                    *report_in_continuation = true;
+179                }
+180            }
+181            _ => {}
+182        }
+183    }
+184
+185    /// Returns an address that needs to be probed, if any.
+186    ///
+187    /// The address will not be returned twice unless marked as such again with
+188    /// [`Self::report_in_continuation`].
+189    ///
+190    /// `ipv6` indicates if the connection runs on a socket that supports IPv6. If so, then all
+191    /// addresses returned in [`NatTraversalRound`] will be IPv6 addresses (and IPv4-mapped IPv6
+192    /// addresses if necessary). Otherwise they're all IPv4 addresses.
+193    /// See also [`map_to_local_socket_family`].
+194    pub(crate) fn continue_nat_traversal_round(&mut self, ipv6: bool) -> Option<(VarInt, IpPort)> {
+195        // this being random depends on iteration not returning always on the same order
+196        let (id, (address, report_in_continuation)) = self
+197            .remote_addresses
+198            .iter_mut()
+199            .filter(|(_id, (_addr, report))| *report)
+200            .filter_map(|(id, ((ip, port), report))| {
+201                // only continue with addresses we can send on our local socket
+202                let Some(ip) = map_to_local_socket_family(*ip, ipv6) else {
+203                    trace!(?ip, "not using IPv6 nat candidate for IPv4 socket");
+204                    return None;
+205                };
+206                Some((*id, ((ip, *port), report)))
+207            })
+208            .next()?;
+209        *report_in_continuation = false;
+210        Some((id, address))
+211    }
+212
+213    /// Add a [`PathId`] as part of the current attempts to create paths based on the server's
+214    /// advertised addresses.
+215    pub(crate) fn set_round_path_ids(&mut self, path_ids: Vec<PathId>) {
+216        self.round_path_ids = path_ids;
+217    }
+218
+219    /// Add a [`PathId`] as part of the current attempts to create paths based on the server's
+220    /// advertised addresses.
+221    pub(crate) fn add_round_path_id(&mut self, path_id: PathId) {
+222        self.round_path_ids.push(path_id);
+223    }
+224
+225    /// Adds an address to the remote set
+226    ///
+227    /// On success returns the address if it was new to the set. It will error when the set has no
+228    /// capacity for the address.
+229    pub(crate) fn add_remote_address(
+230        &mut self,
+231        add_addr: AddAddress,
+232    ) -> Result<Option<SocketAddr>, Error> {
+233        let AddAddress { seq_no, ip, port } = add_addr;
+234        let address = (ip, port);
+235        let allow_new = self.remote_addresses.len() < self.max_remote_addresses;
+236        match self.remote_addresses.entry(seq_no) {
+237            Entry::Occupied(mut occupied_entry) => {
+238                let is_update = occupied_entry.get().0 != address;
+239                if is_update {
+240                    occupied_entry.insert((address, false));
+241                }
+242                // The value might be different. This should not happen, but we assume that the new
+243                // address is more recent than the previous, and thus worth updating
+244                Ok(is_update.then_some(address.into()))
+245            }
+246            Entry::Vacant(vacant_entry) if allow_new => {
+247                vacant_entry.insert((address, false));
+248                Ok(Some(address.into()))
+249            }
+250            _ => Err(Error::TooManyAddresses),
+251        }
+252    }
+253
+254    /// Removes an address from the remote set
+255    ///
+256    /// Returns whether the address was present.
+257    pub(crate) fn remove_remote_address(
+258        &mut self,
+259        remove_addr: RemoveAddress,
+260    ) -> Option<SocketAddr> {
+261        self.remote_addresses
+262            .remove(&remove_addr.seq_no)
+263            .map(|(address, _report_in_continuation)| address.into())
+264    }
+265
+266    /// Checks that a received remote address is valid
+267    ///
+268    /// An address is valid as long as it does not change the value of a known address id.
+269    pub(crate) fn check_remote_address(&self, add_addr: &AddAddress) -> bool {
+270        match self.remote_addresses.get(&add_addr.seq_no) {
+271            None => true,
+272            Some((existing, _)) => existing == &add_addr.ip_port(),
+273        }
+274    }
+275
+276    pub(crate) fn get_remote_nat_traversal_addresses(&self) -> Vec<SocketAddr> {
+277        self.remote_addresses
+278            .values()
+279            .map(|(address, _report_in_continuation)| (*address).into())
+280            .collect()
+281    }
+282}
+283
+284#[derive(Debug)]
+285pub(crate) struct ServerState {
+286    /// Max number of remote addresses we allow.
+287    ///
+288    /// This is set by the local endpoint.
+289    max_remote_addresses: usize,
+290    /// Max number of local addresses allowed.
+291    ///
+292    /// This is set by the remote endpoint.
+293    max_local_addresses: usize,
+294    /// Candidate addresses the server reports as potentially reachable, to use for nat
+295    /// traversal attempts.
+296    local_addresses: FxHashMap<IpPort, VarInt>,
+297    /// The next id to use for local addresses sent to the client.
+298    next_local_addr_id: VarInt,
+299    /// Current nat traversal round
+300    ///
+301    /// Servers keep track of the client's most recent round and cancel probing related to previous
+302    /// rounds.
+303    round: VarInt,
+304    /// Addresses to which PATH_CHALLENGES need to be sent.
+305    pending_probes: FxHashSet<IpPort>,
+306}
+307
+308impl ServerState {
+309    fn new(max_remote_addresses: usize, max_local_addresses: usize) -> Self {
+310        Self {
+311            max_remote_addresses,
+312            max_local_addresses,
+313            local_addresses: Default::default(),
+314            next_local_addr_id: Default::default(),
+315            round: Default::default(),
+316            pending_probes: Default::default(),
+317        }
+318    }
+319
+320    fn add_local_address(&mut self, address: IpPort) -> Result<Option<AddAddress>, Error> {
+321        let allow_new = self.local_addresses.len() < self.max_local_addresses;
+322        match self.local_addresses.entry(address) {
+323            Entry::Occupied(_) => Ok(None),
+324            Entry::Vacant(vacant_entry) if allow_new => {
+325                let id = self.next_local_addr_id;
+326                self.next_local_addr_id = self.next_local_addr_id.saturating_add(1u8);
+327                vacant_entry.insert(id);
+328                Ok(Some(AddAddress::new(address, id)))
+329            }
+330            _ => Err(Error::TooManyAddresses),
+331        }
+332    }
+333
+334    fn remove_local_address(&mut self, address: &IpPort) -> Option<RemoveAddress> {
+335        self.local_addresses.remove(address).map(RemoveAddress::new)
+336    }
+337
+338    /// Handles a received [`ReachOut`].
+339    ///
+340    /// This might ignore the reach out frame if it belongs to an older round or if
+341    /// the reach out can't be handled by an ipv4-only local socket.
+342    pub(crate) fn handle_reach_out(
+343        &mut self,
+344        reach_out: ReachOut,
+345        ipv6: bool,
+346    ) -> Result<(), Error> {
+347        let ReachOut { round, ip, port } = reach_out;
+348
+349        if round < self.round {
+350            trace!(current_round=%self.round, "ignoring REACH_OUT for previous round");
+351            return Ok(());
+352        }
+353        let Some(ip) = map_to_local_socket_family(ip, ipv6) else {
+354            trace!("Ignoring IPv6 REACH_OUT frame due to not supporting IPv6 locally");
+355            return Ok(());
+356        };
+357
+358        if round > self.round {
+359            self.round = round;
+360            self.pending_probes.clear();
+361        } else if self.pending_probes.len() >= self.max_remote_addresses
+362            && !self.pending_probes.contains(&(ip, port))
+363        {
+364            return Err(Error::TooManyAddresses);
+365        }
+366        self.pending_probes.insert((ip, port));
+367        Ok(())
+368    }
+369
+370    pub(crate) fn next_probe(&mut self) -> Option<ServerProbing<'_>> {
+371        self.pending_probes
+372            .iter()
+373            .next()
+374            .copied()
+375            .map(|remote| ServerProbing {
+376                remote,
+377                pending_probes: &mut self.pending_probes,
+378            })
+379    }
+380}
+381
+382pub(crate) struct ServerProbing<'a> {
+383    remote: IpPort,
+384    pending_probes: &'a mut FxHashSet<IpPort>,
+385}
+386
+387impl<'a> ServerProbing<'a> {
+388    pub(crate) fn mark_as_sent(self) {
+389        self.pending_probes.remove(&self.remote);
+390    }
+391
+392    pub(crate) fn remote(&self) -> SocketAddr {
+393        self.remote.into()
+394    }
+395}
+396
+397impl State {
+398    pub(crate) fn new(max_remote_addresses: u8, max_local_addresses: u8, side: Side) -> Self {
+399        match side {
+400            Side::Client => Self::ClientSide(ClientState::new(
+401                max_remote_addresses.into(),
+402                max_local_addresses.into(),
+403            )),
+404            Side::Server => Self::ServerSide(ServerState::new(
+405                max_remote_addresses.into(),
+406                max_local_addresses.into(),
+407            )),
+408        }
+409    }
+410
+411    pub(crate) fn client_side(&self) -> Result<&ClientState, Error> {
+412        match self {
+413            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+414            Self::ClientSide(client_side) => Ok(client_side),
+415            Self::ServerSide(_) => Err(Error::WrongConnectionSide),
+416        }
+417    }
+418
+419    pub(crate) fn client_side_mut(&mut self) -> Result<&mut ClientState, Error> {
+420        match self {
+421            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+422            Self::ClientSide(client_side) => Ok(client_side),
+423            Self::ServerSide(_) => Err(Error::WrongConnectionSide),
+424        }
+425    }
+426
+427    pub(crate) fn server_side_mut(&mut self) -> Result<&mut ServerState, Error> {
+428        match self {
+429            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+430            Self::ClientSide(_) => Err(Error::WrongConnectionSide),
+431            Self::ServerSide(server_side) => Ok(server_side),
+432        }
+433    }
+434
+435    /// Adds a local address to use for nat traversal.
+436    ///
+437    /// When this endpoint is the server within the connection, these addresses will be sent to the
+438    /// client in add address frames. For clients, these addresses will be sent in reach out frames
+439    /// when nat traversal attempts are initiated.
+440    ///
+441    /// If a frame should be sent, it is returned.
+442    pub(crate) fn add_local_address(
+443        &mut self,
+444        address: SocketAddr,
+445    ) -> Result<Option<AddAddress>, Error> {
+446        let ip_port = IpPort::from((address.ip(), address.port()));
+447        match self {
+448            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+449            Self::ClientSide(client_state) => {
+450                client_state.add_local_address(ip_port)?;
+451                Ok(None)
+452            }
+453            Self::ServerSide(server_state) => server_state.add_local_address(ip_port),
+454        }
+455    }
+456
+457    /// Removes a local address from the advertised set for nat traversal.
+458    ///
+459    /// When this endpoint is the server, removed addresses must be reported with remove address
+460    /// frames. Clients will simply stop reporting these addresses in reach out frames.
+461    ///
+462    /// If a frame should be sent, it is returned.
+463    pub(crate) fn remove_local_address(
+464        &mut self,
+465        address: SocketAddr,
+466    ) -> Result<Option<RemoveAddress>, Error> {
+467        let address = IpPort::from((address.ip(), address.port()));
+468        match self {
+469            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+470            Self::ClientSide(client_state) => {
+471                client_state.remove_local_address(&address);
+472                Ok(None)
+473            }
+474            Self::ServerSide(server_state) => Ok(server_state.remove_local_address(&address)),
+475        }
+476    }
+477
+478    pub(crate) fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, Error> {
+479        match self {
+480            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+481            Self::ClientSide(client_state) => Ok(client_state
+482                .local_addresses
+483                .iter()
+484                .copied()
+485                .map(Into::into)
+486                .collect()),
+487            Self::ServerSide(server_state) => Ok(server_state
+488                .local_addresses
+489                .keys()
+490                .copied()
+491                .map(Into::into)
+492                .collect()),
+493        }
+494    }
+495}
+496
+497/// Returns the given address as canonicalized IP address.
+498///
+499/// This checks that the address family is supported by our local socket.
+500/// If it is supported, then the address is mapped to the respective IP address.
+501/// If the given address is an IPv6 address, but our local socket doesn't support
+502/// IPv6, then this returns `None`.
+503pub(crate) fn map_to_local_socket_family(address: IpAddr, ipv6: bool) -> Option<IpAddr> {
+504    let ip = match address {
+505        IpAddr::V4(addr) if ipv6 => IpAddr::V6(addr.to_ipv6_mapped()),
+506        IpAddr::V4(_) => address,
+507        IpAddr::V6(_) if ipv6 => address,
+508        IpAddr::V6(addr) => IpAddr::V4(addr.to_ipv4_mapped()?),
+509    };
+510    Some(ip)
+511}
+512
+513#[cfg(test)]
+514mod tests {
+515    use super::*;
+516
+517    #[test]
+518    fn test_basic_server_state() {
+519        let mut state = ServerState::new(2, 2);
+520
+521        state
+522            .handle_reach_out(
+523                ReachOut {
+524                    round: 1u32.into(),
+525                    ip: std::net::Ipv4Addr::LOCALHOST.into(),
+526                    port: 1,
+527                },
+528                true,
+529            )
+530            .unwrap();
+531
+532        state
+533            .handle_reach_out(
+534                ReachOut {
+535                    round: 1u32.into(),
+536                    ip: "1.1.1.1".parse().unwrap(), //std::net::Ipv4Addr::LOCALHOST.into(),
+537                    port: 2,
+538                },
+539                true,
+540            )
+541            .unwrap();
+542
+543        dbg!(&state);
+544        assert_eq!(state.pending_probes.len(), 2);
+545
+546        let probe = state.next_probe().unwrap();
+547        probe.mark_as_sent();
+548        let probe = state.next_probe().unwrap();
+549        probe.mark_as_sent();
+550
+551        assert!(state.next_probe().is_none());
+552        assert_eq!(state.pending_probes.len(), 0);
+553    }
+554
+555    #[test]
+556    fn test_map_to_local_socket() {
+557        assert_eq!(
+558            map_to_local_socket_family("1.1.1.1".parse().unwrap(), false),
+559            Some("1.1.1.1".parse().unwrap())
+560        );
+561        assert_eq!(
+562            map_to_local_socket_family("1.1.1.1".parse().unwrap(), true),
+563            Some("::ffff:1.1.1.1".parse().unwrap())
+564        );
+565        assert_eq!(
+566            map_to_local_socket_family("::1".parse().unwrap(), true),
+567            Some("::1".parse().unwrap())
+568        );
+569        assert_eq!(
+570            map_to_local_socket_family("::1".parse().unwrap(), false),
+571            None
+572        );
+573        assert_eq!(
+574            map_to_local_socket_family("::ffff:1.1.1.1".parse().unwrap(), false),
+575            Some("1.1.1.1".parse().unwrap())
+576        )
+577    }
+578}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/packet.rs.html b/pr/509/docs/src/noq_proto/packet.rs.html new file mode 100644 index 000000000..f7b5e138e --- /dev/null +++ b/pr/509/docs/src/noq_proto/packet.rs.html @@ -0,0 +1,1089 @@ +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
+236pub(crate) struct Packet {
+237    pub(crate) header: Header,
+238    pub(crate) header_data: Bytes,
+239    pub(crate) payload: BytesMut,
+240}
+241
+242impl Packet {
+243    pub(crate) fn reserved_bits_valid(&self) -> bool {
+244        let mask = match self.header {
+245            Header::Short { .. } => SHORT_RESERVED_BITS,
+246            _ => LONG_RESERVED_BITS,
+247        };
+248        self.header_data[0] & mask == 0
+249    }
+250}
+251
+252pub(crate) struct InitialPacket {
+253    pub(crate) header: InitialHeader,
+254    pub(crate) header_data: Bytes,
+255    pub(crate) payload: BytesMut,
+256}
+257
+258impl From<InitialPacket> for Packet {
+259    fn from(x: InitialPacket) -> Self {
+260        Self {
+261            header: Header::Initial(x.header),
+262            header_data: x.header_data,
+263            payload: x.payload,
+264        }
+265    }
+266}
+267
+268#[cfg_attr(test, derive(Clone))]
+269#[derive(Debug)]
+270pub(crate) enum Header {
+271    Initial(InitialHeader),
+272    Long {
+273        ty: LongType,
+274        dst_cid: ConnectionId,
+275        src_cid: ConnectionId,
+276        number: PacketNumber,
+277        version: u32,
+278    },
+279    Retry {
+280        dst_cid: ConnectionId,
+281        src_cid: ConnectionId,
+282        version: u32,
+283    },
+284    Short {
+285        spin: bool,
+286        key_phase: bool,
+287        dst_cid: ConnectionId,
+288        number: PacketNumber,
+289    },
+290    VersionNegotiate {
+291        random: u8,
+292        src_cid: ConnectionId,
+293        dst_cid: ConnectionId,
+294    },
+295}
+296
+297impl Header {
+298    pub(crate) fn encode(&self, w: &mut (impl BufMut + BufLen)) -> PartialEncode {
+299        use Header::*;
+300        let start = w.len();
+301        match *self {
+302            Initial(InitialHeader {
+303                ref dst_cid,
+304                ref src_cid,
+305                ref token,
+306                number,
+307                version,
+308            }) => {
+309                w.write(u8::from(LongHeaderType::Initial) | number.tag());
+310                w.write(version);
+311                dst_cid.encode_long(w);
+312                src_cid.encode_long(w);
+313                w.write_var(token.len() as u64);
+314                w.put_slice(token);
+315                w.write::<u16>(0); // Placeholder for payload length; see `set_payload_length`
+316                number.encode(w);
+317                PartialEncode {
+318                    start,
+319                    header_len: w.len() - start,
+320                    pn: Some((number.len(), true)),
+321                }
+322            }
+323            Long {
+324                ty,
+325                ref dst_cid,
+326                ref src_cid,
+327                number,
+328                version,
+329            } => {
+330                w.write(u8::from(LongHeaderType::Standard(ty)) | number.tag());
+331                w.write(version);
+332                dst_cid.encode_long(w);
+333                src_cid.encode_long(w);
+334                w.write::<u16>(0); // Placeholder for payload length; see `set_payload_length`
+335                number.encode(w);
+336                PartialEncode {
+337                    start,
+338                    header_len: w.len() - start,
+339                    pn: Some((number.len(), true)),
+340                }
+341            }
+342            Retry {
+343                ref dst_cid,
+344                ref src_cid,
+345                version,
+346            } => {
+347                w.write(u8::from(LongHeaderType::Retry));
+348                w.write(version);
+349                dst_cid.encode_long(w);
+350                src_cid.encode_long(w);
+351                PartialEncode {
+352                    start,
+353                    header_len: w.len() - start,
+354                    pn: None,
+355                }
+356            }
+357            Short {
+358                spin,
+359                key_phase,
+360                ref dst_cid,
+361                number,
+362            } => {
+363                w.write(
+364                    FIXED_BIT
+365                        | if key_phase { KEY_PHASE_BIT } else { 0 }
+366                        | if spin { SPIN_BIT } else { 0 }
+367                        | number.tag(),
+368                );
+369                w.put_slice(dst_cid);
+370                number.encode(w);
+371                PartialEncode {
+372                    start,
+373                    header_len: w.len() - start,
+374                    pn: Some((number.len(), false)),
+375                }
+376            }
+377            VersionNegotiate {
+378                ref random,
+379                ref dst_cid,
+380                ref src_cid,
+381            } => {
+382                w.write(0x80u8 | random);
+383                w.write::<u32>(0);
+384                dst_cid.encode_long(w);
+385                src_cid.encode_long(w);
+386                PartialEncode {
+387                    start,
+388                    header_len: w.len() - start,
+389                    pn: None,
+390                }
+391            }
+392        }
+393    }
+394
+395    /// Whether the packet is encrypted on the wire
+396    pub(crate) fn is_protected(&self) -> bool {
+397        !matches!(*self, Self::Retry { .. } | Self::VersionNegotiate { .. })
+398    }
+399
+400    pub(crate) fn number(&self) -> Option<PacketNumber> {
+401        use Header::*;
+402        Some(match *self {
+403            Initial(InitialHeader { number, .. }) => number,
+404            Long { number, .. } => number,
+405            Short { number, .. } => number,
+406            _ => {
+407                return None;
+408            }
+409        })
+410    }
+411
+412    pub(crate) fn space(&self) -> SpaceKind {
+413        use Header::*;
+414        match *self {
+415            Short { .. } => SpaceKind::Data,
+416            Long {
+417                ty: LongType::ZeroRtt,
+418                ..
+419            } => SpaceKind::Data,
+420            Long {
+421                ty: LongType::Handshake,
+422                ..
+423            } => SpaceKind::Handshake,
+424            _ => SpaceKind::Initial,
+425        }
+426    }
+427
+428    pub(crate) fn key_phase(&self) -> bool {
+429        match *self {
+430            Self::Short { key_phase, .. } => key_phase,
+431            _ => false,
+432        }
+433    }
+434
+435    pub(crate) fn is_short(&self) -> bool {
+436        matches!(*self, Self::Short { .. })
+437    }
+438
+439    pub(crate) fn is_1rtt(&self) -> bool {
+440        self.is_short()
+441    }
+442
+443    pub(crate) fn is_0rtt(&self) -> bool {
+444        matches!(
+445            *self,
+446            Self::Long {
+447                ty: LongType::ZeroRtt,
+448                ..
+449            }
+450        )
+451    }
+452
+453    pub(crate) fn dst_cid(&self) -> ConnectionId {
+454        use Header::*;
+455        match *self {
+456            Initial(InitialHeader { dst_cid, .. }) => dst_cid,
+457            Long { dst_cid, .. } => dst_cid,
+458            Retry { dst_cid, .. } => dst_cid,
+459            Short { dst_cid, .. } => dst_cid,
+460            VersionNegotiate { dst_cid, .. } => dst_cid,
+461        }
+462    }
+463
+464    /// Is this packet allowed to be coalesced with others?
+465    ///
+466    /// Ref <https://www.rfc-editor.org/rfc/rfc9000.html#name-coalescing-packets>
+467    pub(crate) fn can_coalesce(&self) -> bool {
+468        use Header::*;
+469        match *self {
+470            Initial(_) => true,
+471            Long { .. } => true,
+472            Retry { .. } => false,
+473            Short { .. } => false,
+474            VersionNegotiate { .. } => false,
+475        }
+476    }
+477
+478    /// Whether the payload of this packet contains QUIC frames
+479    pub(crate) fn has_frames(&self) -> bool {
+480        use Header::*;
+481        match *self {
+482            Initial(_) => true,
+483            Long { .. } => true,
+484            Retry { .. } => false,
+485            Short { .. } => true,
+486            VersionNegotiate { .. } => false,
+487        }
+488    }
+489
+490    #[cfg(feature = "qlog")]
+491    pub(crate) fn src_cid(&self) -> Option<ConnectionId> {
+492        match self {
+493            Self::Initial(initial_header) => Some(initial_header.src_cid),
+494            Self::Long { src_cid, .. } => Some(*src_cid),
+495            Self::Retry { src_cid, .. } => Some(*src_cid),
+496            Self::Short { .. } => None,
+497            Self::VersionNegotiate { src_cid, .. } => Some(*src_cid),
+498        }
+499    }
+500}
+501
+502pub(crate) struct PartialEncode {
+503    pub(crate) start: usize,
+504    pub(crate) header_len: usize,
+505    // Packet number length, payload length needed
+506    pn: Option<(usize, bool)>,
+507}
+508
+509impl PartialEncode {
+510    pub(crate) fn finish(
+511        self,
+512        buf: &mut [u8],
+513        header_crypto: &dyn crypto::HeaderKey,
+514        crypto: Option<(u64, PathId, &dyn crypto::PacketKey)>,
+515    ) {
+516        let Self { header_len, pn, .. } = self;
+517        let Some((pn_len, write_len)) = pn else {
+518            return;
+519        };
+520
+521        let pn_pos = header_len - pn_len;
+522        if write_len {
+523            let len = buf.len() - header_len + pn_len;
+524            assert!(len < 2usize.pow(14)); // Fits in reserved space
+525            let mut slice = &mut buf[pn_pos - 2..pn_pos];
+526            slice.put_u16(len as u16 | (0b01 << 14));
+527        }
+528
+529        if let Some((packet_number, path_id, crypto)) = crypto {
+530            crypto.encrypt(path_id, packet_number, buf, header_len);
+531        }
+532
+533        debug_assert!(
+534            pn_pos + 4 + header_crypto.sample_size() <= buf.len(),
+535            "packet must be padded to at least {} bytes for header protection sampling",
+536            pn_pos + 4 + header_crypto.sample_size()
+537        );
+538        header_crypto.encrypt(pn_pos, buf);
+539    }
+540
+541    /// Creates a [`PartialEncode`] that has not encoded a header into the buffer.
+542    ///
+543    /// This is used exclusively for testing as such a type is otherwise invalid.
+544    #[cfg(test)]
+545    pub(crate) fn no_header() -> Self {
+546        Self {
+547            start: 0,
+548            header_len: 0,
+549            pn: None,
+550        }
+551    }
+552}
+553
+554/// Plain packet header
+555#[derive(Clone, Debug)]
+556pub enum ProtectedHeader {
+557    /// An Initial packet header
+558    Initial(ProtectedInitialHeader),
+559    /// A Long packet header, as used during the handshake
+560    Long {
+561        /// Type of the Long header packet
+562        ty: LongType,
+563        /// Destination Connection ID
+564        dst_cid: ConnectionId,
+565        /// Source Connection ID
+566        src_cid: ConnectionId,
+567        /// Length of the packet payload
+568        len: u64,
+569        /// QUIC version
+570        version: u32,
+571    },
+572    /// A Retry packet header
+573    Retry {
+574        /// Destination Connection ID
+575        dst_cid: ConnectionId,
+576        /// Source Connection ID
+577        src_cid: ConnectionId,
+578        /// QUIC version
+579        version: u32,
+580    },
+581    /// A short packet header, as used during the data phase
+582    Short {
+583        /// Spin bit
+584        spin: bool,
+585        /// Destination Connection ID
+586        dst_cid: ConnectionId,
+587    },
+588    /// A Version Negotiation packet header
+589    VersionNegotiate {
+590        /// Random value
+591        random: u8,
+592        /// Destination Connection ID
+593        dst_cid: ConnectionId,
+594        /// Source Connection ID
+595        src_cid: ConnectionId,
+596    },
+597}
+598
+599impl ProtectedHeader {
+600    fn as_initial(&self) -> Option<&ProtectedInitialHeader> {
+601        match self {
+602            Self::Initial(x) => Some(x),
+603            _ => None,
+604        }
+605    }
+606
+607    /// The destination Connection ID of the packet
+608    pub fn dst_cid(&self) -> ConnectionId {
+609        use ProtectedHeader::*;
+610        match self {
+611            Initial(header) => header.dst_cid,
+612            &Long { dst_cid, .. } => dst_cid,
+613            &Retry { dst_cid, .. } => dst_cid,
+614            &Short { dst_cid, .. } => dst_cid,
+615            &VersionNegotiate { dst_cid, .. } => dst_cid,
+616        }
+617    }
+618
+619    fn payload_len(&self) -> Option<u64> {
+620        use ProtectedHeader::*;
+621        match self {
+622            Initial(ProtectedInitialHeader { len, .. }) | Long { len, .. } => Some(*len),
+623            _ => None,
+624        }
+625    }
+626
+627    /// Decode a plain header from given buffer, with given [`ConnectionIdParser`].
+628    pub fn decode(
+629        buf: &mut io::Cursor<BytesMut>,
+630        cid_parser: &(impl ConnectionIdParser + ?Sized),
+631        supported_versions: &[u32],
+632        grease_quic_bit: bool,
+633    ) -> Result<Self, PacketDecodeError> {
+634        let first = buf.get::<u8>()?;
+635        if !grease_quic_bit && first & FIXED_BIT == 0 {
+636            return Err(PacketDecodeError::InvalidHeader("fixed bit unset"));
+637        }
+638        if first & LONG_HEADER_FORM == 0 {
+639            let spin = first & SPIN_BIT != 0;
+640
+641            Ok(Self::Short {
+642                spin,
+643                dst_cid: cid_parser.parse(buf)?,
+644            })
+645        } else {
+646            let version = buf.get::<u32>()?;
+647
+648            let dst_cid = ConnectionId::decode_long(buf)
+649                .ok_or(PacketDecodeError::InvalidHeader("malformed cid"))?;
+650            let src_cid = ConnectionId::decode_long(buf)
+651                .ok_or(PacketDecodeError::InvalidHeader("malformed cid"))?;
+652
+653            // TODO: Support long CIDs for compatibility with future QUIC versions
+654            if version == 0 {
+655                let random = first & !LONG_HEADER_FORM;
+656                return Ok(Self::VersionNegotiate {
+657                    random,
+658                    dst_cid,
+659                    src_cid,
+660                });
+661            }
+662
+663            if !supported_versions.contains(&version) {
+664                return Err(PacketDecodeError::UnsupportedVersion {
+665                    src_cid,
+666                    dst_cid,
+667                    version,
+668                });
+669            }
+670
+671            match LongHeaderType::from_byte(first)? {
+672                LongHeaderType::Initial => {
+673                    let token_len = buf.get_var()? as usize;
+674                    let token_start = buf.position() as usize;
+675                    if token_len > buf.remaining() {
+676                        return Err(PacketDecodeError::InvalidHeader("token out of bounds"));
+677                    }
+678                    buf.advance(token_len);
+679
+680                    let len = buf.get_var()?;
+681                    Ok(Self::Initial(ProtectedInitialHeader {
+682                        dst_cid,
+683                        src_cid,
+684                        token_pos: token_start..token_start + token_len,
+685                        len,
+686                        version,
+687                    }))
+688                }
+689                LongHeaderType::Retry => Ok(Self::Retry {
+690                    dst_cid,
+691                    src_cid,
+692                    version,
+693                }),
+694                LongHeaderType::Standard(ty) => Ok(Self::Long {
+695                    ty,
+696                    dst_cid,
+697                    src_cid,
+698                    len: buf.get_var()?,
+699                    version,
+700                }),
+701            }
+702        }
+703    }
+704}
+705
+706/// Header of an Initial packet, before decryption
+707#[derive(Clone, Debug)]
+708pub struct ProtectedInitialHeader {
+709    /// Destination Connection ID
+710    pub dst_cid: ConnectionId,
+711    /// Source Connection ID
+712    pub src_cid: ConnectionId,
+713    /// The position of a token in the packet buffer
+714    pub token_pos: Range<usize>,
+715    /// Length of the packet payload
+716    pub len: u64,
+717    /// QUIC version
+718    pub version: u32,
+719}
+720
+721#[derive(Clone, Debug)]
+722pub(crate) struct InitialHeader {
+723    pub(crate) dst_cid: ConnectionId,
+724    pub(crate) src_cid: ConnectionId,
+725    pub(crate) token: Bytes,
+726    pub(crate) number: PacketNumber,
+727    pub(crate) version: u32,
+728}
+729
+730// An encoded packet number
+731#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+732pub(crate) enum PacketNumber {
+733    U8(u8),
+734    U16(u16),
+735    U24(u32),
+736    U32(u32),
+737}
+738
+739impl PacketNumber {
+740    pub(crate) fn new(n: u64, largest_acked: u64) -> Self {
+741        let range = (n - largest_acked) * 2;
+742        if range < 1 << 8 {
+743            Self::U8(n as u8)
+744        } else if range < 1 << 16 {
+745            Self::U16(n as u16)
+746        } else if range < 1 << 24 {
+747            Self::U24(n as u32)
+748        } else if range < 1 << 32 {
+749            Self::U32(n as u32)
+750        } else {
+751            panic!("packet number too large to encode")
+752        }
+753    }
+754
+755    pub(crate) fn len(self) -> usize {
+756        use PacketNumber::*;
+757        match self {
+758            U8(_) => 1,
+759            U16(_) => 2,
+760            U24(_) => 3,
+761            U32(_) => 4,
+762        }
+763    }
+764
+765    pub(crate) fn encode<W: BufMut>(self, w: &mut W) {
+766        use PacketNumber::*;
+767        match self {
+768            U8(x) => w.write(x),
+769            U16(x) => w.write(x),
+770            U24(x) => w.put_uint(u64::from(x), 3),
+771            U32(x) => w.write(x),
+772        }
+773    }
+774
+775    pub(crate) fn decode<R: Buf>(len: usize, r: &mut R) -> Result<Self, PacketDecodeError> {
+776        use PacketNumber::*;
+777        let pn = match len {
+778            1 => U8(r.get()?),
+779            2 => U16(r.get()?),
+780            3 => U24(r.get_uint(3) as u32),
+781            4 => U32(r.get()?),
+782            _ => unreachable!(),
+783        };
+784        Ok(pn)
+785    }
+786
+787    pub(crate) fn decode_len(tag: u8) -> usize {
+788        1 + (tag & 0x03) as usize
+789    }
+790
+791    fn tag(self) -> u8 {
+792        use PacketNumber::*;
+793        match self {
+794            U8(_) => 0b00,
+795            U16(_) => 0b01,
+796            U24(_) => 0b10,
+797            U32(_) => 0b11,
+798        }
+799    }
+800
+801    pub(crate) fn expand(self, expected: u64) -> u64 {
+802        // From Appendix A
+803        use PacketNumber::*;
+804        let truncated = match self {
+805            U8(x) => u64::from(x),
+806            U16(x) => u64::from(x),
+807            U24(x) => u64::from(x),
+808            U32(x) => u64::from(x),
+809        };
+810        let nbits = self.len() * 8;
+811        let win = 1 << nbits;
+812        let hwin = win / 2;
+813        let mask = win - 1;
+814        // The incoming packet number should be greater than expected - hwin and less than or equal
+815        // to expected + hwin
+816        //
+817        // This means we can't just strip the trailing bits from expected and add the truncated
+818        // because that might yield a value outside the window.
+819        //
+820        // The following code calculates a candidate value and makes sure it's within the packet
+821        // number window.
+822        let candidate = (expected & !mask) | truncated;
+823        if expected.checked_sub(hwin).is_some_and(|x| candidate <= x) {
+824            candidate + win
+825        } else if candidate > expected + hwin && candidate > win {
+826            candidate - win
+827        } else {
+828            candidate
+829        }
+830    }
+831}
+832
+833/// A [`ConnectionIdParser`] implementation that assumes the connection ID is of fixed length
+834pub struct FixedLengthConnectionIdParser {
+835    expected_len: usize,
+836}
+837
+838impl FixedLengthConnectionIdParser {
+839    /// Create a new instance of `FixedLengthConnectionIdParser`
+840    pub fn new(expected_len: usize) -> Self {
+841        Self { expected_len }
+842    }
+843}
+844
+845impl ConnectionIdParser for FixedLengthConnectionIdParser {
+846    fn parse(&self, buffer: &mut dyn Buf) -> Result<ConnectionId, PacketDecodeError> {
+847        (buffer.remaining() >= self.expected_len)
+848            .then(|| ConnectionId::from_buf(buffer, self.expected_len))
+849            .ok_or(PacketDecodeError::InvalidHeader("packet too small"))
+850    }
+851}
+852
+853/// Parse connection id in short header packet
+854pub trait ConnectionIdParser {
+855    /// Parse a connection id from given buffer
+856    fn parse(&self, buf: &mut dyn Buf) -> Result<ConnectionId, PacketDecodeError>;
+857}
+858
+859/// Long packet type including non-uniform cases
+860#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+861pub(crate) enum LongHeaderType {
+862    Initial,
+863    Retry,
+864    Standard(LongType),
+865}
+866
+867impl LongHeaderType {
+868    fn from_byte(b: u8) -> Result<Self, PacketDecodeError> {
+869        use {LongHeaderType::*, LongType::*};
+870        debug_assert!(b & LONG_HEADER_FORM != 0, "not a long packet");
+871        Ok(match (b & 0x30) >> 4 {
+872            0x0 => Initial,
+873            0x1 => Standard(ZeroRtt),
+874            0x2 => Standard(Handshake),
+875            0x3 => Retry,
+876            _ => unreachable!(),
+877        })
+878    }
+879}
+880
+881impl From<LongHeaderType> for u8 {
+882    fn from(ty: LongHeaderType) -> Self {
+883        use {LongHeaderType::*, LongType::*};
+884        match ty {
+885            Initial => LONG_HEADER_FORM | FIXED_BIT,
+886            Standard(ZeroRtt) => LONG_HEADER_FORM | FIXED_BIT | (0x1 << 4),
+887            Standard(Handshake) => LONG_HEADER_FORM | FIXED_BIT | (0x2 << 4),
+888            Retry => LONG_HEADER_FORM | FIXED_BIT | (0x3 << 4),
+889        }
+890    }
+891}
+892
+893/// Long packet types with uniform header structure
+894#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+895pub enum LongType {
+896    /// Handshake packet
+897    Handshake,
+898    /// 0-RTT packet
+899    ZeroRtt,
+900}
+901
+902/// Packet decode error
+903#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+904pub enum PacketDecodeError {
+905    /// Packet uses a QUIC version that is not supported
+906    #[error("unsupported version {version:x}")]
+907    UnsupportedVersion {
+908        /// Source Connection ID
+909        src_cid: ConnectionId,
+910        /// Destination Connection ID
+911        dst_cid: ConnectionId,
+912        /// The version that was unsupported
+913        version: u32,
+914    },
+915    /// The packet header is invalid
+916    #[error("invalid header: {0}")]
+917    InvalidHeader(&'static str),
+918}
+919
+920impl From<coding::UnexpectedEnd> for PacketDecodeError {
+921    fn from(_: coding::UnexpectedEnd) -> Self {
+922        Self::InvalidHeader("unexpected end of packet")
+923    }
+924}
+925
+926pub(crate) const LONG_HEADER_FORM: u8 = 0x80;
+927pub(crate) const FIXED_BIT: u8 = 0x40;
+928pub(crate) const SPIN_BIT: u8 = 0x20;
+929const SHORT_RESERVED_BITS: u8 = 0x18;
+930const LONG_RESERVED_BITS: u8 = 0x0c;
+931const KEY_PHASE_BIT: u8 = 0x04;
+932
+933/// Packet number space identifiers
+934#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+935pub(crate) enum SpaceId {
+936    /// Unprotected packets, used to bootstrap the handshake
+937    Initial = 0,
+938    Handshake = 1,
+939    /// Application data space, used for 0-RTT and post-handshake/1-RTT packets
+940    Data = 2,
+941}
+942
+943impl SpaceId {
+944    pub(crate) fn iter() -> impl Iterator<Item = Self> {
+945        [Self::Initial, Self::Handshake, Self::Data].iter().cloned()
+946    }
+947
+948    /// Returns the next higher packet space.
+949    ///
+950    /// Returns `None` if at  [`SpaceId::Data`].
+951    pub(crate) fn next(&self) -> Option<Self> {
+952        match self {
+953            Self::Initial => Some(Self::Handshake),
+954            Self::Handshake => Some(Self::Data),
+955            Self::Data => None,
+956        }
+957    }
+958
+959    /// Returns the encryption level for this packet space.
+960    pub(crate) fn encryption_level(self) -> EncryptionLevel {
+961        match self {
+962            Self::Initial => EncryptionLevel::Initial,
+963            Self::Handshake => EncryptionLevel::Handshake,
+964            Self::Data => EncryptionLevel::OneRtt,
+965        }
+966    }
+967
+968    /// Returns the [`SpaceKind`] for this packet space.
+969    pub(crate) fn kind(self) -> SpaceKind {
+970        match self {
+971            Self::Initial => SpaceKind::Initial,
+972            Self::Handshake => SpaceKind::Handshake,
+973            Self::Data => SpaceKind::Data,
+974        }
+975    }
+976}
+977
+978#[cfg(test)]
+979mod tests {
+980    use super::*;
+981    use hex_literal::hex;
+982    use std::io;
+983
+984    fn check_pn(typed: PacketNumber, encoded: &[u8]) {
+985        let mut buf = Vec::new();
+986        typed.encode(&mut buf);
+987        assert_eq!(&buf[..], encoded);
+988        let decoded = PacketNumber::decode(typed.len(), &mut io::Cursor::new(&buf)).unwrap();
+989        assert_eq!(typed, decoded);
+990    }
+991
+992    #[test]
+993    fn roundtrip_packet_numbers() {
+994        check_pn(PacketNumber::U8(0x7f), &hex!("7f"));
+995        check_pn(PacketNumber::U16(0x80), &hex!("0080"));
+996        check_pn(PacketNumber::U16(0x3fff), &hex!("3fff"));
+997        check_pn(PacketNumber::U32(0x0000_4000), &hex!("0000 4000"));
+998        check_pn(PacketNumber::U32(0xffff_ffff), &hex!("ffff ffff"));
+999    }
+1000
+1001    #[test]
+1002    fn pn_encode() {
+1003        check_pn(PacketNumber::new(0x10, 0), &hex!("10"));
+1004        check_pn(PacketNumber::new(0x100, 0), &hex!("0100"));
+1005        check_pn(PacketNumber::new(0x10000, 0), &hex!("010000"));
+1006    }
+1007
+1008    #[test]
+1009    fn pn_expand_roundtrip() {
+1010        for expected in 0..1024 {
+1011            for actual in expected..1024 {
+1012                assert_eq!(actual, PacketNumber::new(actual, expected).expand(expected));
+1013            }
+1014        }
+1015    }
+1016
+1017    #[cfg(all(feature = "rustls", any(feature = "aws-lc-rs", feature = "ring")))]
+1018    #[test]
+1019    fn header_encoding() {
+1020        use crate::Side;
+1021        use crate::crypto::rustls::{configured_provider, initial_keys, initial_suite_from_provider};
+1022        use rustls::quic::Version;
+1023
+1024        let dcid = ConnectionId::new(&hex!("06b858ec6f80452b"));
+1025        let provider = configured_provider();
+1026
+1027        let suite = initial_suite_from_provider(&provider).unwrap();
+1028        let client = initial_keys(Version::V1, dcid, Side::Client, &suite);
+1029        let mut buf = Vec::new();
+1030        let header = Header::Initial(InitialHeader {
+1031            number: PacketNumber::U8(0),
+1032            src_cid: ConnectionId::new(&[]),
+1033            dst_cid: dcid,
+1034            token: Bytes::new(),
+1035            version: crate::DEFAULT_SUPPORTED_VERSIONS[0],
+1036        });
+1037        let encode = header.encode(&mut buf);
+1038        let header_len = buf.len();
+1039        buf.resize(header_len + 16 + client.packet.local.tag_len(), 0);
+1040        encode.finish(
+1041            &mut buf,
+1042            &*client.header.local,
+1043            Some((0, PathId::ZERO, &*client.packet.local)),
+1044        );
+1045
+1046        for byte in &buf {
+1047            print!("{byte:02x}");
+1048        }
+1049        println!();
+1050        assert_eq!(
+1051            buf[..],
+1052            hex!(
+1053                "c8000000010806b858ec6f80452b00004021be
+1054                 3ef50807b84191a196f760a6dad1e9d1c430c48952cba0148250c21c0a6a70e1"
+1055            )[..]
+1056        );
+1057
+1058        let server = initial_keys(Version::V1, dcid, Side::Server, &suite);
+1059        let supported_versions = crate::DEFAULT_SUPPORTED_VERSIONS.to_vec();
+1060        let decode = PartialDecode::new(
+1061            buf.as_slice().into(),
+1062            &FixedLengthConnectionIdParser::new(0),
+1063            &supported_versions,
+1064            false,
+1065        )
+1066        .unwrap()
+1067        .0;
+1068        let mut packet = decode.finish(Some(&*server.header.remote)).unwrap();
+1069        assert_eq!(
+1070            packet.header_data[..],
+1071            hex!("c0000000010806b858ec6f80452b0000402100")[..]
+1072        );
+1073        server
+1074            .packet
+1075            .remote
+1076            .decrypt(PathId::ZERO, 0, &packet.header_data, &mut packet.payload)
+1077            .unwrap();
+1078        assert_eq!(packet.payload[..], [0; 16]);
+1079        match packet.header {
+1080            Header::Initial(InitialHeader {
+1081                number: PacketNumber::U8(0),
+1082                ..
+1083            }) => {}
+1084            _ => {
+1085                panic!("unexpected header {:?}", packet.header);
+1086            }
+1087        }
+1088    }
+1089}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/range_set/array_range_set.rs.html b/pr/509/docs/src/noq_proto/range_set/array_range_set.rs.html new file mode 100644 index 000000000..caaa8a824 --- /dev/null +++ b/pr/509/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), 0..8)
+240            .prop_map(|gaps_and_sizes| {
+241                let mut ranges = Self::new();
+242                let mut pos = 0u64;
+243                for (gap, size) in gaps_and_sizes {
+244                    let start = pos + gap;
+245                    let end = start + size;
+246                    ranges.insert(start..end);
+247                    pos = end;
+248                }
+249                ranges
+250            })
+251            .boxed()
+252    }
+253}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/range_set/mod.rs.html b/pr/509/docs/src/noq_proto/range_set/mod.rs.html new file mode 100644 index 000000000..d9544ce15 --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/shared.rs.html b/pr/509/docs/src/noq_proto/shared.rs.html new file mode 100644 index 000000000..b874ff857 --- /dev/null +++ b/pr/509/docs/src/noq_proto/shared.rs.html @@ -0,0 +1,208 @@ +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
+53#[derive(Clone, Debug, Eq, PartialEq)]
+54pub(crate) enum EndpointEventInner {
+55    /// The connection has been drained
+56    Drained,
+57    /// The connection has a new active reset token
+58    ///
+59    /// Whenever the connection switches to a new remote CID issued by the peer, it also
+60    /// switches the matching reset token that can be used to abort this connection. This
+61    /// event provides a new reset token for the active remote CID.
+62    ResetToken(PathId, SocketAddr, ResetToken),
+63    /// Retire the remotely issued reset token for a path, without replacing it with a new one
+64    ///
+65    /// This is like `ResetToken` above, but without replacing the `ResetToken` with a new
+66    /// one. See `ConnectionIndex::connection_reset_tokens`.
+67    RetireResetToken(PathId),
+68    /// The connection needs connection identifiers.
+69    ///
+70    /// The fields are:
+71    /// - The path ID for which the identifiers are needed.
+72    /// - The time when the identifiers were needed, not used to generate the CIDs but sent
+73    ///   back via the [`ConnectionEventInner::NewIdentifiers`] so the connection can track
+74    ///   the lifetime of when it needs to be rotated.  should be rotated.
+75    /// - The number of CIDs needed.
+76    NeedIdentifiers(PathId, Instant, u64),
+77    /// Retire a locally issued CID
+78    ///
+79    /// Stop routing connection ID for this sequence number to the connection
+80    /// When `bool == true`, a new connection ID will be issued to peer
+81    RetireConnectionId(Instant, PathId, u64, bool),
+82}
+83
+84/// Protocol-level identifier for a connection.
+85///
+86/// Mainly useful for identifying this connection's packets on the wire with tools like Wireshark.
+87#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
+88#[cfg_attr(test, derive(test_strategy::Arbitrary))]
+89pub struct ConnectionId {
+90    /// length of CID
+91    #[cfg_attr(test, strategy(1u8..=MAX_CID_SIZE as u8))]
+92    len: u8,
+93    /// CID in byte array
+94    bytes: [u8; MAX_CID_SIZE],
+95}
+96
+97impl ConnectionId {
+98    /// Construct cid from byte array
+99    pub fn new(bytes: &[u8]) -> Self {
+100        debug_assert!(bytes.len() <= MAX_CID_SIZE);
+101        let mut res = Self {
+102            len: bytes.len() as u8,
+103            bytes: [0; MAX_CID_SIZE],
+104        };
+105        res.bytes[..bytes.len()].copy_from_slice(bytes);
+106        res
+107    }
+108
+109    /// Constructs cid by reading `len` bytes from a `Buf`
+110    ///
+111    /// Callers need to assure that `buf.remaining() >= len`
+112    pub fn from_buf(buf: &mut (impl Buf + ?Sized), len: usize) -> Self {
+113        debug_assert!(len <= MAX_CID_SIZE);
+114        let mut res = Self {
+115            len: len as u8,
+116            bytes: [0; MAX_CID_SIZE],
+117        };
+118        buf.copy_to_slice(&mut res[..len]);
+119        res
+120    }
+121
+122    pub(crate) fn len(&self) -> usize {
+123        self.len as usize
+124    }
+125
+126    /// Decode from long header format
+127    pub(crate) fn decode_long(buf: &mut impl Buf) -> Option<Self> {
+128        let len = buf.get::<u8>().ok()? as usize;
+129        match len > MAX_CID_SIZE || buf.remaining() < len {
+130            false => Some(Self::from_buf(buf, len)),
+131            true => None,
+132        }
+133    }
+134
+135    /// Encode in long header format
+136    pub(crate) fn encode_long(&self, buf: &mut impl BufMut) {
+137        buf.put_u8(self.len() as u8);
+138        buf.put_slice(self);
+139    }
+140}
+141
+142impl ::std::ops::Deref for ConnectionId {
+143    type Target = [u8];
+144    fn deref(&self) -> &[u8] {
+145        &self.bytes[0..self.len as usize]
+146    }
+147}
+148
+149impl ::std::ops::DerefMut for ConnectionId {
+150    fn deref_mut(&mut self) -> &mut [u8] {
+151        &mut self.bytes[0..self.len as usize]
+152    }
+153}
+154
+155impl fmt::Debug for ConnectionId {
+156    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+157        self.bytes[0..self.len as usize].fmt(f)
+158    }
+159}
+160
+161impl fmt::Display for ConnectionId {
+162    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+163        for byte in self.iter() {
+164            write!(f, "{byte:02x}")?;
+165        }
+166        Ok(())
+167    }
+168}
+169
+170/// Explicit congestion notification codepoint
+171#[repr(u8)]
+172#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+173pub enum EcnCodepoint {
+174    /// The ECT(0) codepoint, indicating that an endpoint is ECN-capable
+175    Ect0 = 0b10,
+176    /// The ECT(1) codepoint, indicating that an endpoint is ECN-capable
+177    Ect1 = 0b01,
+178    /// The CE codepoint, signalling that congestion was experienced
+179    Ce = 0b11,
+180}
+181
+182impl EcnCodepoint {
+183    /// Create new object from the given bits
+184    pub fn from_bits(x: u8) -> Option<Self> {
+185        use EcnCodepoint::*;
+186        Some(match x & 0b11 {
+187            0b10 => Ect0,
+188            0b01 => Ect1,
+189            0b11 => Ce,
+190            _ => {
+191                return None;
+192            }
+193        })
+194    }
+195
+196    /// Returns whether the codepoint is a CE, signalling that congestion was experienced
+197    pub fn is_ce(self) -> bool {
+198        matches!(self, Self::Ce)
+199    }
+200}
+201
+202#[derive(Debug, Copy, Clone)]
+203pub(crate) struct IssuedCid {
+204    pub(crate) path_id: PathId,
+205    pub(crate) sequence: u64,
+206    pub(crate) id: ConnectionId,
+207    pub(crate) reset_token: ResetToken,
+208}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/token.rs.html b/pr/509/docs/src/noq_proto/token.rs.html new file mode 100644 index 000000000..a1bf11894 --- /dev/null +++ b/pr/509/docs/src/noq_proto/token.rs.html @@ -0,0 +1,491 @@ +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, Rng};
+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 crate::crypto::ring_like::RetryTokenKey;
+407
+408    use super::*;
+409    use rand::prelude::*;
+410
+411    fn token_round_trip(payload: TokenPayload) -> TokenPayload {
+412        let rng = &mut rand::rng();
+413        let token = Token::new(payload, rng);
+414        let master_key = RetryTokenKey::new(rng);
+415        let encoded = token.encode(&master_key);
+416        let decoded = Token::decode(&master_key, &encoded).expect("token didn't decrypt / decode");
+417        assert_eq!(token.nonce, decoded.nonce);
+418        decoded.payload
+419    }
+420
+421    #[test]
+422    fn retry_token_sanity() {
+423        use crate::MAX_CID_SIZE;
+424        use crate::cid_generator::{ConnectionIdGenerator, RandomConnectionIdGenerator};
+425        use crate::{Duration, UNIX_EPOCH};
+426
+427        use std::net::Ipv6Addr;
+428
+429        let address_1 = SocketAddr::new(Ipv6Addr::LOCALHOST.into(), 4433);
+430        let orig_dst_cid_1 = RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid();
+431        let issued_1 = UNIX_EPOCH + Duration::from_secs(42); // Fractional seconds would be lost
+432        let payload_1 = TokenPayload::Retry {
+433            address: address_1,
+434            orig_dst_cid: orig_dst_cid_1,
+435            issued: issued_1,
+436        };
+437        let TokenPayload::Retry {
+438            address: address_2,
+439            orig_dst_cid: orig_dst_cid_2,
+440            issued: issued_2,
+441        } = token_round_trip(payload_1)
+442        else {
+443            panic!("token decoded as wrong variant");
+444        };
+445
+446        assert_eq!(address_1, address_2);
+447        assert_eq!(orig_dst_cid_1, orig_dst_cid_2);
+448        assert_eq!(issued_1, issued_2);
+449    }
+450
+451    #[test]
+452    fn validation_token_sanity() {
+453        use crate::{Duration, UNIX_EPOCH};
+454
+455        use std::net::Ipv6Addr;
+456
+457        let ip_1 = Ipv6Addr::LOCALHOST.into();
+458        let issued_1 = UNIX_EPOCH + Duration::from_secs(42); // Fractional seconds would be lost
+459
+460        let payload_1 = TokenPayload::Validation {
+461            ip: ip_1,
+462            issued: issued_1,
+463        };
+464        let TokenPayload::Validation {
+465            ip: ip_2,
+466            issued: issued_2,
+467        } = token_round_trip(payload_1)
+468        else {
+469            panic!("token decoded as wrong variant");
+470        };
+471
+472        assert_eq!(ip_1, ip_2);
+473        assert_eq!(issued_1, issued_2);
+474    }
+475
+476    #[test]
+477    fn invalid_token_returns_err() {
+478        use super::*;
+479
+480        let master_key = RetryTokenKey::new(&mut rand::rng());
+481
+482        let mut invalid_token = Vec::new();
+483
+484        let mut random_data = [0; 32];
+485        rand::rng().fill_bytes(&mut random_data);
+486        invalid_token.put_slice(&random_data);
+487
+488        // Assert: garbage sealed data returns err
+489        assert!(Token::decode(&master_key, &invalid_token).is_none());
+490    }
+491}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/token_memory_cache.rs.html b/pr/509/docs/src/noq_proto/token_memory_cache.rs.html new file mode 100644 index 000000000..4252015ee --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/transport_error.rs.html b/pr/509/docs/src/noq_proto/transport_error.rs.html new file mode 100644 index 000000000..56c854397 --- /dev/null +++ b/pr/509/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/509/docs/src/noq_proto/transport_parameters.rs.html b/pr/509/docs/src/noq_proto/transport_parameters.rs.html new file mode 100644 index 000000000..8d5169b51 --- /dev/null +++ b/pr/509/docs/src/noq_proto/transport_parameters.rs.html @@ -0,0 +1,977 @@ +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 as _, RngCore, seq::SliceRandom as _};
+17use thiserror::Error;
+18
+19use crate::{
+20    LOCAL_CID_COUNT, MAX_CID_SIZE, MAX_STREAM_COUNT, RESET_TOKEN_SIZE, ResetToken, Side,
+21    TIMER_GRANULARITY, TransportError, VarInt, address_discovery,
+22    cid_generator::ConnectionIdGenerator,
+23    cid_queue::CidQueue,
+24    coding::{BufExt, BufMutExt, UnexpectedEnd},
+25    config::{EndpointConfig, ServerConfig, TransportConfig},
+26    connection::PathId,
+27    shared::ConnectionId,
+28};
+29
+30// Apply a given macro to a list of all the transport parameters having integer types, along with
+31// their codes and default values. Using this helps us avoid error-prone duplication of the
+32// contained information across decoding, encoding, and the `Default` impl. Whenever we want to do
+33// something with transport parameters, we'll handle the bulk of cases by writing a macro that
+34// takes a list of arguments in this form, then passing it to this macro.
+35macro_rules! apply_params {
+36    ($macro:ident) => {
+37        $macro! {
+38            // #[doc] name (id) = default,
+39            /// Milliseconds, disabled if zero
+40            max_idle_timeout(MaxIdleTimeout) = 0,
+41            /// Limits the size of UDP payloads that the endpoint is willing to receive
+42            max_udp_payload_size(MaxUdpPayloadSize) = 65527,
+43
+44            /// Initial value for the maximum amount of data that can be sent on the connection
+45            initial_max_data(InitialMaxData) = 0,
+46            /// Initial flow control limit for locally-initiated bidirectional streams
+47            initial_max_stream_data_bidi_local(InitialMaxStreamDataBidiLocal) = 0,
+48            /// Initial flow control limit for peer-initiated bidirectional streams
+49            initial_max_stream_data_bidi_remote(InitialMaxStreamDataBidiRemote) = 0,
+50            /// Initial flow control limit for unidirectional streams
+51            initial_max_stream_data_uni(InitialMaxStreamDataUni) = 0,
+52
+53            /// Initial maximum number of bidirectional streams the peer may initiate
+54            initial_max_streams_bidi(InitialMaxStreamsBidi) = 0,
+55            /// Initial maximum number of unidirectional streams the peer may initiate
+56            initial_max_streams_uni(InitialMaxStreamsUni) = 0,
+57
+58            /// Exponent used to decode the ACK Delay field in the ACK frame
+59            ack_delay_exponent(AckDelayExponent) = 3,
+60            /// Maximum amount of time in milliseconds by which the endpoint will delay sending
+61            /// acknowledgments
+62            max_ack_delay(MaxAckDelay) = 25,
+63            /// Maximum number of connection IDs from the peer that an endpoint is willing to store
+64            active_connection_id_limit(ActiveConnectionIdLimit) = 2,
+65        }
+66    };
+67}
+68
+69macro_rules! make_struct {
+70    {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
+71        /// Transport parameters used to negotiate connection-level preferences between peers
+72        #[derive(Debug, Copy, Clone, Eq, PartialEq)]
+73        pub struct TransportParameters {
+74            $($(#[$doc])* pub(crate) $name : VarInt,)*
+75
+76            /// Does the endpoint support active connection migration
+77            pub(crate) disable_active_migration: bool,
+78            /// Maximum size for datagram frames
+79            pub(crate) max_datagram_frame_size: Option<VarInt>,
+80            /// The value that the endpoint included in the Source Connection ID field of the first
+81            /// Initial packet it sends for the connection
+82            pub(crate) initial_src_cid: Option<ConnectionId>,
+83            /// The endpoint is willing to receive QUIC packets containing any value for the fixed
+84            /// bit
+85            pub(crate) grease_quic_bit: bool,
+86
+87            /// Minimum amount of time in microseconds by which the endpoint is able to delay
+88            /// sending acknowledgments
+89            ///
+90            /// If a value is provided, it implies that the endpoint supports QUIC Acknowledgement
+91            /// Frequency
+92            pub(crate) min_ack_delay: Option<VarInt>,
+93
+94            // Server-only
+95            /// The value of the Destination Connection ID field from the first Initial packet sent
+96            /// by the client
+97            pub(crate) original_dst_cid: Option<ConnectionId>,
+98            /// The value that the server included in the Source Connection ID field of a Retry
+99            /// packet
+100            pub(crate) retry_src_cid: Option<ConnectionId>,
+101            /// Token used by the client to verify a stateless reset from the server
+102            pub(crate) stateless_reset_token: Option<ResetToken>,
+103            /// The server's preferred address for communication after handshake completion
+104            pub(crate) preferred_address: Option<PreferredAddress>,
+105
+106            /// The randomly generated reserved transport parameter to sustain future extensibility
+107            /// of transport parameter extensions.
+108            /// When present, it is included during serialization but ignored during deserialization.
+109            pub(crate) grease_transport_parameter: Option<ReservedTransportParameter>,
+110
+111            /// Defines the order in which transport parameters are serialized.
+112            ///
+113            /// This field is initialized only for outgoing `TransportParameters` instances and
+114            /// is set to `None` for `TransportParameters` received from a peer.
+115            pub(crate) write_order: Option<[u8; TransportParameterId::SUPPORTED.len()]>,
+116
+117            /// The role of this peer in address discovery, if any.
+118            pub(crate) address_discovery_role: address_discovery::Role,
+119
+120            /// Multipath extension
+121            pub(crate) initial_max_path_id: Option<PathId>,
+122
+123            /// Nat traversal draft
+124            pub max_remote_nat_traversal_addresses: Option<NonZeroU8>,
+125        }
+126
+127        // We deliberately don't implement the `Default` trait, since that would be public, and
+128        // downstream crates should never construct `TransportParameters` except by decoding those
+129        // supplied by a peer.
+130        impl TransportParameters {
+131            /// Standard defaults, used if the peer does not supply a given parameter.
+132            pub(crate) fn default() -> Self {
+133                Self {
+134                    $($name: VarInt::from_u32($default),)*
+135
+136                    disable_active_migration: false,
+137                    max_datagram_frame_size: None,
+138                    initial_src_cid: None,
+139                    grease_quic_bit: false,
+140                    min_ack_delay: None,
+141
+142                    original_dst_cid: None,
+143                    retry_src_cid: None,
+144                    stateless_reset_token: None,
+145                    preferred_address: None,
+146                    grease_transport_parameter: None,
+147                    write_order: None,
+148                    address_discovery_role: address_discovery::Role::Disabled,
+149                    initial_max_path_id: None,
+150                    max_remote_nat_traversal_addresses: None,
+151                }
+152            }
+153        }
+154    }
+155}
+156
+157apply_params!(make_struct);
+158
+159impl TransportParameters {
+160    pub(crate) fn new(
+161        config: &TransportConfig,
+162        endpoint_config: &EndpointConfig,
+163        cid_gen: &dyn ConnectionIdGenerator,
+164        initial_src_cid: ConnectionId,
+165        server_config: Option<&ServerConfig>,
+166        rng: &mut impl RngCore,
+167    ) -> Self {
+168        Self {
+169            initial_src_cid: Some(initial_src_cid),
+170            initial_max_streams_bidi: config.max_concurrent_bidi_streams,
+171            initial_max_streams_uni: config.max_concurrent_uni_streams,
+172            initial_max_data: config.receive_window,
+173            initial_max_stream_data_bidi_local: config.stream_receive_window,
+174            initial_max_stream_data_bidi_remote: config.stream_receive_window,
+175            initial_max_stream_data_uni: config.stream_receive_window,
+176            max_udp_payload_size: endpoint_config.max_udp_payload_size,
+177            max_idle_timeout: config.max_idle_timeout.unwrap_or(VarInt(0)),
+178            disable_active_migration: server_config.is_some_and(|c| !c.migration),
+179            active_connection_id_limit: if cid_gen.cid_len() == 0 {
+180                2 // i.e. default, i.e. unsent
+181            } else {
+182                CidQueue::LEN as u32
+183            }
+184            .into(),
+185            max_datagram_frame_size: config
+186                .datagram_receive_buffer_size
+187                .map(|x| (x.min(u16::MAX.into()) as u16).into()),
+188            grease_quic_bit: endpoint_config.grease_quic_bit,
+189            min_ack_delay: Some(
+190                VarInt::from_u64(u64::try_from(TIMER_GRANULARITY.as_micros()).unwrap()).unwrap(),
+191            ),
+192            grease_transport_parameter: Some(ReservedTransportParameter::random(rng)),
+193            write_order: Some({
+194                let mut order = std::array::from_fn(|i| i as u8);
+195                order.shuffle(rng);
+196                order
+197            }),
+198            address_discovery_role: config.address_discovery_role,
+199            initial_max_path_id: config.get_initial_max_path_id(),
+200            max_remote_nat_traversal_addresses: config.max_remote_nat_traversal_addresses,
+201            ..Self::default()
+202        }
+203    }
+204
+205    /// Check that these parameters are legal when resuming from
+206    /// certain cached parameters
+207    pub(crate) fn validate_resumption_from(&self, cached: &Self) -> Result<(), TransportError> {
+208        if cached.active_connection_id_limit > self.active_connection_id_limit
+209            || cached.initial_max_data > self.initial_max_data
+210            || cached.initial_max_stream_data_bidi_local > self.initial_max_stream_data_bidi_local
+211            || cached.initial_max_stream_data_bidi_remote > self.initial_max_stream_data_bidi_remote
+212            || cached.initial_max_stream_data_uni > self.initial_max_stream_data_uni
+213            || cached.initial_max_streams_bidi > self.initial_max_streams_bidi
+214            || cached.initial_max_streams_uni > self.initial_max_streams_uni
+215            || cached.max_datagram_frame_size > self.max_datagram_frame_size
+216            || cached.grease_quic_bit && !self.grease_quic_bit
+217            || cached.address_discovery_role != self.address_discovery_role
+218            || cached.max_remote_nat_traversal_addresses != self.max_remote_nat_traversal_addresses
+219        {
+220            return Err(TransportError::PROTOCOL_VIOLATION(
+221                "0-RTT accepted with incompatible transport parameters",
+222            ));
+223        }
+224        Ok(())
+225    }
+226
+227    /// Maximum number of CIDs to issue to this peer
+228    ///
+229    /// Consider both a) the active_connection_id_limit from the other end; and
+230    /// b) LOCAL_CID_COUNT used locally
+231    pub(crate) fn issue_cids_limit(&self) -> u64 {
+232        self.active_connection_id_limit.0.min(LOCAL_CID_COUNT)
+233    }
+234}
+235
+236/// A server's preferred address
+237///
+238/// This is communicated as a transport parameter during TLS session establishment.
+239#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+240pub(crate) struct PreferredAddress {
+241    pub(crate) address_v4: Option<SocketAddrV4>,
+242    pub(crate) address_v6: Option<SocketAddrV6>,
+243    pub(crate) connection_id: ConnectionId,
+244    pub(crate) stateless_reset_token: ResetToken,
+245}
+246
+247impl PreferredAddress {
+248    fn wire_size(&self) -> u16 {
+249        4 + 2 + 16 + 2 + 1 + self.connection_id.len() as u16 + 16
+250    }
+251
+252    fn write<W: BufMut>(&self, w: &mut W) {
+253        w.write(self.address_v4.map_or(Ipv4Addr::UNSPECIFIED, |x| *x.ip()));
+254        w.write::<u16>(self.address_v4.map_or(0, |x| x.port()));
+255        w.write(self.address_v6.map_or(Ipv6Addr::UNSPECIFIED, |x| *x.ip()));
+256        w.write::<u16>(self.address_v6.map_or(0, |x| x.port()));
+257        w.write::<u8>(self.connection_id.len() as u8);
+258        w.put_slice(&self.connection_id);
+259        w.put_slice(&self.stateless_reset_token);
+260    }
+261
+262    fn read<R: Buf>(r: &mut R) -> Result<Self, Error> {
+263        let ip_v4 = r.get::<Ipv4Addr>()?;
+264        let port_v4 = r.get::<u16>()?;
+265        let ip_v6 = r.get::<Ipv6Addr>()?;
+266        let port_v6 = r.get::<u16>()?;
+267        let cid_len = r.get::<u8>()?;
+268        if r.remaining() < cid_len as usize || cid_len > MAX_CID_SIZE as u8 {
+269            return Err(Error::Malformed);
+270        }
+271        let mut stage = [0; MAX_CID_SIZE];
+272        r.copy_to_slice(&mut stage[0..cid_len as usize]);
+273        let cid = ConnectionId::new(&stage[0..cid_len as usize]);
+274        if r.remaining() < 16 {
+275            return Err(Error::Malformed);
+276        }
+277        let mut token = [0; RESET_TOKEN_SIZE];
+278        r.copy_to_slice(&mut token);
+279        let address_v4 = if ip_v4.is_unspecified() && port_v4 == 0 {
+280            None
+281        } else {
+282            Some(SocketAddrV4::new(ip_v4, port_v4))
+283        };
+284        let address_v6 = if ip_v6.is_unspecified() && port_v6 == 0 {
+285            None
+286        } else {
+287            Some(SocketAddrV6::new(ip_v6, port_v6, 0, 0))
+288        };
+289        if address_v4.is_none() && address_v6.is_none() {
+290            return Err(Error::IllegalValue);
+291        }
+292        Ok(Self {
+293            address_v4,
+294            address_v6,
+295            connection_id: cid,
+296            stateless_reset_token: token.into(),
+297        })
+298    }
+299}
+300
+301/// Errors encountered while decoding `TransportParameters`
+302#[derive(Debug, Copy, Clone, Eq, PartialEq, Error)]
+303pub enum Error {
+304    /// Parameters that are semantically invalid
+305    #[error("parameter had illegal value")]
+306    IllegalValue,
+307    /// Catch-all error for problems while decoding transport parameters
+308    #[error("parameters were malformed")]
+309    Malformed,
+310}
+311
+312impl From<Error> for TransportError {
+313    fn from(e: Error) -> Self {
+314        match e {
+315            Error::IllegalValue => Self::TRANSPORT_PARAMETER_ERROR("illegal value"),
+316            Error::Malformed => Self::TRANSPORT_PARAMETER_ERROR("malformed"),
+317        }
+318    }
+319}
+320
+321impl From<UnexpectedEnd> for Error {
+322    fn from(_: UnexpectedEnd) -> Self {
+323        Self::Malformed
+324    }
+325}
+326
+327impl TransportParameters {
+328    /// Encode `TransportParameters` into buffer
+329    pub fn write<W: BufMut>(&self, w: &mut W) {
+330        let ids = match &self.write_order {
+331            Some(order) => order,
+332            None => &std::array::from_fn(|i| i as u8),
+333        };
+334
+335        for idx in ids {
+336            let id = TransportParameterId::SUPPORTED[*idx as usize];
+337            match id {
+338                TransportParameterId::ReservedTransportParameter => {
+339                    if let Some(param) = self.grease_transport_parameter {
+340                        param.write(w);
+341                    }
+342                }
+343                TransportParameterId::StatelessResetToken => {
+344                    if let Some(ref x) = self.stateless_reset_token {
+345                        w.write_var(id as u64);
+346                        w.write_var(16);
+347                        w.put_slice(x);
+348                    }
+349                }
+350                TransportParameterId::DisableActiveMigration => {
+351                    if self.disable_active_migration {
+352                        w.write_var(id as u64);
+353                        w.write_var(0);
+354                    }
+355                }
+356                TransportParameterId::MaxDatagramFrameSize => {
+357                    if let Some(x) = self.max_datagram_frame_size {
+358                        w.write_var(id as u64);
+359                        w.write_var(x.size() as u64);
+360                        w.write(x);
+361                    }
+362                }
+363                TransportParameterId::PreferredAddress => {
+364                    if let Some(ref x) = self.preferred_address {
+365                        w.write_var(id as u64);
+366                        w.write_var(x.wire_size() as u64);
+367                        x.write(w);
+368                    }
+369                }
+370                TransportParameterId::OriginalDestinationConnectionId => {
+371                    if let Some(ref cid) = self.original_dst_cid {
+372                        w.write_var(id as u64);
+373                        w.write_var(cid.len() as u64);
+374                        w.put_slice(cid);
+375                    }
+376                }
+377                TransportParameterId::InitialSourceConnectionId => {
+378                    if let Some(ref cid) = self.initial_src_cid {
+379                        w.write_var(id as u64);
+380                        w.write_var(cid.len() as u64);
+381                        w.put_slice(cid);
+382                    }
+383                }
+384                TransportParameterId::RetrySourceConnectionId => {
+385                    if let Some(ref cid) = self.retry_src_cid {
+386                        w.write_var(id as u64);
+387                        w.write_var(cid.len() as u64);
+388                        w.put_slice(cid);
+389                    }
+390                }
+391                TransportParameterId::GreaseQuicBit => {
+392                    if self.grease_quic_bit {
+393                        w.write_var(id as u64);
+394                        w.write_var(0);
+395                    }
+396                }
+397                TransportParameterId::MinAckDelayDraft07 => {
+398                    if let Some(x) = self.min_ack_delay {
+399                        w.write_var(id as u64);
+400                        w.write_var(x.size() as u64);
+401                        w.write(x);
+402                    }
+403                }
+404                TransportParameterId::ObservedAddr => {
+405                    if let Some(varint_role) = self.address_discovery_role.as_transport_parameter()
+406                    {
+407                        w.write_var(id as u64);
+408                        w.write_var(varint_role.size() as u64);
+409                        w.write(varint_role);
+410                    }
+411                }
+412                TransportParameterId::InitialMaxPathId => {
+413                    if let Some(val) = self.initial_max_path_id {
+414                        w.write_var(id as u64);
+415                        w.write_var(val.size() as u64);
+416                        w.write(val);
+417                    }
+418                }
+419                TransportParameterId::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 RngCore) -> 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 RngCore) -> 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 RngCore for StepRng {
+890        #[inline]
+891        fn next_u32(&mut self) -> u32 {
+892            self.next_u64() as u32
+893        }
+894
+895        #[inline]
+896        fn next_u64(&mut self) -> u64 {
+897            let res = self.0;
+898            self.0 = self.0.wrapping_add(1);
+899            res
+900        }
+901
+902        #[inline]
+903        fn fill_bytes(&mut self, dst: &mut [u8]) {
+904            let mut left = dst;
+905            while left.len() >= 8 {
+906                let (l, r) = left.split_at_mut(8);
+907                left = r;
+908                l.copy_from_slice(&self.next_u64().to_le_bytes());
+909            }
+910            let n = left.len();
+911            if n > 0 {
+912                left.copy_from_slice(&self.next_u32().to_le_bytes()[..n]);
+913            }
+914        }
+915    }
+916
+917    #[test]
+918    fn reserved_transport_parameter_ignored_when_read() {
+919        let mut buf = Vec::new();
+920        let reserved_parameter = ReservedTransportParameter::random(&mut rand::rng());
+921        assert!(reserved_parameter.payload_len < ReservedTransportParameter::MAX_PAYLOAD_LEN);
+922        assert!(reserved_parameter.id.0 % 31 == 27);
+923
+924        reserved_parameter.write(&mut buf);
+925        assert!(!buf.is_empty());
+926        let read_params = TransportParameters::read(Side::Server, &mut buf.as_slice()).unwrap();
+927        assert_eq!(read_params, TransportParameters::default());
+928    }
+929
+930    #[test]
+931    fn read_semantic_validation() {
+932        #[allow(clippy::type_complexity)]
+933        let illegal_params_builders: Vec<Box<dyn FnMut(&mut TransportParameters)>> = vec![
+934            Box::new(|t| {
+935                // This min_ack_delay is bigger than max_ack_delay!
+936                let min_ack_delay = t.max_ack_delay.0 * 1_000 + 1;
+937                t.min_ack_delay = Some(VarInt::from_u64(min_ack_delay).unwrap())
+938            }),
+939            Box::new(|t| {
+940                // Preferred address can only be sent by senders (and we are reading the transport
+941                // params as a client)
+942                t.preferred_address = Some(PreferredAddress {
+943                    address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
+944                    address_v6: None,
+945                    connection_id: ConnectionId::new(&[]),
+946                    stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
+947                })
+948            }),
+949        ];
+950
+951        for mut builder in illegal_params_builders {
+952            let mut buf = Vec::new();
+953            let mut params = TransportParameters::default();
+954            builder(&mut params);
+955            params.write(&mut buf);
+956
+957            assert_eq!(
+958                TransportParameters::read(Side::Server, &mut buf.as_slice()),
+959                Err(Error::IllegalValue)
+960            );
+961        }
+962    }
+963
+964    #[test]
+965    fn resumption_params_validation() {
+966        let high_limit = TransportParameters {
+967            initial_max_streams_uni: 32u32.into(),
+968            ..TransportParameters::default()
+969        };
+970        let low_limit = TransportParameters {
+971            initial_max_streams_uni: 16u32.into(),
+972            ..TransportParameters::default()
+973        };
+974        high_limit.validate_resumption_from(&low_limit).unwrap();
+975        low_limit.validate_resumption_from(&high_limit).unwrap_err();
+976    }
+977}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_proto/varint.rs.html b/pr/509/docs/src/noq_proto/varint.rs.html new file mode 100644 index 000000000..b53e4d0b6 --- /dev/null +++ b/pr/509/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/509/docs/src/noq_udp/cmsg/mod.rs.html b/pr/509/docs/src/noq_udp/cmsg/mod.rs.html new file mode 100644 index 000000000..937486b08 --- /dev/null +++ b/pr/509/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/509/docs/src/noq_udp/cmsg/unix.rs.html b/pr/509/docs/src/noq_udp/cmsg/unix.rs.html new file mode 100644 index 000000000..6f4276d24 --- /dev/null +++ b/pr/509/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/509/docs/src/noq_udp/lib.rs.html b/pr/509/docs/src/noq_udp/lib.rs.html new file mode 100644 index 000000000..a21874d37 --- /dev/null +++ b/pr/509/docs/src/noq_udp/lib.rs.html @@ -0,0 +1,301 @@ +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    fn effective_segment_size(&self) -> Option<usize> {
+166        match self.segment_size? {
+167            size if size >= self.contents.len() => None,
+168            size => Some(size),
+169        }
+170    }
+171}
+172
+173/// Log at most 1 IO error per minute
+174#[cfg(not(wasm_browser))]
+175const IO_ERROR_LOG_INTERVAL: Duration = std::time::Duration::from_secs(60);
+176
+177/// Logs a warning message when sendmsg fails
+178///
+179/// Logging will only be performed if at least [`IO_ERROR_LOG_INTERVAL`]
+180/// has elapsed since the last error was logged.
+181#[cfg(all(not(wasm_browser), any(feature = "tracing-log", feature = "log")))]
+182fn log_sendmsg_error(
+183    last_send_error: &Mutex<Instant>,
+184    err: impl core::fmt::Debug,
+185    transmit: &Transmit<'_>,
+186) {
+187    let now = Instant::now();
+188    let last_send_error = &mut *last_send_error.lock().expect("poisend lock");
+189    if now.saturating_duration_since(*last_send_error) > IO_ERROR_LOG_INTERVAL {
+190        *last_send_error = now;
+191        log::warn!(
+192            "sendmsg error: {:?}, Transmit: {{ destination: {:?}, src_ip: {:?}, ecn: {:?}, len: {:?}, segment_size: {:?} }}",
+193            err,
+194            transmit.destination,
+195            transmit.src_ip,
+196            transmit.ecn,
+197            transmit.contents.len(),
+198            transmit.segment_size
+199        );
+200    }
+201}
+202
+203// No-op
+204#[cfg(not(any(wasm_browser, feature = "tracing-log", feature = "log")))]
+205fn log_sendmsg_error(_: &Mutex<Instant>, _: impl core::fmt::Debug, _: &Transmit<'_>) {}
+206
+207/// A borrowed UDP socket
+208///
+209/// On Unix, constructible via `From<T: AsFd>`. On Windows, constructible via `From<T:
+210/// AsSocket>`.
+211// Wrapper around socket2 to avoid making it a public dependency and incurring stability risk
+212#[cfg(not(wasm_browser))]
+213pub struct UdpSockRef<'a>(socket2::SockRef<'a>);
+214
+215#[cfg(unix)]
+216impl<'s, S> From<&'s S> for UdpSockRef<'s>
+217where
+218    S: AsFd,
+219{
+220    fn from(socket: &'s S) -> Self {
+221        Self(socket.into())
+222    }
+223}
+224
+225#[cfg(windows)]
+226impl<'s, S> From<&'s S> for UdpSockRef<'s>
+227where
+228    S: AsSocket,
+229{
+230    fn from(socket: &'s S) -> Self {
+231        Self(socket.into())
+232    }
+233}
+234
+235/// Explicit congestion notification codepoint
+236#[repr(u8)]
+237#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+238pub enum EcnCodepoint {
+239    /// The ECT(0) codepoint, indicating that an endpoint is ECN-capable
+240    Ect0 = 0b10,
+241    /// The ECT(1) codepoint, indicating that an endpoint is ECN-capable
+242    Ect1 = 0b01,
+243    /// The CE codepoint, signalling that congestion was experienced
+244    Ce = 0b11,
+245}
+246
+247impl EcnCodepoint {
+248    /// Create new object from the given bits
+249    pub fn from_bits(x: u8) -> Option<Self> {
+250        use EcnCodepoint::*;
+251        Some(match x & 0b11 {
+252            0b10 => Ect0,
+253            0b01 => Ect1,
+254            0b11 => Ce,
+255            _ => {
+256                return None;
+257            }
+258        })
+259    }
+260}
+261
+262#[cfg(all(test, not(posix_minimal)))]
+263mod tests {
+264    use std::net::Ipv4Addr;
+265
+266    use super::*;
+267
+268    #[test]
+269    fn effective_segment_size() {
+270        assert_eq!(
+271            make_transmit(&[0u8; 10], Some(15)).effective_segment_size(),
+272            None,
+273            "segment_size > content_len should yield no effective segment_size"
+274        );
+275        assert_eq!(
+276            make_transmit(&[0u8; 10], Some(10)).effective_segment_size(),
+277            None,
+278            "segment_size == content_len should yield no effective segment_size"
+279        );
+280        assert_eq!(
+281            make_transmit(&[0u8; 10], None).effective_segment_size(),
+282            None,
+283            "no segment_size should yield no effective segment_size"
+284        );
+285        assert_eq!(
+286            make_transmit(&[0u8; 10], Some(5)).effective_segment_size(),
+287            Some(5),
+288            "segment_size < content_len should yield effective segment_size"
+289        );
+290    }
+291
+292    fn make_transmit(contents: &[u8], segment_size: Option<usize>) -> Transmit<'_> {
+293        Transmit {
+294            destination: SocketAddr::from((Ipv4Addr::UNSPECIFIED, 1)),
+295            ecn: None,
+296            contents,
+297            segment_size,
+298            src_ip: None,
+299        }
+300    }
+301}
\ No newline at end of file diff --git a/pr/509/docs/src/noq_udp/unix.rs.html b/pr/509/docs/src/noq_udp/unix.rs.html new file mode 100644 index 000000000..7ac3a2dd6 --- /dev/null +++ b/pr/509/docs/src/noq_udp/unix.rs.html @@ -0,0 +1,1077 @@ +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::unix::io::AsRawFd,
+9    sync::{
+10        Mutex,
+11        atomic::{AtomicBool, AtomicUsize, Ordering},
+12    },
+13    time::Instant,
+14};
+15
+16use socket2::SockRef;
+17
+18use super::{
+19    EcnCodepoint, IO_ERROR_LOG_INTERVAL, RecvMeta, Transmit, UdpSockRef, cmsg, log_sendmsg_error,
+20};
+21
+22// Adapted from https://github.com/apple-oss-distributions/xnu/blob/8d741a5de7ff4191bf97d57b9f54c2f6d4a15585/bsd/sys/socket_private.h
+23#[cfg(apple_fast)]
+24#[repr(C)]
+25#[allow(non_camel_case_types)]
+26pub(crate) struct msghdr_x {
+27    pub msg_name: *mut libc::c_void,
+28    pub msg_namelen: libc::socklen_t,
+29    pub msg_iov: *mut libc::iovec,
+30    pub msg_iovlen: libc::c_int,
+31    pub msg_control: *mut libc::c_void,
+32    pub msg_controllen: libc::socklen_t,
+33    pub msg_flags: libc::c_int,
+34    pub msg_datalen: usize,
+35}
+36
+37#[cfg(apple_fast)]
+38unsafe extern "C" {
+39    fn recvmsg_x(
+40        s: libc::c_int,
+41        msgp: *const msghdr_x,
+42        cnt: libc::c_uint,
+43        flags: libc::c_int,
+44    ) -> isize;
+45
+46    fn sendmsg_x(
+47        s: libc::c_int,
+48        msgp: *const msghdr_x,
+49        cnt: libc::c_uint,
+50        flags: libc::c_int,
+51    ) -> isize;
+52}
+53
+54#[cfg(target_os = "freebsd")]
+55type IpTosTy = libc::c_uchar;
+56#[cfg(not(any(target_os = "freebsd", target_os = "netbsd")))]
+57type IpTosTy = libc::c_int;
+58
+59/// Tokio-compatible UDP socket with some useful specializations.
+60///
+61/// Unlike a standard tokio UDP socket, this allows ECN bits to be read and written on some
+62/// platforms.
+63#[derive(Debug)]
+64pub struct UdpSocketState {
+65    last_send_error: Mutex<Instant>,
+66    max_gso_segments: AtomicUsize,
+67    gro_segments: NonZeroUsize,
+68    may_fragment: bool,
+69
+70    /// True if we have received EINVAL error from `sendmsg` system call at least once.
+71    ///
+72    /// If enabled, we assume that old kernel is used and switch to fallback mode.
+73    /// In particular, we do not use IP_TOS cmsg_type in this case,
+74    /// which is not supported on Linux <3.13 and results in not sending the UDP packet at all.
+75    sendmsg_einval: AtomicBool,
+76}
+77
+78impl UdpSocketState {
+79    pub fn new(sock: UdpSockRef<'_>) -> io::Result<Self> {
+80        let io = sock.0;
+81        let mut cmsg_platform_space = 0;
+82        if cfg!(target_os = "linux")
+83            || cfg!(bsd)
+84            || cfg!(apple)
+85            || cfg!(target_os = "android")
+86            || cfg!(solarish)
+87        {
+88            cmsg_platform_space +=
+89                unsafe { libc::CMSG_SPACE(mem::size_of::<libc::in6_pktinfo>() as _) as usize };
+90        }
+91
+92        assert!(
+93            CMSG_LEN
+94                >= unsafe { libc::CMSG_SPACE(mem::size_of::<libc::c_int>() as _) as usize }
+95                    + cmsg_platform_space
+96        );
+97        assert!(
+98            mem::align_of::<libc::cmsghdr>() <= mem::align_of::<cmsg::Aligned<[u8; 0]>>(),
+99            "control message buffers will be misaligned"
+100        );
+101
+102        io.set_nonblocking(true)?;
+103
+104        let addr = io.local_addr()?;
+105        let is_ipv4 = addr.family() == libc::AF_INET as libc::sa_family_t;
+106
+107        // mac and ios do not support IP_RECVTOS on dual-stack sockets :(
+108        // older macos versions also don't have the flag and will error out if we don't ignore it
+109        #[cfg(not(any(
+110            target_os = "openbsd",
+111            target_os = "netbsd",
+112            target_os = "dragonfly",
+113            solarish
+114        )))]
+115        if (is_ipv4 || !io.only_v6()?)
+116            && let Err(_err) =
+117                set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_RECVTOS, OPTION_ON)
+118        {
+119            crate::log::debug!("Ignoring error setting IP_RECVTOS on socket: {_err:?}");
+120        }
+121
+122        let mut may_fragment = false;
+123        #[cfg(any(target_os = "linux", target_os = "android"))]
+124        {
+125            // opportunistically try to enable GRO. See gro::gro_segments().
+126            let _ = set_socket_option(&*io, libc::SOL_UDP, libc::UDP_GRO, OPTION_ON);
+127
+128            // Forbid IPv4 fragmentation. Set even for IPv6 to account for IPv6 mapped IPv4 addresses.
+129            // Set `may_fragment` to `true` if this option is not supported on the platform.
+130            may_fragment |= !set_socket_option_supported(
+131                &*io,
+132                libc::IPPROTO_IP,
+133                libc::IP_MTU_DISCOVER,
+134                libc::IP_PMTUDISC_PROBE,
+135            )?;
+136
+137            if is_ipv4 {
+138                set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_PKTINFO, OPTION_ON)?;
+139            } else {
+140                // Set `may_fragment` to `true` if this option is not supported on the platform.
+141                may_fragment |= !set_socket_option_supported(
+142                    &*io,
+143                    libc::IPPROTO_IPV6,
+144                    libc::IPV6_MTU_DISCOVER,
+145                    libc::IPV6_PMTUDISC_PROBE,
+146                )?;
+147            }
+148        }
+149        #[cfg(any(target_os = "freebsd", apple))]
+150        {
+151            if is_ipv4 {
+152                // Set `may_fragment` to `true` if this option is not supported on the platform.
+153                may_fragment |= !set_socket_option_supported(
+154                    &*io,
+155                    libc::IPPROTO_IP,
+156                    libc::IP_DONTFRAG,
+157                    OPTION_ON,
+158                )?;
+159            }
+160        }
+161        #[cfg(any(bsd, apple, solarish))]
+162        // IP_RECVDSTADDR == IP_SENDSRCADDR on FreeBSD
+163        // macOS uses only IP_RECVDSTADDR, no IP_SENDSRCADDR on macOS (the same on Solaris)
+164        // macOS also supports IP_PKTINFO
+165        {
+166            if is_ipv4 {
+167                set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, OPTION_ON)?;
+168            }
+169        }
+170
+171        // Options standardized in RFC 3542
+172        if !is_ipv4 {
+173            set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, OPTION_ON)?;
+174            set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_RECVTCLASS, OPTION_ON)?;
+175            // Linux's IP_PMTUDISC_PROBE allows us to operate under interface MTU rather than the
+176            // kernel's path MTU guess, but actually disabling fragmentation requires this too. See
+177            // __ip6_append_data in ip6_output.c.
+178            // Set `may_fragment` to `true` if this option is not supported on the platform.
+179            may_fragment |= !set_socket_option_supported(
+180                &*io,
+181                libc::IPPROTO_IPV6,
+182                libc::IPV6_DONTFRAG,
+183                OPTION_ON,
+184            )?;
+185        }
+186
+187        let now = Instant::now();
+188        Ok(Self {
+189            last_send_error: Mutex::new(now.checked_sub(2 * IO_ERROR_LOG_INTERVAL).unwrap_or(now)),
+190            max_gso_segments: AtomicUsize::new(gso::max_gso_segments()),
+191            gro_segments: gro::gro_segments(),
+192            may_fragment,
+193            sendmsg_einval: AtomicBool::new(false),
+194        })
+195    }
+196
+197    /// Sends a [`Transmit`] on the given socket.
+198    ///
+199    /// This function will only ever return errors of kind [`io::ErrorKind::WouldBlock`].
+200    /// All other errors will be logged and converted to `Ok`.
+201    ///
+202    /// UDP transmission errors are considered non-fatal because higher-level protocols must
+203    /// employ retransmits and timeouts anyway in order to deal with UDP's unreliable nature.
+204    /// Thus, logging is most likely the only thing you can do with these errors.
+205    ///
+206    /// If you would like to handle these errors yourself, use [`UdpSocketState::try_send`]
+207    /// instead.
+208    pub fn send(&self, socket: UdpSockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+209        match send(self, socket.0, transmit) {
+210            Ok(()) => Ok(()),
+211            Err(e) if e.kind() == io::ErrorKind::WouldBlock => Err(e),
+212            // - EMSGSIZE is expected for MTU probes. Future work might be able to avoid
+213            //   these by automatically clamping the MTUD upper bound to the interface MTU.
+214            Err(e) if e.raw_os_error() == Some(libc::EMSGSIZE) => Ok(()),
+215            Err(e) => {
+216                log_sendmsg_error(&self.last_send_error, e, transmit);
+217
+218                Ok(())
+219            }
+220        }
+221    }
+222
+223    /// Sends a [`Transmit`] on the given socket without any additional error handling.
+224    pub fn try_send(&self, socket: UdpSockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+225        send(self, socket.0, transmit)
+226    }
+227
+228    pub fn recv(
+229        &self,
+230        socket: UdpSockRef<'_>,
+231        bufs: &mut [IoSliceMut<'_>],
+232        meta: &mut [RecvMeta],
+233    ) -> io::Result<usize> {
+234        recv(socket.0, bufs, meta)
+235    }
+236
+237    /// The maximum amount of segments which can be transmitted if a platform
+238    /// supports Generic Send Offload (GSO).
+239    ///
+240    /// This is 1 if the platform doesn't support GSO. Subject to change if errors are detected
+241    /// while using GSO.
+242    #[inline]
+243    pub fn max_gso_segments(&self) -> NonZeroUsize {
+244        self.max_gso_segments
+245            .load(Ordering::Relaxed)
+246            .try_into()
+247            .expect("must have non zero GSO segments")
+248    }
+249
+250    /// The number of segments to read when GRO is enabled. Used as a factor to
+251    /// compute the receive buffer size.
+252    ///
+253    /// Returns 1 if the platform doesn't support GRO.
+254    #[inline]
+255    pub fn gro_segments(&self) -> NonZeroUsize {
+256        self.gro_segments
+257    }
+258
+259    /// Resize the send buffer of `socket` to `bytes`
+260    #[inline]
+261    pub fn set_send_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> {
+262        socket.0.set_send_buffer_size(bytes)
+263    }
+264
+265    /// Resize the receive buffer of `socket` to `bytes`
+266    #[inline]
+267    pub fn set_recv_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> {
+268        socket.0.set_recv_buffer_size(bytes)
+269    }
+270
+271    /// Get the size of the `socket` send buffer
+272    #[inline]
+273    pub fn send_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result<usize> {
+274        socket.0.send_buffer_size()
+275    }
+276
+277    /// Get the size of the `socket` receive buffer
+278    #[inline]
+279    pub fn recv_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result<usize> {
+280        socket.0.recv_buffer_size()
+281    }
+282
+283    /// Whether transmitted datagrams might get fragmented by the IP layer
+284    ///
+285    /// Returns `false` on targets which employ e.g. the `IPV6_DONTFRAG` socket option.
+286    #[inline]
+287    pub fn may_fragment(&self) -> bool {
+288        self.may_fragment
+289    }
+290
+291    /// Returns true if we previously got an EINVAL error from `sendmsg` syscall.
+292    fn sendmsg_einval(&self) -> bool {
+293        self.sendmsg_einval.load(Ordering::Relaxed)
+294    }
+295
+296    /// Sets the flag indicating we got EINVAL error from `sendmsg` syscall.
+297    #[cfg(not(any(apple, target_os = "openbsd", target_os = "netbsd")))]
+298    fn set_sendmsg_einval(&self) {
+299        self.sendmsg_einval.store(true, Ordering::Relaxed)
+300    }
+301}
+302
+303#[cfg(not(any(apple, target_os = "openbsd", target_os = "netbsd")))]
+304fn send(
+305    #[allow(unused_variables)] // only used on Linux
+306    state: &UdpSocketState,
+307    io: SockRef<'_>,
+308    transmit: &Transmit<'_>,
+309) -> io::Result<()> {
+310    #[allow(unused_mut)] // only mutable on FreeBSD
+311    let mut encode_src_ip = true;
+312    #[cfg(target_os = "freebsd")]
+313    {
+314        let addr = io.local_addr()?;
+315        let is_ipv4 = addr.family() == libc::AF_INET as libc::sa_family_t;
+316        if is_ipv4 {
+317            if let Some(socket) = addr.as_socket_ipv4() {
+318                encode_src_ip = socket.ip() == &Ipv4Addr::UNSPECIFIED;
+319            }
+320        }
+321    }
+322    let mut msg_hdr: libc::msghdr = unsafe { mem::zeroed() };
+323    let mut iovec: libc::iovec = unsafe { mem::zeroed() };
+324    let mut cmsgs = cmsg::Aligned([0u8; CMSG_LEN]);
+325    let dst_addr = socket2::SockAddr::from(transmit.destination);
+326    prepare_msg(
+327        transmit,
+328        &dst_addr,
+329        &mut msg_hdr,
+330        &mut iovec,
+331        &mut cmsgs,
+332        encode_src_ip,
+333        state.sendmsg_einval(),
+334    );
+335
+336    loop {
+337        let n = unsafe { libc::sendmsg(io.as_raw_fd(), &msg_hdr, 0) };
+338
+339        if n >= 0 {
+340            return Ok(());
+341        }
+342
+343        let e = io::Error::last_os_error();
+344        match e.kind() {
+345            // Retry the transmission
+346            io::ErrorKind::Interrupted => continue,
+347            io::ErrorKind::WouldBlock => return Err(e),
+348            _ => {
+349                // Some network adapters and drivers do not support GSO. Unfortunately, Linux
+350                // offers no easy way for us to detect this short of an EIO or sometimes EINVAL
+351                // when we try to actually send datagrams using it.
+352                #[cfg(any(target_os = "linux", target_os = "android"))]
+353                if let Some(libc::EIO) | Some(libc::EINVAL) = e.raw_os_error() {
+354                    // Prevent new transmits from being scheduled using GSO. Existing GSO transmits
+355                    // may already be in the pipeline, so we need to tolerate additional failures.
+356                    if state.max_gso_segments().get() > 1 {
+357                        crate::log::info!(
+358                            "`libc::sendmsg` failed with {e}; halting segmentation offload"
+359                        );
+360                        state
+361                            .max_gso_segments
+362                            .store(1, std::sync::atomic::Ordering::Relaxed);
+363                    }
+364                }
+365
+366                // Some arguments to `sendmsg` are not supported. Switch to
+367                // fallback mode and retry if we haven't already.
+368                if e.raw_os_error() == Some(libc::EINVAL) && !state.sendmsg_einval() {
+369                    state.set_sendmsg_einval();
+370                    prepare_msg(
+371                        transmit,
+372                        &dst_addr,
+373                        &mut msg_hdr,
+374                        &mut iovec,
+375                        &mut cmsgs,
+376                        encode_src_ip,
+377                        state.sendmsg_einval(),
+378                    );
+379                    continue;
+380                }
+381
+382                return Err(e);
+383            }
+384        }
+385    }
+386}
+387
+388#[cfg(apple_fast)]
+389fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+390    let mut hdrs = unsafe { mem::zeroed::<[msghdr_x; BATCH_SIZE]>() };
+391    let mut iovs = unsafe { mem::zeroed::<[libc::iovec; BATCH_SIZE]>() };
+392    let mut ctrls = [cmsg::Aligned([0u8; CMSG_LEN]); BATCH_SIZE];
+393    let addr = socket2::SockAddr::from(transmit.destination);
+394    let segment_size = transmit.segment_size.unwrap_or(transmit.contents.len());
+395    let mut cnt = 0;
+396    debug_assert!(transmit.contents.len().div_ceil(segment_size) <= BATCH_SIZE);
+397    for (i, chunk) in transmit
+398        .contents
+399        .chunks(segment_size)
+400        .enumerate()
+401        .take(BATCH_SIZE)
+402    {
+403        prepare_msg(
+404            &Transmit {
+405                destination: transmit.destination,
+406                ecn: transmit.ecn,
+407                contents: chunk,
+408                segment_size: Some(chunk.len()),
+409                src_ip: transmit.src_ip,
+410            },
+411            &addr,
+412            &mut hdrs[i],
+413            &mut iovs[i],
+414            &mut ctrls[i],
+415            true,
+416            state.sendmsg_einval(),
+417        );
+418        hdrs[i].msg_datalen = chunk.len();
+419        cnt += 1;
+420    }
+421    loop {
+422        let n = unsafe { sendmsg_x(io.as_raw_fd(), hdrs.as_ptr(), cnt as u32, 0) };
+423
+424        if n >= 0 {
+425            return Ok(());
+426        }
+427
+428        let e = io::Error::last_os_error();
+429        match e.kind() {
+430            // Retry the transmission
+431            io::ErrorKind::Interrupted => continue,
+432            _ => return Err(e),
+433        }
+434    }
+435}
+436
+437#[cfg(any(target_os = "openbsd", target_os = "netbsd", apple_slow))]
+438fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+439    let mut hdr: libc::msghdr = unsafe { mem::zeroed() };
+440    let mut iov: libc::iovec = unsafe { mem::zeroed() };
+441    let mut ctrl = cmsg::Aligned([0u8; CMSG_LEN]);
+442    let addr = socket2::SockAddr::from(transmit.destination);
+443    prepare_msg(
+444        transmit,
+445        &addr,
+446        &mut hdr,
+447        &mut iov,
+448        &mut ctrl,
+449        cfg!(apple) || cfg!(target_os = "openbsd") || cfg!(target_os = "netbsd"),
+450        state.sendmsg_einval(),
+451    );
+452    loop {
+453        let n = unsafe { libc::sendmsg(io.as_raw_fd(), &hdr, 0) };
+454
+455        if n >= 0 {
+456            return Ok(());
+457        }
+458
+459        let e = io::Error::last_os_error();
+460        match e.kind() {
+461            // Retry the transmission
+462            io::ErrorKind::Interrupted => continue,
+463            _ => return Err(e),
+464        }
+465    }
+466}
+467
+468#[cfg(not(any(
+469    apple,
+470    target_os = "openbsd",
+471    target_os = "netbsd",
+472    target_os = "dragonfly",
+473    solarish
+474)))]
+475fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result<usize> {
+476    let mut names = [MaybeUninit::<libc::sockaddr_storage>::uninit(); BATCH_SIZE];
+477    let mut ctrls = [cmsg::Aligned(MaybeUninit::<[u8; CMSG_LEN]>::uninit()); BATCH_SIZE];
+478    let mut hdrs = unsafe { mem::zeroed::<[libc::mmsghdr; BATCH_SIZE]>() };
+479    let max_msg_count = bufs.len().min(BATCH_SIZE);
+480    for i in 0..max_msg_count {
+481        prepare_recv(
+482            &mut bufs[i],
+483            &mut names[i],
+484            &mut ctrls[i],
+485            &mut hdrs[i].msg_hdr,
+486        );
+487    }
+488    let msg_count = loop {
+489        let n = unsafe {
+490            libc::recvmmsg(
+491                io.as_raw_fd(),
+492                hdrs.as_mut_ptr(),
+493                bufs.len().min(BATCH_SIZE) as _,
+494                0,
+495                ptr::null_mut::<libc::timespec>(),
+496            )
+497        };
+498
+499        if n >= 0 {
+500            break n;
+501        }
+502
+503        let e = io::Error::last_os_error();
+504        match e.kind() {
+505            // Retry receiving
+506            io::ErrorKind::Interrupted => continue,
+507            _ => return Err(e),
+508        }
+509    };
+510    for i in 0..(msg_count as usize) {
+511        meta[i] = decode_recv(&names[i], &hdrs[i].msg_hdr, hdrs[i].msg_len as usize)?;
+512    }
+513    Ok(msg_count as usize)
+514}
+515
+516#[cfg(apple_fast)]
+517fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result<usize> {
+518    let mut names = [MaybeUninit::<libc::sockaddr_storage>::uninit(); BATCH_SIZE];
+519    // MacOS 10.15 `recvmsg_x` does not override the `msghdr_x`
+520    // `msg_controllen`. Thus, after the call to `recvmsg_x`, one does not know
+521    // which control messages have been written to. To prevent reading
+522    // uninitialized memory, do not use `MaybeUninit` for `ctrls`, instead
+523    // initialize `ctrls` with `0`s. A control message of all `0`s is
+524    // automatically skipped by `libc::CMSG_NXTHDR`.
+525    let mut ctrls = [cmsg::Aligned([0u8; CMSG_LEN]); BATCH_SIZE];
+526    let mut hdrs = unsafe { mem::zeroed::<[msghdr_x; BATCH_SIZE]>() };
+527    let max_msg_count = bufs.len().min(BATCH_SIZE);
+528    for i in 0..max_msg_count {
+529        prepare_recv(&mut bufs[i], &mut names[i], &mut ctrls[i], &mut hdrs[i]);
+530    }
+531    let msg_count = loop {
+532        let n = unsafe { recvmsg_x(io.as_raw_fd(), hdrs.as_mut_ptr(), max_msg_count as _, 0) };
+533
+534        if n >= 0 {
+535            break n;
+536        }
+537
+538        let e = io::Error::last_os_error();
+539        match e.kind() {
+540            // Retry receiving
+541            io::ErrorKind::Interrupted => continue,
+542            _ => return Err(e),
+543        }
+544    };
+545    for i in 0..(msg_count as usize) {
+546        meta[i] = decode_recv(&names[i], &hdrs[i], hdrs[i].msg_datalen as usize)?;
+547    }
+548    Ok(msg_count as usize)
+549}
+550
+551#[cfg(any(
+552    target_os = "openbsd",
+553    target_os = "netbsd",
+554    target_os = "dragonfly",
+555    solarish,
+556    apple_slow
+557))]
+558fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result<usize> {
+559    let mut name = MaybeUninit::<libc::sockaddr_storage>::uninit();
+560    let mut ctrl = cmsg::Aligned(MaybeUninit::<[u8; CMSG_LEN]>::uninit());
+561    let mut hdr = unsafe { mem::zeroed::<libc::msghdr>() };
+562    prepare_recv(&mut bufs[0], &mut name, &mut ctrl, &mut hdr);
+563    let n = loop {
+564        let n = unsafe { libc::recvmsg(io.as_raw_fd(), &mut hdr, 0) };
+565
+566        if hdr.msg_flags & libc::MSG_TRUNC != 0 {
+567            continue;
+568        }
+569
+570        if n >= 0 {
+571            break n;
+572        }
+573
+574        let e = io::Error::last_os_error();
+575        match e.kind() {
+576            // Retry receiving
+577            io::ErrorKind::Interrupted => continue,
+578            _ => return Err(e),
+579        }
+580    };
+581    meta[0] = decode_recv(&name, &hdr, n as usize)?;
+582    Ok(1)
+583}
+584
+585const CMSG_LEN: usize = 88;
+586
+587fn prepare_msg(
+588    transmit: &Transmit<'_>,
+589    dst_addr: &socket2::SockAddr,
+590    #[cfg(not(apple_fast))] hdr: &mut libc::msghdr,
+591    #[cfg(apple_fast)] hdr: &mut msghdr_x,
+592    iov: &mut libc::iovec,
+593    ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>,
+594    #[allow(unused_variables)] // only used on FreeBSD & macOS
+595    encode_src_ip: bool,
+596    sendmsg_einval: bool,
+597) {
+598    iov.iov_base = transmit.contents.as_ptr() as *const _ as *mut _;
+599    iov.iov_len = transmit.contents.len();
+600
+601    // SAFETY: Casting the pointer to a mutable one is legal,
+602    // as sendmsg is guaranteed to not alter the mutable pointer
+603    // as per the POSIX spec. See the section on the sys/socket.h
+604    // header for details. The type is only mutable in the first
+605    // place because it is reused by recvmsg as well.
+606    let name = dst_addr.as_ptr() as *mut libc::c_void;
+607    let namelen = dst_addr.len();
+608    hdr.msg_name = name as *mut _;
+609    hdr.msg_namelen = namelen;
+610    hdr.msg_iov = iov;
+611    hdr.msg_iovlen = 1;
+612
+613    hdr.msg_control = ctrl.0.as_mut_ptr() as _;
+614    hdr.msg_controllen = CMSG_LEN as _;
+615    let mut encoder = unsafe { cmsg::Encoder::new(hdr) };
+616    let ecn = transmit.ecn.map_or(0, |x| x as libc::c_int);
+617    // True for IPv4 or IPv4-Mapped IPv6
+618    let is_ipv4 = transmit.destination.is_ipv4()
+619        || matches!(transmit.destination.ip(), IpAddr::V6(addr) if addr.to_ipv4_mapped().is_some());
+620    if is_ipv4 {
+621        if !sendmsg_einval {
+622            #[cfg(not(target_os = "netbsd"))]
+623            {
+624                encoder.push(libc::IPPROTO_IP, libc::IP_TOS, ecn as IpTosTy);
+625            }
+626        }
+627    } else {
+628        encoder.push(libc::IPPROTO_IPV6, libc::IPV6_TCLASS, ecn);
+629    }
+630
+631    if let Some(segment_size) = transmit.effective_segment_size() {
+632        gso::set_segment_size(&mut encoder, segment_size as u16);
+633    }
+634
+635    if let Some(ip) = &transmit.src_ip {
+636        match ip {
+637            IpAddr::V4(v4) => {
+638                #[cfg(any(target_os = "linux", target_os = "android"))]
+639                {
+640                    let pktinfo = libc::in_pktinfo {
+641                        ipi_ifindex: 0,
+642                        ipi_spec_dst: libc::in_addr {
+643                            s_addr: u32::from_ne_bytes(v4.octets()),
+644                        },
+645                        ipi_addr: libc::in_addr { s_addr: 0 },
+646                    };
+647                    encoder.push(libc::IPPROTO_IP, libc::IP_PKTINFO, pktinfo);
+648                }
+649                #[cfg(any(bsd, apple, solarish))]
+650                {
+651                    if encode_src_ip {
+652                        let addr = libc::in_addr {
+653                            s_addr: u32::from_ne_bytes(v4.octets()),
+654                        };
+655                        encoder.push(libc::IPPROTO_IP, libc::IP_RECVDSTADDR, addr);
+656                    }
+657                }
+658            }
+659            IpAddr::V6(v6) => {
+660                let pktinfo = libc::in6_pktinfo {
+661                    ipi6_ifindex: 0,
+662                    ipi6_addr: libc::in6_addr {
+663                        s6_addr: v6.octets(),
+664                    },
+665                };
+666                encoder.push(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO, pktinfo);
+667            }
+668        }
+669    }
+670
+671    encoder.finish();
+672}
+673
+674#[cfg(not(apple_fast))]
+675fn prepare_recv(
+676    buf: &mut IoSliceMut<'_>,
+677    name: &mut MaybeUninit<libc::sockaddr_storage>,
+678    ctrl: &mut cmsg::Aligned<MaybeUninit<[u8; CMSG_LEN]>>,
+679    hdr: &mut libc::msghdr,
+680) {
+681    hdr.msg_name = name.as_mut_ptr() as _;
+682    hdr.msg_namelen = mem::size_of::<libc::sockaddr_storage>() as _;
+683    hdr.msg_iov = buf as *mut IoSliceMut<'_> as *mut libc::iovec;
+684    hdr.msg_iovlen = 1;
+685    hdr.msg_control = ctrl.0.as_mut_ptr() as _;
+686    hdr.msg_controllen = CMSG_LEN as _;
+687    hdr.msg_flags = 0;
+688}
+689
+690#[cfg(apple_fast)]
+691fn prepare_recv(
+692    buf: &mut IoSliceMut<'_>,
+693    name: &mut MaybeUninit<libc::sockaddr_storage>,
+694    ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>,
+695    hdr: &mut msghdr_x,
+696) {
+697    hdr.msg_name = name.as_mut_ptr() as _;
+698    hdr.msg_namelen = mem::size_of::<libc::sockaddr_storage>() as _;
+699    hdr.msg_iov = buf as *mut IoSliceMut<'_> as *mut libc::iovec;
+700    hdr.msg_iovlen = 1;
+701    hdr.msg_control = ctrl.0.as_mut_ptr() as _;
+702    hdr.msg_controllen = CMSG_LEN as _;
+703    hdr.msg_flags = 0;
+704    hdr.msg_datalen = buf.len();
+705}
+706
+707fn decode_recv<M: cmsg::MsgHdr<ControlMessage = libc::cmsghdr>>(
+708    name: &MaybeUninit<libc::sockaddr_storage>,
+709    hdr: &M,
+710    len: usize,
+711) -> io::Result<RecvMeta> {
+712    let name = unsafe { name.assume_init() };
+713    let mut ecn_bits = 0;
+714    let mut dst_ip = None;
+715    let mut interface_index = None;
+716    #[allow(unused_mut)] // only mutable on Linux
+717    let mut stride = len;
+718
+719    let cmsg_iter = unsafe { cmsg::Iter::new(hdr) };
+720    for cmsg in cmsg_iter {
+721        match (cmsg.cmsg_level, cmsg.cmsg_type) {
+722            (libc::IPPROTO_IP, libc::IP_TOS) => unsafe {
+723                ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
+724            },
+725            // FreeBSD uses IP_RECVTOS here, and we can be liberal because cmsgs are opt-in.
+726            #[cfg(not(any(
+727                target_os = "openbsd",
+728                target_os = "netbsd",
+729                target_os = "dragonfly",
+730                solarish
+731            )))]
+732            (libc::IPPROTO_IP, libc::IP_RECVTOS) => unsafe {
+733                ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
+734            },
+735            (libc::IPPROTO_IPV6, libc::IPV6_TCLASS) => unsafe {
+736                // Temporary hack around broken macos ABI. Remove once upstream fixes it.
+737                // https://bugreport.apple.com/web/?problemID=48761855
+738                #[allow(clippy::unnecessary_cast)] // cmsg.cmsg_len defined as size_t
+739                if cfg!(apple)
+740                    && cmsg.cmsg_len as usize == libc::CMSG_LEN(mem::size_of::<u8>() as _) as usize
+741                {
+742                    ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
+743                } else {
+744                    ecn_bits = cmsg::decode::<libc::c_int, libc::cmsghdr>(cmsg) as u8;
+745                }
+746            },
+747            #[cfg(any(target_os = "linux", target_os = "android"))]
+748            (libc::IPPROTO_IP, libc::IP_PKTINFO) => {
+749                let pktinfo = unsafe { cmsg::decode::<libc::in_pktinfo, libc::cmsghdr>(cmsg) };
+750                dst_ip = Some(IpAddr::V4(Ipv4Addr::from(
+751                    pktinfo.ipi_addr.s_addr.to_ne_bytes(),
+752                )));
+753                interface_index = Some(pktinfo.ipi_ifindex as u32);
+754            }
+755            #[cfg(any(bsd, apple))]
+756            (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
+757                let in_addr = unsafe { cmsg::decode::<libc::in_addr, libc::cmsghdr>(cmsg) };
+758                dst_ip = Some(IpAddr::V4(Ipv4Addr::from(in_addr.s_addr.to_ne_bytes())));
+759            }
+760            (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => {
+761                let pktinfo = unsafe { cmsg::decode::<libc::in6_pktinfo, libc::cmsghdr>(cmsg) };
+762                dst_ip = Some(IpAddr::V6(Ipv6Addr::from(pktinfo.ipi6_addr.s6_addr)));
+763                interface_index = Some(pktinfo.ipi6_ifindex as u32);
+764            }
+765            #[cfg(any(target_os = "linux", target_os = "android"))]
+766            (libc::SOL_UDP, libc::UDP_GRO) => unsafe {
+767                stride = cmsg::decode::<libc::c_int, libc::cmsghdr>(cmsg) as usize;
+768            },
+769            _ => {}
+770        }
+771    }
+772
+773    let addr = match libc::c_int::from(name.ss_family) {
+774        libc::AF_INET => {
+775            // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in.
+776            let addr: &libc::sockaddr_in =
+777                unsafe { &*(&name as *const _ as *const libc::sockaddr_in) };
+778            SocketAddr::V4(SocketAddrV4::new(
+779                Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes()),
+780                u16::from_be(addr.sin_port),
+781            ))
+782        }
+783        libc::AF_INET6 => {
+784            // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6.
+785            let addr: &libc::sockaddr_in6 =
+786                unsafe { &*(&name as *const _ as *const libc::sockaddr_in6) };
+787            SocketAddr::V6(SocketAddrV6::new(
+788                Ipv6Addr::from(addr.sin6_addr.s6_addr),
+789                u16::from_be(addr.sin6_port),
+790                addr.sin6_flowinfo,
+791                addr.sin6_scope_id,
+792            ))
+793        }
+794        f => {
+795            return Err(io::Error::other(format!(
+796                "expected AF_INET or AF_INET6, got {f} in decode_recv"
+797            )));
+798        }
+799    };
+800
+801    Ok(RecvMeta {
+802        len,
+803        stride,
+804        addr,
+805        ecn: EcnCodepoint::from_bits(ecn_bits),
+806        dst_ip,
+807        interface_index,
+808    })
+809}
+810
+811#[cfg(not(apple_slow))]
+812// Chosen somewhat arbitrarily; might benefit from additional tuning.
+813pub(crate) const BATCH_SIZE: usize = 32;
+814
+815#[cfg(apple_slow)]
+816pub(crate) const BATCH_SIZE: usize = 1;
+817
+818#[cfg(any(target_os = "linux", target_os = "android"))]
+819mod gso {
+820    use super::*;
+821    use std::{ffi::CStr, mem, str::FromStr, sync::OnceLock};
+822
+823    // Support for UDP GSO has been added to linux kernel in version 4.18
+824    // https://github.com/torvalds/linux/commit/cb586c63e3fc5b227c51fd8c4cb40b34d3750645
+825    const SUPPORTED_SINCE: KernelVersion = KernelVersion {
+826        version: 4,
+827        major_revision: 18,
+828    };
+829
+830    /// Checks whether GSO support is available by checking the kernel version followed by setting
+831    /// the UDP_SEGMENT option on a socket
+832    pub(crate) fn max_gso_segments() -> usize {
+833        const GSO_SIZE: libc::c_int = 1500;
+834
+835        if !SUPPORTED_BY_CURRENT_KERNEL.get_or_init(supported_by_current_kernel) {
+836            return 1;
+837        }
+838
+839        let Ok(socket) = std::net::UdpSocket::bind("[::]:0")
+840            .or_else(|_| std::net::UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)))
+841        else {
+842            return 1;
+843        };
+844
+845        // As defined in linux/udp.h
+846        // #define UDP_MAX_SEGMENTS        (1 << 6UL)
+847        match set_socket_option(&socket, libc::SOL_UDP, libc::UDP_SEGMENT, GSO_SIZE) {
+848            Ok(()) => 64,
+849            Err(_e) => {
+850                crate::log::debug!(
+851                    "failed to set `UDP_SEGMENT` socket option ({_e}); setting `max_gso_segments = 1`"
+852                );
+853
+854                1
+855            }
+856        }
+857    }
+858
+859    pub(crate) fn set_segment_size(
+860        encoder: &mut cmsg::Encoder<'_, libc::msghdr>,
+861        segment_size: u16,
+862    ) {
+863        encoder.push(libc::SOL_UDP, libc::UDP_SEGMENT, segment_size);
+864    }
+865
+866    // Avoid calling `supported_by_current_kernel` for each socket by using `OnceLock`.
+867    static SUPPORTED_BY_CURRENT_KERNEL: OnceLock<bool> = OnceLock::new();
+868
+869    fn supported_by_current_kernel() -> bool {
+870        let kernel_version_string = match kernel_version_string() {
+871            Ok(kernel_version_string) => kernel_version_string,
+872            Err(_e) => {
+873                crate::log::warn!("GSO disabled: uname returned {_e}");
+874                return false;
+875            }
+876        };
+877
+878        let Some(kernel_version) = KernelVersion::from_str(&kernel_version_string) else {
+879            crate::log::warn!(
+880                "GSO disabled: failed to parse kernel version ({kernel_version_string})"
+881            );
+882            return false;
+883        };
+884
+885        if kernel_version < SUPPORTED_SINCE {
+886            crate::log::info!("GSO disabled: kernel too old ({kernel_version_string}); need 4.18+",);
+887            return false;
+888        }
+889
+890        true
+891    }
+892
+893    fn kernel_version_string() -> io::Result<String> {
+894        let mut n = unsafe { mem::zeroed() };
+895        let r = unsafe { libc::uname(&mut n) };
+896        if r != 0 {
+897            return Err(io::Error::last_os_error());
+898        }
+899        Ok(unsafe {
+900            CStr::from_ptr(n.release[..].as_ptr())
+901                .to_string_lossy()
+902                .into_owned()
+903        })
+904    }
+905
+906    // https://www.linfo.org/kernel_version_numbering.html
+907    #[derive(Eq, PartialEq, Ord, PartialOrd, Debug)]
+908    struct KernelVersion {
+909        version: u8,
+910        major_revision: u8,
+911    }
+912
+913    impl KernelVersion {
+914        fn from_str(release: &str) -> Option<Self> {
+915            let mut split = release
+916                .split_once('-')
+917                .map(|pair| pair.0)
+918                .unwrap_or(release)
+919                .split('.');
+920
+921            let version = u8::from_str(split.next()?).ok()?;
+922            let major_revision = u8::from_str(split.next()?).ok()?;
+923
+924            Some(Self {
+925                version,
+926                major_revision,
+927            })
+928        }
+929    }
+930
+931    #[cfg(test)]
+932    mod test {
+933        use super::*;
+934
+935        #[test]
+936        fn parse_current_kernel_version_release_string() {
+937            let release = kernel_version_string().unwrap();
+938            KernelVersion::from_str(&release).unwrap();
+939        }
+940
+941        #[test]
+942        fn parse_kernel_version_release_string() {
+943            // These are made up for the test
+944            assert_eq!(
+945                KernelVersion::from_str("4.14"),
+946                Some(KernelVersion {
+947                    version: 4,
+948                    major_revision: 14
+949                })
+950            );
+951            assert_eq!(
+952                KernelVersion::from_str("4.18"),
+953                Some(KernelVersion {
+954                    version: 4,
+955                    major_revision: 18
+956                })
+957            );
+958            // These were seen in the wild
+959            assert_eq!(
+960                KernelVersion::from_str("4.14.186-27095505"),
+961                Some(KernelVersion {
+962                    version: 4,
+963                    major_revision: 14
+964                })
+965            );
+966            assert_eq!(
+967                KernelVersion::from_str("6.8.0-59-generic"),
+968                Some(KernelVersion {
+969                    version: 6,
+970                    major_revision: 8
+971                })
+972            );
+973        }
+974    }
+975}
+976
+977// On Apple platforms using the `sendmsg_x` call, UDP datagram segmentation is not
+978// offloaded to the NIC or even the kernel, but instead done here in user space in
+979// [`send`]) and then passed to the OS as individual `iovec`s (up to `BATCH_SIZE`).
+980#[cfg(not(any(target_os = "linux", target_os = "android")))]
+981mod gso {
+982    use super::*;
+983
+984    pub(super) fn max_gso_segments() -> usize {
+985        #[cfg(apple_fast)]
+986        {
+987            BATCH_SIZE
+988        }
+989        #[cfg(not(apple_fast))]
+990        {
+991            1
+992        }
+993    }
+994
+995    pub(super) fn set_segment_size(
+996        #[cfg(not(apple_fast))] _encoder: &mut cmsg::Encoder<'_, libc::msghdr>,
+997        #[cfg(apple_fast)] _encoder: &mut cmsg::Encoder<'_, msghdr_x>,
+998        _segment_size: u16,
+999    ) {
+1000    }
+1001}
+1002
+1003#[cfg(any(target_os = "linux", target_os = "android"))]
+1004mod gro {
+1005    use super::*;
+1006
+1007    pub(crate) fn gro_segments() -> NonZeroUsize {
+1008        let Ok(socket) = std::net::UdpSocket::bind("[::]:0")
+1009            .or_else(|_| std::net::UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)))
+1010        else {
+1011            return NonZeroUsize::MIN;
+1012        };
+1013
+1014        // As defined in net/ipv4/udp_offload.c
+1015        // #define UDP_GRO_CNT_MAX 64
+1016        //
+1017        // NOTE: this MUST be set to UDP_GRO_CNT_MAX to ensure that the receive buffer size
+1018        // (get_max_udp_payload_size() * gro_segments()) is large enough to hold the largest GRO
+1019        // list the kernel might potentially produce. See
+1020        // https://github.com/quinn-rs/quinn/pull/1354.
+1021        match set_socket_option(&socket, libc::SOL_UDP, libc::UDP_GRO, OPTION_ON) {
+1022            Ok(()) => NonZeroUsize::new(64).expect("known"),
+1023            Err(_) => NonZeroUsize::MIN,
+1024        }
+1025    }
+1026}
+1027
+1028/// Returns whether the given socket option is supported on the current platform
+1029///
+1030/// Yields `Ok(true)` if the option was set successfully, `Ok(false)` if setting
+1031/// the option raised an `ENOPROTOOPT` or `EOPNOTSUPP` error, and `Err` for any other error.
+1032fn set_socket_option_supported(
+1033    socket: &impl AsRawFd,
+1034    level: libc::c_int,
+1035    name: libc::c_int,
+1036    value: libc::c_int,
+1037) -> io::Result<bool> {
+1038    match set_socket_option(socket, level, name, value) {
+1039        Ok(()) => Ok(true),
+1040        Err(err) if err.raw_os_error() == Some(libc::ENOPROTOOPT) => Ok(false),
+1041        Err(err) if err.raw_os_error() == Some(libc::EOPNOTSUPP) => Ok(false),
+1042        Err(err) => Err(err),
+1043    }
+1044}
+1045
+1046fn set_socket_option(
+1047    socket: &impl AsRawFd,
+1048    level: libc::c_int,
+1049    name: libc::c_int,
+1050    value: libc::c_int,
+1051) -> io::Result<()> {
+1052    let rc = unsafe {
+1053        libc::setsockopt(
+1054            socket.as_raw_fd(),
+1055            level,
+1056            name,
+1057            &value as *const _ as _,
+1058            mem::size_of_val(&value) as _,
+1059        )
+1060    };
+1061
+1062    match rc == 0 {
+1063        true => Ok(()),
+1064        false => Err(io::Error::last_os_error()),
+1065    }
+1066}
+1067
+1068const OPTION_ON: libc::c_int = 1;
+1069
+1070#[cfg(not(any(target_os = "linux", target_os = "android")))]
+1071mod gro {
+1072    use std::num::NonZeroUsize;
+1073
+1074    pub(super) fn gro_segments() -> NonZeroUsize {
+1075        NonZeroUsize::MIN
+1076    }
+1077}
\ No newline at end of file diff --git a/pr/509/docs/src/perf/client.rs.html b/pr/509/docs/src/perf/client.rs.html new file mode 100644 index 000000000..4e2d81f3c --- /dev/null +++ b/pr/509/docs/src/perf/client.rs.html @@ -0,0 +1,419 @@ +client.rs - source

perf/
client.rs

1#[cfg(feature = "json-output")]
+2use std::path::PathBuf;
+3use std::{
+4    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
+5    sync::Arc,
+6    time::{Duration, Instant},
+7};
+8
+9use anyhow::{Context, Result};
+10use bytes::Bytes;
+11use clap::Parser;
+12use 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        loop {
+170            let start = Instant::now();
+171            tokio::time::sleep(interval_duration).await;
+172            {
+173                stats.on_interval(start, &stream_stats);
+174
+175                stats.print();
+176                if opt.common.conn_stats {
+177                    println!("{:?}\n", connection.stats());
+178                }
+179            }
+180        }
+181    };
+182
+183    tokio::select! {
+184        _ = drive_fut => {}
+185        _ = stats_fut => {}
+186        _ = tokio::signal::ctrl_c() => {
+187            info!("shutting down");
+188            connection.close(0u32.into(), b"interrupted");
+189        }
+190        // Add a small duration so the final interval can be reported
+191        _ = tokio::time::sleep(Duration::from_secs(opt.duration) + Duration::from_millis(200)) => {
+192            info!("shutting down");
+193            connection.close(0u32.into(), b"done");
+194        }
+195    }
+196
+197    endpoint.wait_idle().await;
+198
+199    #[cfg(feature = "json-output")]
+200    if let Some(path) = opt.json {
+201        stats.print_json(path.as_path())?;
+202    }
+203
+204    Ok(())
+205}
+206
+207async fn drain_stream(
+208    mut stream: noq::RecvStream,
+209    download: u64,
+210    stream_stats: OpenStreamStats,
+211) -> Result<()> {
+212    if download == 0 {
+213        return Ok(());
+214    }
+215
+216    #[rustfmt::skip]
+217    let mut bufs = [
+218        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+219        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+220        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+221        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+222        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+223        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+224        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+225        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+226    ];
+227    let download_start = Instant::now();
+228    let recv_stream_stats = stream_stats.new_receiver(&stream, download);
+229
+230    let mut first_byte = true;
+231
+232    while let Some(size) = stream.read_chunks(&mut bufs[..]).await? {
+233        if first_byte {
+234            recv_stream_stats.on_first_byte(download_start.elapsed());
+235            first_byte = false;
+236        }
+237        let bytes_received = bufs[..size].iter().map(|b| b.len()).sum();
+238        recv_stream_stats.on_bytes(bytes_received);
+239    }
+240
+241    if first_byte {
+242        recv_stream_stats.on_first_byte(download_start.elapsed());
+243    }
+244    recv_stream_stats.finish(download_start.elapsed());
+245
+246    debug!("response finished on {}", stream.id());
+247    Ok(())
+248}
+249
+250async fn drive_uni(
+251    connection: noq::Connection,
+252    stream_stats: OpenStreamStats,
+253    concurrency: u64,
+254    upload: u64,
+255    download: u64,
+256) -> Result<()> {
+257    if concurrency == 0 {
+258        return Ok(());
+259    }
+260
+261    let sem = Arc::new(Semaphore::new(concurrency as usize));
+262
+263    loop {
+264        let permit = sem.clone().acquire_owned().await.unwrap();
+265        let send = connection.open_uni().await?;
+266        let stream_stats = stream_stats.clone();
+267
+268        debug!("sending request on {}", send.id());
+269        let connection = connection.clone();
+270        tokio::spawn(async move {
+271            if let Err(e) = request_uni(send, connection, upload, download, stream_stats).await {
+272                error!("sending request failed: {:#}", e);
+273            }
+274
+275            drop(permit);
+276        });
+277    }
+278}
+279
+280async fn request_uni(
+281    send: noq::SendStream,
+282    conn: noq::Connection,
+283    upload: u64,
+284    download: u64,
+285    stream_stats: OpenStreamStats,
+286) -> Result<()> {
+287    request(send, upload, download, stream_stats.clone()).await?;
+288    let recv = conn.accept_uni().await?;
+289    drain_stream(recv, download, stream_stats).await?;
+290    Ok(())
+291}
+292
+293async fn request(
+294    mut send: noq::SendStream,
+295    mut upload: u64,
+296    download: u64,
+297    stream_stats: OpenStreamStats,
+298) -> Result<()> {
+299    let upload_start = Instant::now();
+300    send.write_all(&download.to_be_bytes()).await?;
+301    if upload == 0 {
+302        send.finish().unwrap();
+303        return Ok(());
+304    }
+305
+306    let send_stream_stats = stream_stats.new_sender(&send, upload);
+307
+308    static DATA: [u8; 1024 * 1024] = [42; 1024 * 1024];
+309    while upload > 0 {
+310        let chunk_len = upload.min(DATA.len() as u64);
+311        send.write_chunk(Bytes::from_static(&DATA[..chunk_len as usize]))
+312            .await
+313            .context("sending response")?;
+314        send_stream_stats.on_bytes(chunk_len as usize);
+315        upload -= chunk_len;
+316    }
+317    send.finish().unwrap();
+318    // Wait for stream to close
+319    _ = send.stopped().await;
+320    send_stream_stats.finish(upload_start.elapsed());
+321
+322    debug!("upload finished on {}", send.id());
+323    Ok(())
+324}
+325
+326async fn drive_bi(
+327    connection: noq::Connection,
+328    stream_stats: OpenStreamStats,
+329    concurrency: u64,
+330    upload: u64,
+331    download: u64,
+332) -> Result<()> {
+333    if concurrency == 0 {
+334        return Ok(());
+335    }
+336
+337    let sem = Arc::new(Semaphore::new(concurrency as usize));
+338
+339    loop {
+340        let permit = sem.clone().acquire_owned().await.unwrap();
+341        let (send, recv) = connection.open_bi().await?;
+342        let stream_stats = stream_stats.clone();
+343
+344        debug!("sending request on {}", send.id());
+345        tokio::spawn(async move {
+346            if let Err(e) = request_bi(send, recv, upload, download, stream_stats).await {
+347                error!("request failed: {:#}", e);
+348            }
+349
+350            drop(permit);
+351        });
+352    }
+353}
+354
+355async fn request_bi(
+356    send: noq::SendStream,
+357    recv: noq::RecvStream,
+358    upload: u64,
+359    download: u64,
+360    stream_stats: OpenStreamStats,
+361) -> Result<()> {
+362    request(send, upload, download, stream_stats.clone()).await?;
+363    drain_stream(recv, download, stream_stats).await?;
+364    Ok(())
+365}
+366
+367#[derive(Debug)]
+368struct SkipServerVerification(Arc<rustls::crypto::CryptoProvider>);
+369
+370impl SkipServerVerification {
+371    fn new(provider: Arc<rustls::crypto::CryptoProvider>) -> Arc<Self> {
+372        Arc::new(Self(provider))
+373    }
+374}
+375
+376impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
+377    fn verify_server_cert(
+378        &self,
+379        _end_entity: &CertificateDer<'_>,
+380        _intermediates: &[CertificateDer<'_>],
+381        _server_name: &ServerName<'_>,
+382        _ocsp: &[u8],
+383        _now: UnixTime,
+384    ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
+385        Ok(rustls::client::danger::ServerCertVerified::assertion())
+386    }
+387
+388    fn verify_tls12_signature(
+389        &self,
+390        message: &[u8],
+391        cert: &CertificateDer<'_>,
+392        dss: &rustls::DigitallySignedStruct,
+393    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
+394        rustls::crypto::verify_tls12_signature(
+395            message,
+396            cert,
+397            dss,
+398            &self.0.signature_verification_algorithms,
+399        )
+400    }
+401
+402    fn verify_tls13_signature(
+403        &self,
+404        message: &[u8],
+405        cert: &CertificateDer<'_>,
+406        dss: &rustls::DigitallySignedStruct,
+407    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
+408        rustls::crypto::verify_tls13_signature(
+409            message,
+410            cert,
+411            dss,
+412            &self.0.signature_verification_algorithms,
+413        )
+414    }
+415
+416    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
+417        self.0.signature_verification_algorithms.supported_schemes()
+418    }
+419}
\ No newline at end of file diff --git a/pr/509/docs/src/perf/lib.rs.html b/pr/509/docs/src/perf/lib.rs.html new file mode 100644 index 000000000..4694f9e5a --- /dev/null +++ b/pr/509/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    Bbr,
+202    NewReno,
+203}
+204
+205impl CongestionAlgorithm {
+206    pub fn build(self) -> Arc<dyn ControllerFactory + Send + Sync + 'static> {
+207        match self {
+208            Self::Cubic => Arc::new(congestion::CubicConfig::default()),
+209            Self::Bbr => Arc::new(congestion::BbrConfig::default()),
+210            Self::NewReno => Arc::new(congestion::NewRenoConfig::default()),
+211        }
+212    }
+213}
+214
+215pub static PERF_CIPHER_SUITES: &[rustls::SupportedCipherSuite] = &[
+216    cipher_suite::TLS13_AES_128_GCM_SHA256,
+217    cipher_suite::TLS13_AES_256_GCM_SHA384,
+218    cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
+219];
\ No newline at end of file diff --git a/pr/509/docs/src/perf/noprotection.rs.html b/pr/509/docs/src/perf/noprotection.rs.html new file mode 100644 index 000000000..7f8c771c9 --- /dev/null +++ b/pr/509/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/509/docs/src/perf/server.rs.html b/pr/509/docs/src/perf/server.rs.html new file mode 100644 index 000000000..ef6348b3f --- /dev/null +++ b/pr/509/docs/src/perf/server.rs.html @@ -0,0 +1,202 @@ +server.rs - source

perf/
server.rs

1use std::{net::SocketAddr, path::PathBuf, sync::Arc, time::Duration};
+2
+3use anyhow::{Context, Result};
+4use bytes::Bytes;
+5use clap::Parser;
+6use 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!("{} connected", connection.remote_address());
+105    tokio::try_join!(
+106        drive_uni(connection.clone()),
+107        drive_bi(connection.clone()),
+108        conn_stats(connection, opt)
+109    )?;
+110    Ok(())
+111}
+112
+113async fn conn_stats(connection: noq::Connection, opt: Arc<Opt>) -> Result<()> {
+114    if opt.common.conn_stats {
+115        loop {
+116            tokio::time::sleep(Duration::from_secs(2)).await;
+117            println!("{:?}\n", connection.stats());
+118        }
+119    }
+120
+121    Ok(())
+122}
+123
+124async fn drive_uni(connection: noq::Connection) -> Result<()> {
+125    while let Ok(stream) = connection.accept_uni().await {
+126        let connection = connection.clone();
+127        tokio::spawn(async move {
+128            if let Err(e) = handle_uni(connection, stream).await {
+129                error!("request failed: {:#}", e);
+130            }
+131        });
+132    }
+133    Ok(())
+134}
+135
+136async fn handle_uni(connection: noq::Connection, stream: noq::RecvStream) -> Result<()> {
+137    let bytes = read_req(stream).await?;
+138    let response = connection.open_uni().await?;
+139    respond(bytes, response).await?;
+140    Ok(())
+141}
+142
+143async fn drive_bi(connection: noq::Connection) -> Result<()> {
+144    while let Ok((send, recv)) = connection.accept_bi().await {
+145        tokio::spawn(async move {
+146            if let Err(e) = handle_bi(send, recv).await {
+147                error!("request failed: {:#}", e);
+148            }
+149        });
+150    }
+151    Ok(())
+152}
+153
+154async fn handle_bi(send: noq::SendStream, recv: noq::RecvStream) -> Result<()> {
+155    let bytes = read_req(recv).await?;
+156    respond(bytes, send).await?;
+157    Ok(())
+158}
+159
+160async fn read_req(mut stream: noq::RecvStream) -> Result<u64> {
+161    let mut buf = [0; 8];
+162    stream
+163        .read_exact(&mut buf)
+164        .await
+165        .context("reading request")?;
+166    let n = u64::from_be_bytes(buf);
+167    debug!("got req for {} bytes on {}", n, stream.id());
+168    drain_stream(stream).await?;
+169    Ok(n)
+170}
+171
+172async fn drain_stream(mut stream: noq::RecvStream) -> Result<()> {
+173    #[rustfmt::skip]
+174    let mut bufs = [
+175        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+176        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+177        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+178        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+179        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+180        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+181        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+182        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+183    ];
+184    while stream.read_chunks(&mut bufs[..]).await?.is_some() {}
+185    debug!("finished reading {}", stream.id());
+186    Ok(())
+187}
+188
+189async fn respond(mut bytes: u64, mut stream: noq::SendStream) -> Result<()> {
+190    static DATA: [u8; 1024 * 1024] = [42; 1024 * 1024];
+191
+192    while bytes > 0 {
+193        let chunk_len = bytes.min(DATA.len() as u64);
+194        stream
+195            .write_chunk(Bytes::from_static(&DATA[..chunk_len as usize]))
+196            .await
+197            .context("sending response")?;
+198        bytes -= chunk_len;
+199    }
+200    debug!("finished responding on {}", stream.id());
+201    Ok(())
+202}
\ No newline at end of file diff --git a/pr/509/docs/src/perf/stats.rs.html b/pr/509/docs/src/perf/stats.rs.html new file mode 100644 index 000000000..9b65dfe3c --- /dev/null +++ b/pr/509/docs/src/perf/stats.rs.html @@ -0,0 +1,413 @@ +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        match path {
+124            path if path == Path::new("-") => json::print(self, std::io::stdout()),
+125            _ => {
+126                let file = File::create(path)?;
+127                json::print(self, file)
+128            }
+129        }
+130        Ok(())
+131    }
+132}
+133
+134/// Statistics for the currently open streams
+135#[derive(Clone, Default)]
+136pub struct OpenStreamStats(Arc<Mutex<Vec<Arc<StreamStats>>>>);
+137
+138impl OpenStreamStats {
+139    pub fn new_sender(&self, stream: &noq::SendStream, upload_size: u64) -> Arc<StreamStats> {
+140        let send_stream_stats = StreamStats {
+141            id: stream.id(),
+142            request_size: upload_size,
+143            bytes: Default::default(),
+144            sender: true,
+145            finished: Default::default(),
+146            duration: Default::default(),
+147            first_byte_latency: Default::default(),
+148        };
+149        let send_stream_stats = Arc::new(send_stream_stats);
+150        self.push(send_stream_stats.clone());
+151        send_stream_stats
+152    }
+153
+154    pub fn new_receiver(&self, stream: &noq::RecvStream, download_size: u64) -> Arc<StreamStats> {
+155        let recv_stream_stats = StreamStats {
+156            id: stream.id(),
+157            request_size: download_size,
+158            bytes: Default::default(),
+159            sender: false,
+160            finished: Default::default(),
+161            duration: Default::default(),
+162            first_byte_latency: Default::default(),
+163        };
+164        let recv_stream_stats = Arc::new(recv_stream_stats);
+165        self.push(recv_stream_stats.clone());
+166        recv_stream_stats
+167    }
+168
+169    fn push(&self, stream_stats: Arc<StreamStats>) {
+170        self.0.lock().unwrap().push(stream_stats);
+171    }
+172}
+173
+174pub struct StreamStats {
+175    id: StreamId,
+176    request_size: u64,
+177    bytes: AtomicUsize,
+178    sender: bool,
+179    finished: AtomicBool,
+180    duration: AtomicU64,
+181    first_byte_latency: AtomicU64,
+182}
+183
+184impl StreamStats {
+185    pub fn on_first_byte(&self, latency: Duration) {
+186        self.first_byte_latency
+187            .store(latency.as_micros() as u64, Ordering::SeqCst);
+188    }
+189
+190    pub fn on_bytes(&self, bytes: usize) {
+191        self.bytes.fetch_add(bytes, Ordering::SeqCst);
+192    }
+193
+194    pub fn finish(&self, duration: Duration) {
+195        self.duration
+196            .store(duration.as_micros() as u64, Ordering::SeqCst);
+197        self.finished.store(true, Ordering::SeqCst);
+198    }
+199}
+200
+201struct Interval {
+202    streams: Vec<StreamIntervalStats>,
+203    period: IntervalPeriod,
+204}
+205
+206impl Interval {
+207    fn new(start: Duration, end: Duration) -> Self {
+208        let period = IntervalPeriod {
+209            start: start.as_secs_f64(),
+210            end: end.as_secs_f64(),
+211            seconds: (end - start).as_secs_f64(),
+212        };
+213
+214        Self {
+215            streams: vec![],
+216            period,
+217        }
+218    }
+219
+220    fn record_stream_stats(&mut self, stream_stats: Arc<StreamStats>) {
+221        let bytes = stream_stats.bytes.swap(0, Ordering::SeqCst);
+222        self.streams.push(StreamIntervalStats {
+223            id: stream_stats.id,
+224            bytes,
+225            sender: stream_stats.sender,
+226        })
+227    }
+228}
+229
+230struct IntervalPeriod {
+231    start: f64,
+232    end: f64,
+233    seconds: f64,
+234}
+235
+236struct StreamIntervalStats {
+237    id: StreamId,
+238    bytes: usize,
+239    sender: bool,
+240}
+241
+242fn throughput_bytes_per_second(duration_in_micros: u64, size: u64) -> f64 {
+243    (size as f64) / (duration_in_micros as f64 / 1000000.0)
+244}
+245
+246#[cfg(feature = "json-output")]
+247mod json {
+248    use crate::stats;
+249    use crate::stats::{Stats, StreamIntervalStats};
+250    use noq::StreamId;
+251    use serde::{self, Serialize, Serializer, ser::SerializeStruct};
+252    use std::io::Write;
+253    use std::time::{SystemTime, UNIX_EPOCH};
+254
+255    pub(crate) fn print<W: Write>(stats: &Stats, out: W) {
+256        let report = Report {
+257            start: Start {
+258                timestamp: stats.start,
+259            },
+260            intervals: &stats
+261                .intervals
+262                .iter()
+263                .map(Interval::from_stats_interval)
+264                .collect(),
+265        };
+266
+267        serde_json::to_writer(out, &report).unwrap();
+268    }
+269
+270    #[derive(Serialize)]
+271    struct Report<'a> {
+272        start: Start,
+273        intervals: &'a Vec<Interval>,
+274        // TODO: add end stats
+275    }
+276
+277    #[derive(Serialize)]
+278    struct Start {
+279        #[serde(serialize_with = "serialize_timestamp")]
+280        timestamp: SystemTime,
+281    }
+282
+283    fn serialize_timestamp<S>(time: &SystemTime, s: S) -> Result<S::Ok, S::Error>
+284    where
+285        S: serde::Serializer,
+286    {
+287        use serde::ser::SerializeMap;
+288        let mut state = s.serialize_map(Some(1))?;
+289        state.serialize_entry(
+290            "timesecs",
+291            &time.duration_since(UNIX_EPOCH).unwrap().as_secs(),
+292        )?;
+293        state.end()
+294    }
+295
+296    struct Interval {
+297        streams: Vec<Stream>,
+298        recv_sum: Sum,
+299        send_sum: Sum,
+300    }
+301
+302    impl Interval {
+303        fn from_stats_interval(interval: &stats::Interval) -> Self {
+304            Self {
+305                streams: interval
+306                    .streams
+307                    .iter()
+308                    .map(|stats| Stream::from_stream_interval_stats(stats, &interval.period))
+309                    .collect(),
+310                recv_sum: Sum::from_stream_interval_stats(
+311                    &interval.streams,
+312                    &interval.period,
+313                    false,
+314                ),
+315                send_sum: Sum::from_stream_interval_stats(
+316                    &interval.streams,
+317                    &interval.period,
+318                    true,
+319                ),
+320            }
+321        }
+322    }
+323
+324    impl Serialize for Interval {
+325        fn serialize<S>(
+326            &self,
+327            serializer: S,
+328        ) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
+329        where
+330            S: Serializer,
+331        {
+332            let mut state = serializer.serialize_struct("Interval", 2)?;
+333            state.serialize_field("streams", &self.streams)?;
+334            // iperf3 outputs duplicate "sum" entries when run in bidirectional mode
+335            // serde does not support duplicate keys, so only output one of the sums
+336            if self.send_sum.bytes > 0 {
+337                state.serialize_field("sum", &self.send_sum)?;
+338            } else {
+339                state.serialize_field("sum", &self.recv_sum)?;
+340            }
+341            state.end()
+342        }
+343    }
+344
+345    #[derive(Serialize)]
+346    struct Stream {
+347        #[serde(serialize_with = "serialize_stream_id")]
+348        id: StreamId,
+349        start: f64,
+350        end: f64,
+351        seconds: f64,
+352        bytes: usize,
+353        bits_per_second: f64,
+354        sender: bool,
+355    }
+356
+357    impl Stream {
+358        fn from_stream_interval_stats(
+359            stats: &stats::StreamIntervalStats,
+360            period: &stats::IntervalPeriod,
+361        ) -> Self {
+362            let bits_per_second = stats.bytes as f64 * 8.0 / period.seconds;
+363
+364            Self {
+365                id: stats.id,
+366                start: period.start,
+367                end: period.end,
+368                seconds: period.seconds,
+369                bytes: stats.bytes,
+370                bits_per_second,
+371                sender: stats.sender,
+372            }
+373        }
+374    }
+375
+376    fn serialize_stream_id<S: Serializer>(id: &StreamId, serializer: S) -> Result<S::Ok, S::Error> {
+377        serializer.serialize_u64(u64::from(*id))
+378    }
+379
+380    #[derive(Serialize)]
+381    struct Sum {
+382        start: f64,
+383        end: f64,
+384        seconds: f64,
+385        bytes: usize,
+386        bits_per_second: f64,
+387        sender: bool,
+388    }
+389
+390    impl Sum {
+391        fn from_stream_interval_stats(
+392            stats: &[StreamIntervalStats],
+393            period: &stats::IntervalPeriod,
+394            sender: bool,
+395        ) -> Self {
+396            let bytes = stats
+397                .iter()
+398                .filter(|stat| stat.sender == sender)
+399                .map(|stat| stat.bytes)
+400                .sum();
+401            let bits_per_second = bytes as f64 * 8.0 / period.seconds;
+402
+403            Self {
+404                start: period.start,
+405                end: period.end,
+406                seconds: period.seconds,
+407                bytes,
+408                bits_per_second,
+409                sender,
+410            }
+411        }
+412    }
+413}
\ No newline at end of file diff --git a/pr/509/docs/src/set_up_connection/set-up-connection.rs.html b/pr/509/docs/src/set_up_connection/set-up-connection.rs.html new file mode 100644 index 000000000..039f258a7 --- /dev/null +++ b/pr/509/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/509/docs/static.files/COPYRIGHT-7fb11f4e.txt b/pr/509/docs/static.files/COPYRIGHT-7fb11f4e.txt new file mode 100644 index 000000000..752dab0a3 --- /dev/null +++ b/pr/509/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/509/docs/static.files/FiraMono-Medium-86f75c8c.woff2 b/pr/509/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/509/docs/static.files/FiraMono-Regular-87c26294.woff2 b/pr/509/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/509/docs/static.files/FiraSans-Italic-81dc35de.woff2 b/pr/509/docs/static.files/FiraSans-Italic-81dc35de.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..3f63664fee6ddf221f71a57461a7f4d1de281177 GIT binary patch literal 136300 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 _HdD`n5<@_AzZ{+=$) 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�e%nZC1!^pP?`Fe-tm;C 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 G0{{TFWc059 literal 0 HcmV?d00001 diff --git a/pr/509/docs/static.files/FiraSans-LICENSE-05ab6dbd.txt b/pr/509/docs/static.files/FiraSans-LICENSE-05ab6dbd.txt new file mode 100644 index 000000000..d7e9c149b --- /dev/null +++ b/pr/509/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/509/docs/static.files/FiraSans-Medium-e1aa3f0a.woff2 b/pr/509/docs/static.files/FiraSans-Medium-e1aa3f0a.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..7a1e5fc548ef28137a32150b6aa50a568cd53d02 GIT binary patch literal 132780 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�|o|A<_G9#bJ&K$mXTSM@kZfZ^-ZYS)6EG~kHJ{! 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

TIWHPC<_9>em(Cvlhuf-BD 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 literal 0 HcmV?d00001 diff --git a/pr/509/docs/static.files/FiraSans-MediumItalic-ccf7e434.woff2 b/pr/509/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/509/docs/static.files/FiraSans-Regular-0fe48ade.woff2 b/pr/509/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 z8LKR1xI2@S!59U8c7Hs;$~3BKr$|YD#i8q3=*MFcQ4N`U06ft$(Q0dJ3@?2UR4o8& z>zoDR#y0U{*TjrS=h$lCM3N@_l(X~y|NsC0|NsC0|F@F-2!FFT6PVfE$!@+tNCcDu zV*OOD)=#aqt^M7#cMv7cyWlNH?h%ut1`6 zwUxO^vkq8=dF$^BHHk>@&PDhZr61bLw#>sLN?OgU?&GenmrH@RP9d{xXMGzS6P$A< zKDyp3Qu3RHSse0mZX&J3iW~=@m(6C;6c`@Kyg#tjmdsE5V)6yv3!-3XoTI+--qUkLbEt#9b&F^gHZ)%y(U&H6 z1u9HiAEL}4@TIQT(1|cJ(OePbzV4Ebwn8#9Sj5?3U-l`%hAAu(E!u8kWIpMl2F2F( zPD7W(g^)`kEvj-#irk}I;@~R|T6`l~HfV|+e22QV7PAyYCLS>ac&N-A_4&1yKEcVQ z^x`}ka(X#nMmD5AVTD-;mdwr5tw;3GvRep@;a$$OvRKHHle_{WQi~wjMj5rRDmGjoZsBE9LJWj4J~BoBL;k z@e4bDkQMXqFXC1o@#hb-fa=9xW|!fw=Tul}x(xr}D^p=%xSRboxr1+6F|HroJDl(@S>k3)Kk)sXj-9bK z&cb%%zTOM=L)dXg<i#UCSHd;}^V z(%-N4y7zNwmD18~=i5|^N~sR|t)do%ZJI6&3fmTU!3YKyVKIgYSZpxvHXbyn$p6er`k!5Tv9him0cc#DW+w#`u&rSe(gW5KdCC04vVK%=0`rD>ywYJOL3A zFcLgbaRK1_*;0M|k@r1mX2(fcaM7J{?SxCL2rg~h27op|SB1Fs<@o1i&mH?wu}(K$ zAT<+;bwZS*kZnp9^P@{I0;FV`}(}AJ9ca*w4hBMP8=T6w9^9fQa}E#pr^H= z1&Q`dZ74yaGsUKb=swo}744ncv<0a%>(-$!@lc)b+uQshz=7CNz(13jf)#)Ycz{vr z0L`sKDSefwjTjrb3Sz|8cdNdGRv57$%1SJZhgU&GeG4o;jeilV0Rv1FL_l$Z_y(9S zVQdS@z%A-4VfMxWKeq=1!6Zg*EQ-+!Wk`)!1>4*1&5a%iCTL?7+5=P!m|$QODuRuO z!AAuH3otMc1#ApHumL`A|0cSf=~DTnpGp_faT|8LZMMC)=e;@R$dGf&k?Y(r$8UkLSnl7uhaj!LA*IfGCy?P%jg?ht)uHXUL-wEKgAv@hu4(s&1hZq4B(*FOy)uP$_G9rdq zjAo<0gV`uXHtN@)#oS7GMirw6v#*U&*l3TH@KyNBpos9h+FYC#O?63s1TzC3d+*-> z0s;U3cP?k-$;qlg)qp*an~-PJA7kH@<87{>(v0}P4`c!dbZ_E{AwN{y$FmB-Rqb`V!QCVP+n{i^;gvix3I)@XJ} z=`6>VQp`7eNn#&kVY&_DW1Ms#iL<4VWPQ4ED8StL^7{WigaOp;#3iLtl~k(z@9Pn~ z1hkzVK-)R97FQh0)%IN23r(@o123v))6NPqH0iP}BKkc?RK7f|SX1Dn&Cp~rMJHEG zzG4Hs?f!ig_o?I~c4){fS_;*TcW>UiyZ7F`cVl+lT|)^OE1#yO+0d5M#y36@qM^+6 zLu9RDfBDAO&(qoh6)@mb!oaf$Q3M2)(g7?|MT(x>&{DKO z(G9UN*akirj4^BsX4sHzFktxb4DfU7A6^C5WuVQ25o7OBG8XI&T4{k18QXZU{%y&{qy~%-)B`N21E2V3|w)eW~*H@OD0S+ zFqELluw72m>sj;nB}FAWKy&Meh;6_a+lWOMtZsvrDwPl=MLz~6f@s9-l#V+ z-#3f@`|aCdk5Td28CT3Lbj*!p0|P6cfiVUK#u#H@j4{Tjj8Pb9|GM^$;}M1a3a~it z_>3B!9>^($?$5ncje1FQ&GI@Rn#idYr;JPZ;%;twGZ|ld@CL?Ukr&!6aSVi#NwQgH z8JLFJR=hrYcLoM+3`R2&SSxr$WN*Se8-1zIxx%tjn z#0HFtZ6GL-f>ETt4Wd{W z{eIX%SYTn+K-=uQi|DP5#ssS;HdP8XSb$Ym1N_{2L>ZyDjask(1Y|7PfH7q8-rI{6 zjJ@p*FcFw+l!V@)m&yeun6#K+;N5X$BzNt7jEFCy4G1cGs;&ASFf>|pS$U~W;@yAL zsAL&5AnXHroHJ8%<*JpLp1S$+ z$B^!;|5rp+l8Qh?hZ|^nc4pzyf6o|14pIz4$DY!eEl65hV9`pv0!y|G6#E?XMGB#- z)=!*p>7x3g*K@+|@?C!VZ#wSef30N^>VyL10J@_Ps-58M5%vgoLINoiCJb`j&DLB) zuz$Ei^Bg>ZmWE+Qsal&hG z012f$n$e7;Q5q${X8l=vFlARKWE8J9wOZ`V~)#l;@ecwVQi;%ob)f-pPph8NPvjK#@OG1LU!oWlvHq&1*R!zst0EoAR#iY zUIU;5=+AVpkan!b0M+;R{C^AZh(Fk1LskOWKy<@L@KgW(!8<=~`~tV}HLXEG;KW}R z3>-|<=D+MNu<&+)B={mLE?mH>57Vi3C{z3t$ue$^M*3Z@o!Tc7qyz#|H3d-J z5~+4l`|2KR)so7$Vg2rJoY~D*za9-1R1VUbMvRlJ|vi6OaVpDw@aTi z-mC=1zrfDu{&y|2f0`ZuN9G89!2vn-H#_t8W@aIIb6g;KKq>`feL{C-7F(I;t2_yF z4MQ(V43qv5tEyD9+-iBG0~F=JlLjGZl(brQ4`Q*-yF{fUc{#r{NO=U;>FhYO^x_RuWQcL~eI*G`m?34}qZ^8Rr-bLz z25by)Uu9)hWvZk`{>&c`a|2H6!V4nTnU< z*w$=<0YK0pCT5;$s_Gu-*{ciNPJtpo6JW{rx$O2+?bFsz(9maF2>O?EmxV1&H3YWo zLXT)s*CtI_lK4zO?&T)5hJfBjrynurceA^12L(hyEH#Ne3fNFnyGi%=c0MQ8|2qlE z`^#PWwtbA=t*S;;T~RTrVnjsMh>D6QzW90a3GvYw*Yp^_S5s_0FaSv$q@#H<%)k(J zkl0Ud_d4I^N?o|h z90Pz1PXK6+qx=+Q%B|K9&kz3hOaulCg+L)t2m}TK1A)TAz(8RjQ0V==7uffi{+-$P zCFd`>@OyUO>)D!{3}kR;Hc1-Zn#DX{w2(xyQ|Xp5q>qEx$4v77 z>i2>eg%%amnnfeSR$OXTP7~?2d|oi}xs@hn7s+_pV(jv0X5N&F$b$k9C~wI81|3B5 z#pUOOHgU)&fU5MIZfOuTEI~(n|3Qr z#6_U+^zuQ)+XC7Ypw{gjL6$v$f4>o@3}E-oKzE^QX$7C4{FM2kNP}py?E@R-+LFcuh(={DLVqpy3^$T2ar0?GAr^ZP5iXb% zQik>b0e=H>jgr+Ixj|a{EsG+AY5W zMuJtM*GZ5Nec~96K#i3Vor#PLgJ1^S5aI=gfnFm^7qFv=$`H{L$bg?CJ&b!`2~Z(%`c=*p`DbwO6zj2cEl4CHEAt^* zgQ$HF@FySAH`VY>*D0>3mEruI$!G9{z&*UFX5pk`|$fN0#wb$8=eFQ{jBF^BG$jO!Vj+B2=DJz2Own*|j5FXS{Z{$ytaICKHx_=JbCrHo6Gb8&>`j zc{Y~5*(YRpw3kzsMSl%u1wy@^1)FqcTFWQ7??&=O$0Ki8{Vz9BN*9A1?hVY2rqkI< zE3I+sPw`7O$StXBVCfLNB6EL7^RL7`Z`KB+F9(?2eggLkO~a4S3wG3*+^0*^-x z-qERjgqDc`y%fbQ$=I2m5*5Gv-!aPINszZ@{0%4=jo_Lc^$&6|vh)5JNg4lF3UDx34Uj*EFSRH5t4^qq{f2<&|7~aJFdq|UYrt$bQJ%>5kC<>z zjS~iMyeV>a3S5wgJq8E@@7U(9N22OaJNODFW75+>4$ofjThR>&|G{@Dvq2=o4Hhu- zO{JDWuG0aR?|5gvpI-8R1(OVlpY)kbdt#qewTEe@Jt-JFSNy@-G--HSeg3P=cw|B1 zhMNqZ);K7Yp`A7t=-}mROU)3c$|S)-49R@6JyHfA1ZVVx277yVq!M2K2p(Dk2k~c~ zTGSe1s{+KwyyQ;lS-w=SM5Ex0hT6w?UaO4vKrSb(yE zH-}Y_TcTD2r*d8+LFuCGLw9z!=Aq$o(~Md)(u+8Hoav9sy$77%@jB}wfzpxgqwy8D zlm(QGVJ&0I5>s_)Q|zuzZ2Bd)LJT$z{TrGJgQs$>>s(stsbW8}u$qa7Vh7MWxh1?i zBAGIIz?{$*zt;3x*4CEQ#HYu6m&K;ZysHpi=#V0)p ziu?Gj99d7b0Cg+$Z#a;@OYa=qCZ>}_Xvmbwx-fDJl~veU!k*ll^GV=B;;4z7bHuPH zmyw3-E&QA7)zz?3uyd$0#|`4IhQBAhm~hv#Y4<%U9~myLFEO?z%d_2{+~e+@?Y*5$ z)1R(#wRwl)BPKhhF3X6G&l~RER6kbTI(MHsQA~HwxcGXGhq zuk7Vz{=$%ees8{GO!7>_FNTT`~4 z0L!{}<<53iia(BQX(Od~#KS=;LZU~sfye{WzaHJ1?za*bls?|#1|B#;*s=bX_r?&u zfr0KdJBWx^44qNoea2WA-=_F!_dmPve~niuvQzs%at85D=7sNS1W)m>+wmo%#}oY6M_KzmdWfrd#a{)%A(*{@dKQj^eRLgrg$MOD)wr3PAM{CJ7JiY)Q znBZ$3=c^Xp&Ou^S>a>{>XzJ4IW0~*A zynZUr8eEuhVYf%pb~tShzaw?UV(uu#!zCqCOyZK`;X5hru+ap9MOr>X#L)jMc!Q0I zh>VYbW=VD!EFQLvp&5`6J5w%I$A0p-8bg(ABV;V4GqH3V(B5+ZWlcmDhF!?x$j?(QEysxUM|GgYHUGvblalyNE*sYog1 zR3v071u2o*NiC^&sw#Dgd&-$IDR66cR5{8nLs^u!@>jab=?rJ2ou1NFrj>~@Rw_f0 zIT~u|^!@432fWYbqtdy0d`^x64O#{@|Anfa_+Tbf! zn_HM`_=XVyE!C>BzJ76B%ztL$;(1y!w)4L~yug=<6uEp|4Ke11!lI^mVJM+`F?yHe0<d zTL-qzymmse3GMXrDb45j^QSJC0GeV8Y;S-fuqCrxjyf|)$-Ab!{8C0?#J{e#-lc5) z=Je&KGt~5I+%S;yn0m;BQErREp< zM)4a3`t~;h@r_>J=*@59H|96|Mtq)29I=4y#S0|Zxg4;FV?C-C(>sxkeL|@KfW1k% z2C4=PiU=zjB;rye3}{zW?56zPV@C=*_qg3toT)vCszw!sT&XvIFx#mLi5wM2iX&5}`Hb3boMn9q3okf{Y*?kVGOJ z!4Onn!Xh+R5CjoVhzx*l>p%o+P!&7^LpT^B7|bv;+_8W;T7Al3P-}7=%1McMQRF3c z0PFyU>Mx7dOG~@_u zNlu_&5Weg-wE8_7#3K=DFl6jD)WBDR|0UYmhH4L*H*Jbd-n{wEwhHQa!P zXv~dwqpEQZgW90*s3Wi?IfK60b?@q92B1eBfi1}i^aWSa+(THMn4M$ny5Qvp;9%xJ zAQ;Uw*nqe^QO3X3&@Y8o8RSLO$|?oh$)u?#866Kc{TjM_?xkNF_@ZA%zvj{}^h*Pc zaR#UjIe?Oxwj~e4u&AhGkYJMK{)a&WLs%XM?ZkmL5Tu4(VL>@bm8MrlU4C7%y{BHU z)0{eA1DUeXfyn5*YHNBTeL`82YSq&zF!>s)dX3YhLKWUXAxB_Kas-bG6V910Io3PI zu8c~H+b4TfRFn%!)LcWI(ix|+PQN%g&VQdUj5=160AYYf9g+P%c zrY=imyvt!b0}ae{<=um`C0^)fVQ_fV(9Lq>5%dLJ%RU>!7;nznfv#LgY9%L(B9!Rw z0B=5l(eM>ej38PZfroVTtfr$x%Iue#0JEt+2 zpoR#yIkNx;C{RFyf+w7SIgm%^f+A&7>8XI_SitJel|nwx!rXZ%hZQvKr49t8Sj1dU zqo~G0wkoKt`icffbN>|}Boq-syO2T%NytKxLXbith3J(A{EVGjZAe?N{k36uJWhZuU>!arISfpUyYGiHAO&iu(DMW#gCvkW=hgZ zba)FL?y3 z9Ys?y>BxBVtDPRiS$B8izRaA{H^rieT%qXo!cGv z)GATUd>89I8x5=CTLPH~{dIOPj?FRKi(#=?JTIPFgsk3@w5|I8|&BW}z|6(&`;sTA8(ns17+ z*Y|kK8@l!+vy)Nz?^LwxH=_Qdu0it6$IeQi62X{5zcB=Zdu3+;>NXV-%(t=dSin?> zsTilCs#J=i)&JB`gABI39*&z1SV>VOEVEsf;ldcC4)({;FW79}#XACjV84NS7lQX#AjUs@y<+~iLJ;py zm?9F^%Z^IXso{sDXjQE}TRUlKT1ixDZ8=vdt&A#U++0nl(=v}%y=`R5OhD0`rDK88 zstQd|8GCsmMGezH2dZPx_@>`f-J@z%?xcn@?i*{ST4GvRRC;Ntsao>`91;ZY6AR6! zZuM$K7m7ohmONVHK^0HYvKoIs3GZCfRee=Bez_Ap8x)lBuE%|`4C)%L-Wcl$8M9a5 zzRxnInvPT91~k~G^pu~1WJXBo`cq%0+Td`mJ{$sE2td+yz}Lu?K758I8uugL7M_UTHz>iAH6 zC`RDD@M9sri62@%RP~|9QxBd3`%}3oV^exlicQ%!W!383obN521E0B)aU?zxI1(`) zNwy<7{Nea;JYA*7$MB(e*7Ny1`S8RafBu;3P#(IzHk1c$7$7!eRQ?2rAC9KDdcF}j zp2P?7#82Rfc(OccwLT_xm*;e4D>6>4dUBd4lbs*C)=jak!G4_C0f>solL#CM$}#2F z;s@G>^Y7n#d>`>=^PfBXImDl1`g4DM?^S;6-qu(*w?QK~p(u8#HKbZn@&H|LKiOAR zp>yANRO!dQu6}KYzK;0Xiu}KZ_2-`;(zY)|JwC?{lDJ-_|0+T z|KEuJcKpA0;C~;8<8al-q5T;4U2#6@yqo(eaU63Ui*nv|ecSV{>#>m^huP2j0qh6M zainH#b~$$C7cZ+qY%qXUBE%S>hUA}` zSdd^MUlizb3tMnCbns9-P;kne0v-}PH1kj^C5JLNIF#nltu>N^gA9(u90@lHrQBO> zuzvzFO+0uEJVtrB;-4k;g2|y`@#NMhU2Xkd29#edJh;6sJiRET!#0%R{f#6<4im5cgJFfyWzCh zal8BLbd@XjxI3e}q*^1A*%F3~>Ycbwe)9_6@xt%bUGs0Y-=sy`5SfQp^@=)?zl$b? zOS~hD5|ieOwwjg8O|dBL-2C4np|wT1_&Q%bnEAHc@a*U7jQ>74#kv0P`j-C0xBM;p zjsBwMzwkHyBmTpGf&UQS^gmU+KI1Q+aBQ*0<_$K{w&H#gCvnGj;6ywaPqIQDW%jZi zy`QhF+2u#Ir?b<}bPE_Sn~@cQ7Cg95X$NuK8ykK(N;~>TVVe)Q3GE*JK>L}duw0tEa|``erx<>Au%Qtm1ny-#R_M1f<$j#{)8g46 zX3(lv1oV#3v+gQb6NXPi6M%1JK)*(gh`hBpZ{7{#@2GdlP3bS1A`0$o05qF4?*(f` zVby@S485Ej17guk&6Pt{=2L%^bPuq9H@I5|b$2U6C+&u^LmJ;0*C@j9LU$OpjR4=o zg5PN-&Z+>#U{X_yXTg+CO4o2Uu!GCD`VFm&e!OFxTwp7G9ne<^>=}-!DN7DTQV?y} zN@&DD1}Q3r@tKdeF)xodBqy;={yP)zaPnCP%sBRp_@8VU(B29gF2&iRtE=>7nD7mN zp8dV8I^w=kzDWoABUeueOs?xz?%+v# z^2K#rE{eEl{k{wv>Edk({Je-R>aV*zL9kD{?G3g-lXV2ObsukVFx9;Z?4S))zqo}5 zzq`BQ`_eqvJ~z)|V>+-2Jtya9`gv))H)t4(3vu;AN!Xqc!htjtLigivgmFHv%(AUf zuzp_K2ZdZ9!xQ9E@1eeZvHG6s?JIX5;IJ>yQK#-x=Y0Xrg>o>cxxhsL#l*cT!y`nsmi+rGEh|fUO+Ia0yq0OM?)Y(9fUue#e7mw?V`G<7Gq7 zaF)~L`mFq|9*Ir`?>oY!f$Q^yy2a-jk3=X002VV{y*(w&JV9yqk~Yllv5G2jdc;Ad zk0{dsEJ~hTzLGftx0?*aDSNva2qJ=9#dSb)J;Vrh(Av5YLhs{!lLek63W~no{gGjxgR^E~7 zv3u{b{NR^1gCAe=LwZP-8Ffe9)vxZ*3g4MSoUVFFem)Z&fE|%s1gEI_3;z@Nl}$jKYh3sA)06*$5OBM zK)IDe@r?{c4@%2Hjo?ySq6B%~H@b)f3V1diYX8TlQLLGcZ`>hNZ^fdN z&iwzcR3+FI;`>mX+sb``us%90e;sO;z;hAphEL#`7xsM{d`9rNMaumS($q80>W9Lp z-PA2h&gsu6kipIMn`R<-LGP#yxQed*3cCh)RY(iNL@V_$s6Yd1(1H}uf*uHh968Lu z9b59yU$R1+C3Fw!g9z*X{i5!>SN+ockL8r8iXr%G`T||Z&;@S9CfO3G`-p3vK&y#+Gwpk?{ojwC(f7d=QWk3)s`>2c|3lHJMS;Xl6iW; z4^U4~LQc3rGc%!9Gve zbNj4kpypM~vIt(!Obb{XwM=@J5wL&a(dhLa*k9DHvu`UV*L|3KKev+J3>PIm#`Y0Ot+~f!sNm!EO&WVB&)htxrNy9ZvnODY7`P4Vsca65hbp$NdF7***?-4~XR!>)86(Hn0ID(gwDTY!8L4%AG0q zum%by+lc}%zfc6{Imh&o+Kjbf18%3S4eFg7L2KmOWj0^{Y}xwu4;I=IdM5AtHnOc_ z^P(3(H((I5MJ=H3#;|KY(hNRct8=G$Isp5n|4G|GdpKyF-_$)6HgYF-vf20jkI%=0 zAk&W{kUuev|Zb!U5%z57?Gi$%UsOBcUj6$#7d?(}?jnX0W!L^l0FMOyl~rGy@0 zOMCG99q_hq*P5_-(R{IDlg6PSMe{zv2cQrBfpqz>vQcP)9vFfyF)5n8ePS81HgykU zp4*1a(Il(wM6GTQEbB07JbjPVEEHf1;_)o)-t(Ry*^-7gF1Y||OIU4%4X1}Ni1v(V4D>rw72&DH_>c{@Q zSL(_|#AUQ#Q28tUf`CHE+9E^>AQxOL^=&GQt-1gSsE`^G*d$u3-@jQEU92>ir-T?( zw20?z4FT*>D)9yg_iGd3q|CUUg@#-0#BB&rEgixXTIF~7 zyzk%WkB9Ug-}@2qhakGreuxJ6bC-w39!-d@BnZ5Tcu;PAewlYZO&GpX1@3zXY6-E! zVmKnd2vV7k-OINdfqvTi}LhasTxbK4?w)mW4 zR>*(>AmC78hge1z%dr>|aB?Rt4#V6wkcX+_(Fc&skT1pa2+b#*gtGAV;PTz@ zyd0m7{`zPA73Ny#wBHSS%89D&@^z4ce1J>m0$<9#Lj~$V7S8_DX&B}Nj_|k^*DuBl z)puA8Hu~f|8KRrYG(GTAqD5nt>6u;|jN`6tOv$tX;Lyi%m;=;0i-sr==2_m} zVT204&R7c8Flu*c3>+QT`T5~@FhZ|>G>}e}5XOUK;~&sJDqf2b4n%=BE|^cS=Ot6COEAIL*gxW>V?3-tHlkh71ypt zHyM`fE?(mvwCr)7{JQaNu5m8}_>tROhN9qOY#HB)p{;N}+QR(oslqJfijy-Ea_+NT zd3!fKnbROZ7}rADwUAzjS@I4#Kz@;6LQ3*fO+C!0`x*5h^9mn@CSGoGf(Nu>(5C36Bv)+eARwkMo*7S-Fh3pjWP_ zydkW6TcmJjvr#GQx%U(74qff7z~2MUH6)=xfmxr%5?QDcQtg`SPT$97!b!r1;E@#o z48UU3;&=rWXPV?XX-+Vw6rSc4<%BxYCr7Ti&&-7-X-Qoc{c%0jGln6?0N&0JI%|iS z2QTZ)*wlRp8A=mgbeK_9opee&)h6Pp(zAtEF%3+;wI=(DsQMS_Mmc;M!TuQ;ECKWQ zArw2sOQR$(?(lrp-l{`>>!lv6=bKW!(B)_G)jyqx&*d0(kbQ==WG}JCwP$<#Kv@x( zY@yk&{c_>=z#DU4XryO8@hAndm6QNY?3_@qtcv_yNjg0CITfp&QO^fqWj+C~nn{6D zKi00w!ny1giHP?p^G^UrU)%Q6*zB0m8mcE-vFpxJ@MSWeQ>-mfcA%%##ascjL4JcE zgBd04&BKb;vXW{G5=*|Y>&ZuCQA?l7fTo)Da(xqrUh<*A^+`|n_`^W0FHubI<$PYJgDg|1QDwYnw3^R_O7AQG z3JJvR&pBQi~vbWhC35eB%0LZIuX7|ACYVU z1@DvXa?Z`#zK|x4H3qbk=gPS@UnZDP zmtQrzDcuhmGAkdtxJOM=MPXW3{SH1T!F$G8=qKV75gi#3N617>HeiEvR0Kk^O@ zn{}v}OUz8QKv1(SFXHz|D*bdHyP`+_BZw%z8Anh0;j-~s+@6lGyMHw2-Hw7|;U zd6Wvo-JdJZ#MSh3(m?aCVfgI5>Qa?WtxV-1C?LmeQL6HV(Y7>=7}vMb~Aj3pi5Y3?zl5D3FGYvhVa>h zm5QkDS$Ctc8t=KaLvqsPIkbwl?&-EEU*3-?Ac_RDR4m9N>W?NBN9EN4CG znJ~r7@YB*>Iw*I~HPurGJFVQAT5Sm6E@VgDS*Q&gNo`8gWA8OOQ%AL^sbhm}w+|ff zH~1{M>>Gu~5Kq>2x5cXDv$S%+jarniE3MyspUz!A(Lfh!p|tARu|-C3OdAsOn@`3- z)F)^qec6Y~LHOcpf?}d;lrmpZCkc(3ufO(TpFBcymCcUh z+))gF)a)u$+TU3a2@K;f2ZerKAstq6Huh6q*)))^qi+NPNbPSeG_~Q_bi_$_Q0nJ57laeyK zRk_Pp<6>mKAAS`X=Asm5Ht@}-zB!ifu5@ndIXfD>TX#)yhmp66nG(9h34KGQCBZ~; zayNN)K(P2K42cF&37C&uC^DTpU;2nf{jh zswy>jg|e$Y1{ovv=|Q^&&d!U*+DHd>wRZ>YJGTC<`hxh0K3qb|Ocn0#P0~m~3uB$G z@%z_esrlsP9yL8bqaX3Io6_io)Z@MEefG{jJvn|tMMg)C)5g#oAfigW6AZfYo_OkR zeAU0kbF*KpsouISmOPAvN6(xYKOHa0X^I1+CW^@kPCVRLyNvGh!Icq4SxsB7&K6Ko z*;yGF%O!c8lX1Y%_yg{1W_V89T(Ihs5n%z7#Jz=b z)*_%IW#f)Tu^du%elD@}w%$P4uT4N8MNj+4YajV%t%nGoXgKB(@V!ErGOwsqIEo(O zXY;vt-A3uGxlHax6pb*?X}l?-F>t=-H?!q{64YFTsg%rG!fs(eF0)mKksloY-9I#a zw6uTehj_;88Z3s>4p+}f(kRe))8=7be*k*6OA9`Rlyr_jrgh~!XWw^VHoNHxNb-=% zgOy{h9u}dHR6i-lPN5bTZjo1LV9skV5TozWx$NjS@l`ABYEf5amj{C4pNbfh-aw0xv`8g zLeUGabMj?$1wjljFA27#(gih7HAfLz5~#>R!*K@ll2}o20dS&{bpXn(SzLhF>0d46YM6x+57phBx5o>@ie3Bu;YN*8hP@#X zLwvSYzEs*4Mbow%1el5+esrmK-G@<5+0p4%T%sh9KJzhh#W5>|y5J7SUZP?+{^sl6 zW(sVrKT!`=s;DqayPS(~cgsUFQ7smiZP8ggqylqodO7Lk!?J0|X+GIOS;Ce!Wv59$PPzE5KyAZf&UdJ`smM&l3ffkk?5!LEfmQ&t)W2BHVjbxEJ)f z>pXA<1$g+EexplPD1rhvu2R?6(^{7|8yHbIDGZZ@S`mjlxP!Y$fgAzgUqu_})ou#V zfN*$BZ;l6I)@$pgq^mxZ?hw%!m(BEvZgdR4srkOx6+v#_6<;44DkAWt@$Zo#nEZQS z^PBVQ7nmH%EH^6+=!@fHD>B1#)W^j_;hRG&z@aROU0H9ffW zWCu&`z-RGtedc9c@wr&s%vlL?mEDrav`xIypohW&v81l-gc z{<2P{vYdZPoKaV9mG3}iLZa3NTdco*Gne-OZ*1lFY8=Je>!pta_O%%YGp)uwmzaX) za51b>AX7qRI7A0xfAF}m!Y{5G^7&WSgES%`xukCyl}p`1XTvlr zlT{2y3G9fUADE5$$`9=g+JvR$;tFilkk30T0z1DC2w_mQTmXUq|04B@VW&#rowt|M z;u^NSj_R7b`>`xR{-F@s?bc=OpWC`#M5S;5Y{=^yN*#ar8Rt!q5Y^pnq@FXYNcHE6xo6n{j?Ri=^$u?(#!Cc2A zL&`(Wp+ibsI!h61r_NARAn)iz6=}V@uKT#B-ZfJ^r0Jw~PGK~-Um$6&_9sZN`^^b?57Ms5Ua&qg^<{Xk?a~Mq1T3^IyeXBESSGt zeeFP@Nz-jcZpsa$e$KrJDicbRhO=JVN$3v(U(=F-orUK6z)h%Ud4*lc>=}qPVCdNl z2M|q;q>HE}_9AZft2I?I{+{BpBVk*Y@E|Zhb2^c+j8Uh^J}2m=&CDzBU)w^Z_!AhN z4#s!Lcs&aA2zbt$i1?rYea&!vW;jNigpmPDkRommBTU1HQ!(PJAqaEVFBOGK+~1aS z04tffF_(d@Dd$dJ2z*R&fH|hz$=D7_a-tGab^}|iT-r!P6HY%eGx-3`c`M;A^( zKs1qjCra;U1EcO_W{4|Y3TFNNm}@A1usl&I)*Uc!ql6BS;Q%2L?%P|K6-NDW28oz%>G=jQyK9vE^$(>9%rOu^Gth_Dyha=3dMA2qrv>YimRSZ!Siv7Hec zZ6NI}5Y8M87%3Z+vuRDaY~s!)Z(oO9>BdVon%1A$%H`bltuDJO9VN9DbAA!(T9 z=!DeR5+V~GJZltM84?ohB)<&s{I@H>d^Xgbc6L*`t0d|PX`vjFdZUY^4!bDRiVDJo z`kiigV2tRDSh4aPU{(P$W_|#ugqRV*EDn&x5TZChYi8zB9)z?ai9KM!4I@U((B+Is znzClaHXJ|OGX5#^>Yt#W)XvZ5_rWc!mc)5_mX$aP?cI)#)RweM(PnoJ6Hu8ucd;@9SjVbV@ha=DJo5h{^?UhWJQ*5Mzum`Gj7wS^mc^RmSIoBVTQqs@emA4NIzI{C0 znn^@t44j6}g|-eH1#>{_9e6RbN7RoAonQ|&NT`mw2T$okXEi(X$ztgm!jr_r+DO1` zGJWf6t#-yCQx7?8NKayupd~}~1lFMe=TMT8R|%~>;LC#AkK9TnTcv{ypDPUbaOS5C z6`}i28f4>zqS@|9c)mU1YWmaiW}JwTaC@zpoVU_A&`VhQT4v}PtI`SS!GbW%UpK@| zM7DK1^D3q3y+6fS-mi<$XUAEy<;wHOx}Y#Kg*Q@JDVaXOX& z(Et9K@nG^Co7$UVVX3(uWZYy;Hot_YJF?-Yg}9|f6}cyG>chTUv9`}VvOm(3MbwrN zhi-1{vctX9K0-NucfI6Csx%49liGk1^(^88JgkKiK{_s@2u>1URV2<(#IChY*HOCS z>eCId0O>^+-h>YH#S=ZYC}8N|yP*BVrT`)VIrYkU0J99o4f2PeFh#(Tz-pZ3P^6Z6 zVZ_W%7mPqj^XSuIC4^~&zRJW3S*lTUBN7uRgfZ(WVM;N3Ass5XD4}aveB2P2r1!x< zk`dBb2t~NcS_RR8j_uKfsR%P5KDO=!mRh;&o= zC?VZHG%eS*r0-LL3BbjPDgFxpR^E> zE@Y^mNGOkyG~=W1v)liK3v_So?NlAUbQmJedD_jEHP!YQOV;!nB5He%F@xT796SB=zDs#AOia1-L(oBs@kfdxuE~K(Tw9r)Y z3N4=o7GD!M~&C~;gVAWS3 z4fF{BvVMWHun1Pz_DDqqT+Gh^!;L7&jP0He%)kKtvk*BYu?`~U4MuS&tbSG&5biPM zhXp^)5RqIft)2oGYO7oA*p8Uu>X?kXHl}f_-t@(I!|`!@hy*)*l2QrMb55~;b9Q{} z$0U2|=g<98W*k*(t)%Tcybq7$1g6Mxw{ucmp|*}np!SZCyv@(9lvDb-_fxUMC&ibt z^|P^Vqn0@g56UkxbiC3N=uNebA($a87a>%C0%g6EhIw76zT6UqT>hMTskidgnTctQ z-8nip6rjs^D7>v>t{7|a6+P1P_H_*UkV*>w^7it^)vU zQLAuayhZRTCD+}OQv33Zvkd8aKf!4-w(few@8#Y<2lKTPl2?P>deiOt2jd|N4@GEx zb`GMjt_N`f9?$(e!D2AAGGrEK_0sWP@+3*SToBqU+3CZVq?}p_+ZvZFXuu7ArDXoR z6O^<4Sl$Rxl6q{tnH9858Xh&$#Wr+(l!lGk7uth{YyAIiDreZA9+4r5v_?)ty=KNf zW(NJY*QzV4{^c{UftQ+eNcH#uJY?X?BPX6_q7>X^DK2pVM!<0Sb2^CXYtB^$Eg&DKs@2rY6G(uO_A$xM>cl^oHtSw$x1 z33T6#`bB1HAVZb@&5{R+k8|Uo@^fI$r6)j!MjS!?86GkQ2Q{^gefxK$+`@! zvW(1J>sFQZi)E<9qm{0V3VmvpRNL)YMHpul8mkTW;V8QZkn->Z@Bk$8!TCc$jS7|{ zx%6ZoOuON`N>cgy=#-0KG)pg_pb{r0j>yxQ42G`8s&OoDyY99c)po|WaJZ7z*thb3 zV6n};Q0=Aj@@P+Vfp|=cD*FmGea18K4xFMF{81Z2Kse)#Bxjg%RM{A7D`bE5?^eYA z8JinD`i9Zr$)XkSUYU~7xjo&sQHUJ>b7-#N3+O^o`t=R?7hg(N@muO_mfV)n?|Jew6qQm-5NpWC8e+bw zvgu@1Nn@QT@e2y7;8`5&QS~k)=mv=}97jS7r)b0&PS>q6j}Z%zQJL|eHjX1Uhf`!r zMny^5aqFulZi>Vi);9XJ*fN;sd(C;?WOx(lbr0vF3nxf~7i@>b7a>Y{*nhf+G)b=N zSs|#dSix0(vD3AvX!c}8{tiyv6T+2UT}O8HWJXAzIv3l3oi$*WYq3Z7Y_U6I&68VQ z;&zp}lrAb@`gLuS8yDEvz3ZYIG}sT^vW^&Q%=4PD!mhe3)kF9yQH{Cvf+MDf#KInXW=}ssR;T(@%*0dT|Pizw@p|esnTK5G3X8q z|KF4byvfBZwAYZ;0Z^B~%oVwF_AiZO@G)EVFS z9oBD{fD;`t3R>#>L8!634l{FbI?U+cZWsXmh7jg(7)8b7)a6V?6#vym0+%+KeJ7s- z_a>$4!^v8EG>jl6+d@A;Rv_*08rVRunYMa^U!5<=bIX-Z+e3bAK=3O(C2baq7^_Lf8rpRv*UK#-#H&yYF1-%&7Pl1RSyIE zW@GoWU-Z($Zm#_-Htabp8IcK5X#&U|^g>PTEH+Z+6-TKgy2w+R6Y-5?&_W)P%$Hmh zlB=`nl2$Y2ON7JQ)J0JVVUj|kkiwl%OXZqCngEG_OHYO-`%}7wnZ)=rkO{$*Vbcr5 z^q6#S;9RXylC{j4Qi1fk()T~BC;TX> zkpf}7AX%v(v5yrv$g{mgVF%8pgh}L+M6i*5rT5-9+sTBM7c@4Eiyd-Kp1WFnBICK6 zWRani=MY2lq;hHBNHu_qc{I=@^3g%s>{XlfgFE?rL%>&{Wc8H9nK^4^vEAja42+gI zT3|?mQxp z+b+IH-}I)qF=rbuEo$0yu)Va>*~NhMPq63sXUCYD>yJA&B>oc}KHjlcuWZch>P+6+WH>!1vVWp)h;nR!1uBQT+Ni8#Iz0`1+67d?ny^ae4;D zT)V-Uv%566hV9R$!PJdd{Gxl0&lX)am<4%^MeklNzsbE*xd6Mxat4q{owR|R5dF9& z2zZYR$H4N>9O10Egr9B*AAGFZdDDQ)qTsMxxR%70-qPf5GZt1Ujx)BdRum2BDScBj zocn_gAIm|=GXy$v%*&+xQk~}R4;q*3kQ-dmrgVq7dk?F`H1_H|(Q~U&ID$nMM4lQP zEOJkp`I#JXt z#ZIOz*4Quj>O&xY4cluMIo2u47&=w-l+Kz&k$V}z?121LR4lwsk-JyRb(lf999$d8 z8_OKYO5`CS1w89Kb6edN=n%8p`IiNWSiNVwI3F^;J`wAT9T_9s(lG;CWXXs~Oh1ls zFpIpBM(dint(HkD`_~x+N!`8@b?rf(D&DYg`5+ZuTDzLS(w!%fQw}XxE7K59;wT6? zPa+`2D_28P!Fri2rb?2&T~%Q_ZdI3`Q^i)1X5s@Qd|YGcQVPm*7UqSzn96oNbc1Gg zK}6B&PPcOptPIpA1EnfRHwy;-bXNmE5f^5kHwTk=RViI6k|4*XOo4PTK9)+j(j{j| zsFjHs_iM1t)gS!GYMz!D4?eF8TukW5FC9}o@_9YjCuH#tF}BAj)1k8xA%)*pvf^71 z*}{L83PzqogF{<?~p=3x@%WabspYa6wBM@*wkV z?CxpcfFbs(hQ=(Gn^#;S$xn4~eiryVeOt55naPi?qq!ok7v;s!E0z~Fh~$)mfET1N z72!2FTI-b{kR?{3qp0I6C1njmYTG$l0>l&wOPEi_$ zL?GelsiNT_qSWq-pGH~N2f0+%Y1XOF2<}V2YaWS!C+vuKNu}F4h(W5|#ItpHO|3=& zod4m&~b?-L;SBb5p!r;iq8ps_B>+jXrz%>Cj?4cQ6h5B zFkl8a=u(ScK+?HfT)0VtfVWIssJqJZouHoEQOZ8n?%>?%_6Hoa-0-Bh`?WU0I?18L z<)yKPf4zgye)jl9GAL^w`5I?>?BR-0!%dfhKV|jdNL2mlKot^-gP$Xb?`#jeZtDsl zO5Y}s5-dJYEW2f?=`?1V1x>6oBne z!S05FXcz&q8^ksbow!rFA0)VbCE_eeC{2@UoNkzr8Zh! zj?AY_j2|@M_i@q_Tqp3uUl!DtnESuT3Jryk5R0yEW^OT1_G!}NrvoUGZy02)t-@l1 zMZzxQ@5h;9(!t{#F{ypBQIWcHyGLra+CH3Qf1v>86uW7D-PeSzH@2b}h$%E>iZ!!5 zd<8AGZ-K*ge!iFqlds|CpuUEa+5s%czt=E7=9vWfa?VE{NmQ?=ecZb*<4SD*<`cXX ziFs?v?{?+GhbpRwM?T^0!Emk~AB<7;XR;ku+rG9Fujk&ZQbzEOzpIAj3V$UMbsU$b zM02%6;wr8f+O{Iuv7p$Lu^pv!Q+eyFf=RW*5FuVD(L;8*XEt~c2|CN2wOr5*%RJaF zL?D@#osO{Z^hJDJZo;wMQ<-rJ5j7qcOl}y7pOB-Xs0<&-xD3TZjxq72^~S@*!3yh^ zW7Yyyna->2%P>Vm>^ivtNFNi9| z*(fD~K4;`S)vkapoBSF7)jT(!8>z-M39@HaZQ0C+nW7n3^YeDP!^Zg8Lfl9FIyHgg zlXuCC^B~zb%?IDG9wOAwI29zSuYuWEKV-S=(w!`S@^`(Z_xHXZ@%wlaU7y16fOqZf zG^K6igrzN>y0o>N#B`{LK)raS(CYm}{&X7>1lW0a^eGnFi$^K=?_(E9mV zQ^g1xlR-~mQ7)U!746_q3&Un_P50EwfR$zadgpV${NvzDF2jmDev3&)0!JirrA0YIb7^x)}84!s%ICs;6$} z7v2}UDwm&`R=A-SHeh%{yukXJJ=H-PR8$sji zcisuYMKynh#&)n zh%#=_n(lY8@qfR)j2da{KvaOnA*DIE2osy^xPc$`r{ zwxW1(zIgNI!4YG~Ffb6Ow^Kpaam4VmuYzFxcj`it+g4qE>du$4h~YZn&BQ?k=%Jh5dIl7JU2VZCBG4i0SgrA~?Pjv^5;V$b-Pinbo7CNCg;*AtUWXuQU^ zleVNEvikF^%q4Gyy&&7?rO`c`<`otfz+}(BV-oK|( zefz(qasMfZn{WPmfn8wCb8)W*^Xt_q@>gzcj~wiy7jSMI|76qLkwa6SI2`3G#H5Br2k!`Fcn(cOd#WMjee(Zp94yB&bK%xF-6| z`GEn-dI*F6`Mgsf3Q*<*kJfst{yPmUvX)xIG!_Fvh~(I=R`vxCu{ z@yM@B;?ZfcrRT61<=#+jfiS%Ry+nyaffIX0neLF|JPl)2^zNY)CxgZ9VvSa&z2UGu zf0>iw>zqI;1(Za1TPJ;?Qn>i)Ky_oMeK1;4Nbwq5Y{0gmAN9kq z3gboV26=JnHH`~U&heivU0*rj`V^j|(JtBGBxRqB`;9!`UF5mB)a3Se*SV+xrzOcgXN+&|mc z_lhL`{cnYDf8=^Qw19}u9#6!j8&7(!M;4OaOit$AXQzW~o;w~RTl+WAKiO#&f_*^6 zMr723bs~pbQ6V1&XVq~#gC@C!KTo7+*Idyq!TWD zIrXbG`S*i>$JF7c=7p2p1}GaueH)q2vzs3ByelB8_(RQOEqT65$d z9YGOYj!K=D<;*BHb<-qg_qqfwoP$Mr&ntt;(JmJuXCI%2C>M zYYf{JXY2lFJ_~WMg68vSo$Nt8KIRUTILC4p&SA@Xd}{}+DL#dldGRQN?nS5ZL7~3O zfoR)Wqq=GX0dPo{(%H#LMLeZP9&g2T9_w2bBtKEW6P%On7375MZ?mP;j<#`)w(`PS zZ_zgnBd2wC)q19uG0@z?9j2+i(>VzXBGN^-xTB&yc=IQD(mWbdTT@a~C7p9fQj4-S zu3f&wSG;rRrG{ONC(t z1l3a`ke6d42q@k-1Tvwi;8RX53D(AfgJC@M(yMmWcIzRb99MRz;$Bo@jm7jDa}z(` zL8_)@*R~Vj(P`>Vu>d2f%*)h6U@2WAi&H(EoMt0KjH%gBar!BY!#5lLFil6P2Q%1n z@jGq2ei?NX{-gX9)O@5U13}bzr1QpSPjo|V99M>L;_D)y<0SOmkwCLxBCoepbGoJBrpuzNfqTbLOkFDjN?Tc z3aKIrHx^PSo?OEaAoPfNueyf(y!wbl1nDIcMkb1_0Ne5NV=7)>_ga0vMm2K|)Nk|b zo2u;P-L{i>q!^l#X1VSD$4z|#2KZsaBr7?bm6AQ%zqzPfEPo=uT>vwpzX}5rxWRGE z$%&Hu)4V1AxK-NB$+^NHk~7sM!qMbuYbN48wILR`iD({FVp~g7X3`xA-6pi^wio$5 z8p--y0Cuz}FX2#QKLs!1sxB^CXK>FH+UYI*$PP4#u*z*?J7#Yn-{ zO}gfQ`vUdjG1&ViNL$auyoZaCb;lt|I)WY2c)CWPqAc&wW)wPJyZ*Qz#Vwt6Hs!G;_xf0dj0qbY=6Fo<7kX&Myj0PY zPOeaeHihLFqwpaToB>2CkwG*tTW6Ye+VV?BbEdtAi;RyEt|4tTj8MqPNW;T6-Ot1A z=nscp&Nz*~R0v{)i%e>Az~9P}s%VaLjpc?)LomZxN~~kJ$ff`d_}&fC;ZZ>RM;F?}7IFLHPh;burS;qbdx$kzkADl`Ah= zT+}Q>%g`LgXG6!v7t|yoGGSLVC3B8F7m?nKc}9lsQRCddkH+A&IbXB<49oP5`0Rz* z-20O9j!D0t%`!RXeT$xfrLl(DFtQ)#IzuDNC9+@tc%_J#qVWzL$dVJSs;Bq(dD@D} zdqLK3%Cyn`Z)CI8$IU_V`m$EVUI|p;^Y8YdEm3`7G2yfM`EaH9b8SBobpd_x&2~J- z^pCWx>6HrXMCT%5r4{KYv5~fqgJJOT?UXj}#C04jyV&x4aGdC5+g`^WZhL5FHaOa9 zK>h;z*Z8}x)myQCFnM&z*;91B=Hi_)(z4yRXcC*PD9Rm#bJ9lc)u?47ie`Bb;V|kG zF1|m;+WudC>{TNacnxvR*ApSxEF*_)=lDBw@*`PV82;IP;$t&mhemwYY|lEY9!U{T z8atl&6w%?dozv2=}6rPoCfu$WAc(x_SG!fK~` zpES^e;Zw6d!hFbfKh?`~zVW?YXbtarAal5nO{eAKVF$9nYE$8ka#OBmT%fb(YKI!U zI~t#8rTE!@`QH$bPM7NShTh(oK$ivhLG;%-c=KUm59LjwS;5eU^ATW~z5ikODE{@8 zLW+Eu72Z>Ce4x-}0?9%j>Y6eGMkaBYcbx zu*Xyv-8UJC_L1*KmirtmQ(T4Pd(W)le#G+^z+IpFa^by)3#||LA3^tMl2@n;)#Jk3 zu^(44(J=7hKj>6dbRz@+2DuEN?(0i&*7`8|sU}`Py62y5^+_`)<2R6FZYW?aTdxDO zwvUy)Mq{t(%hcwA-~=o7+>3MQO0mqe zZGb+z+BjYO+}v+>9w+tjN9IfU2nsc)&37Y#MYt_1_QbA-`yoJpNZmlv#fW%AX&9C8JO=lE3W~4cy9-O`M>K8pxxks!=ai^W`Ekmd8R2po zw;#Xy*qp9ciSbdECmb$)q+i@&+Eav_;HpfZu>LW{*Z^*iWwATLWk!(pbNjHcwCjr% z+kM_X*g@(rvZJH+rd4wwV!h!7`}>HQ+!tVwHnFKwu>fsA=37kdJc)%jzAa(N(!Ti7 z&K(|jPefniJj&U4Q*UFkmkdZbu2)=gDY_k71N9po2P;Sp3!2T=Pt|?1?T&$c0u|4h zlwdb{JNkTM@D;2f5_xl}@eSN0K05}@@pnJVJSSiXMv8$ovBAR!a!;5HHY}nf8_ueu zWRbuLO;agH+^l0yvVe-{-clr8c2S*P?h_^ro7PItzwH+;N5gQvEsEo}i~5NW(%-6y zRJ=GRT(?3W^_8}@{9*D6E9Y(=nlBW&U!RBpXR39^kNHOq$f0h(qzu#R$m4-y7zVSW z`E|@23<|5)X*_033he4g;=UX{`KCA*9^If~H|CP{@Bx1XYe6&T##B}HEIudTtoBEa zOMt4}0h`(vnWZ(goTsKr=Nyu*^5UAqhqxZaS}zv0FiQ%HD&f$$LBsygFhJFWi)L7X z6j5>Gs>XRh1Spw>zGMUc7G4qLOlW!iZGY?(*u3%e53^mvWRE{%)4h6)?F5qX(p2Na z$&pD-TfzJ@HQ>|J^Z?kEKy?<-RA7dj%;{=F`!XJRuc$N}G5*91Z4nbvYd=TJWg-N1 zf!(5lg+6UKZ}BmGy!1ex1ic#_Mdc8>`FG*D~y;1p40}TTNcT zh18eammwdKdib?*5hSNka0POzg|ea|l`*&1lcAzdEQ$^EWy^-b=P_7tpm<;Yv+NU{ zUXmT>{NKxAFlXK{DpiWM;x_{Z9}74;6N6I)DMNJ+ucc9qq%>XNu`|7TGelodaxMWo zH~O3&|0gyF#8J^4CC4ERP2py57338emdI8t-MV5&C2aH zv1;)t>dr28E#|{LU`4lA`*D>=#Ze$WzW{_j@Uo#N*_;_ZG2r~&IHKf!q2NWeNR<<) zSm3Q;0O*AF)BKkEjT%>N%O)3a6aA7Qkl!5nn-axRPE3#{osB_U%FCNsXSYB@5 zSC5r_zR{Hz)nTLsyB+8*OTjjz7e1bW|<`=L!pM zk#=ywi*)VD(z`fPV-wyvJ^A9BC-u{IR0Ath_284aEv!D!X&>{~=6QLNjd1(p?dIFX zY@oZuA-O+$zTls(J(2>srnG+`HW%hKbyG56EJGKbK0EY5a0iP*>CY0$4sYMhLn`^Y zjqU4YBOEW6X>!m?{`jEvEwcUk#bvfW<(vN+Yk#6w1UC+feKd43CTVN)C8tEdldI;Q z-l8-g|8fGAGE5%S_~A~oeVeXsFg}}rpLyj9ZVq+KS2O3c>;id?Jc>HRPLtZ=q<4bmJ*!tM( z9}q-|&i^1iW?;1JOh0>RG{#<>=JH`cOD`+DoMm@$-CSi}@FuT$K*8>PLB~>*);BND zVdGrL+u$!8Y)F&TE2m@0M!)oULR~GkR`$NaXCBDeOm{GSbWt_s)d8;(r!Ujo<|!zj z6_1=jn+^I3=vA%43qnt2k;XIRptk4zgWm6U?VIp1h3%d7_IDm_`YuQ>1oieMjk?lm z4zhW4z161|(Ks%C2<-x;$aV371#mJ;_st4~k(&hlsnX-|@v*fc+$QtJEM4B{@Sy&o zsPQJf+gy~Whqa0CWuF<6t6=`Qs4$-&aCZ2QqHL&jmDJ?l*^t2%d z3Bc($A+R6}Ja^KQkkD^?L+_u{PNQ#DG_gqJDv{f)r;HwL`X%RgIXy^j?$U9I1rdMT zmN}5$F#AG2mk(p3zP-+9M_Hwte{Qx1&kbcZ3sui~_s-38>d&d#bqCL`O54g%QF3AD zPm#%e1<$V9ZBg=TZ+6wF@(P?;s4hHoXh7h4m1gszqxF+d^eCMaPa+ETldl_L4WqkF z=8vsh2(8(cgl~tn`s&$BJZ>`0O53;!s*oY(_L*bG-E>AA=fJ+vwKa3AtvNidbdQ|y z{+dn5T~D1zmz(F6d~$Z@N~vZa$UL!U^K`e($8mPGBb3VUHleMEFy@#g`9sj`!JCD7 z1K>i2EszpL8Bd-X%sVy{#_2KGx{qgWACO4NofdQ*&YVzzapWTcJ}Ka-_M5l?rj$|s z4^GCE^PMr{QQgQh2WM;q(S+F2pChGpX++zOdwhQQ9g3#YO@S(wM=GZ%0fduNMsm)` zc5=xOxm;3Dhg?ayW2o{7)xf)ai4$;L5mZKW#e8B-5|^I?h4NW?OC_>wY-0XDyLOS- z>bVs&^B=j4D*~Kvg&;^&h5u+X8kyyjP;od5cBZ&L3TO)LwR*;vuS%rMRCFTSxk-3A z{B}q=cqb$T$JC(m-ED|TpdD8TLp_+192$BQj);~4O4ox|v_wCLdaXoBV-GmVQofmt zZ(z@dLE(`7IIJcXA~@a`LJ%aq!~cjR1G!lt1IXR`x}gx`i!xqhY8G621xH+BekzOt zV6NjEbA6LF2yP{Wq2Q61t||>7^*0oQ({ zQH{Asu{RIGp%CB@@SZ@7Rc0ml958&ff-LhN2)F>3?M$2uJiBg5zArF=Xh`VwY%x3GFu6I}6r8f@A8*%GMl_6L@ zxPV;|UqOZgcbf^w6t}SZF=krC_vm7UVF`49^U^X{Y*4@S32ERgk{sd;27GY=%udiE zJ0t&yyQfZw2gfk@Dm&q;(uY7w}RXXUUzAzp%fFZ2i{GKB3qtx`Z7@sAM3sO;+mHdm8 z6Ea$P^)XZk7b5#&#uzv<-Z)r^EPuHMW-woFg3xR&R2Af$aIRAvlFRi>u^-H%@*=S+ zEUIO~z`CT-Q)MODDE?B{1cVq?>85}8P*2L^W56?py^C5?YX}bTfG1ye1J@Xe6eQLb>=gGDwlg zM(k<4Y4cNaTC7tTANG7Q-{ikqRHMVhD?_keFmILX{NL|8db;^5nPrCV5`{_>bg(H4 zxblb#EVd8+sv1UU)v3U5UN>V8k|q9e^sB!FRdgW+lE-OQI#1oioBdiP!J&MN-R$yL zB@S}VU*FXie<;ZhYbjE^zl>qI>Qt;0!VIyYqYV zJK61v<39@8?CQb6z2C0Lco_U7B+{ap-CUlqB?@N<30Q2;eOq9vM-q%fBp0I~WApYm zwec{;g8(0F5<3G8G)XU$XgsIql9#(L1!dq)u#qDLVe`0fy_R4Kf&f|uQ6-zRZ`LQT z9qIPys$tR)=Y$-O=(}BhHH(Fvlm(f1B*ytN=H$fKwKS030z#BcS%)ngQiM->W z%`5jyi-+byhYE0XgHROUxwa*q&J@Z$v0_4)o&e6{E6UoK!8D1~;=-UogLNajiq! zd*FMx5va&%!6_&O4R}NNf9RtX!cxUS(OLIw@Qh#0F&L}Dny$XPp>$w`^$RsuZ;--O^&qRQ|L;P-+ck@Q@_h78hhRhQZ314`O7u#7D+{>(12%L2(6 zE}sElqGo5s$(SrM#|Ju*A;z-13Uy$BVue7^SG`|}5^mC1BpJ$-P2x>@0dYBgB+fy3 zjqscQJM~u+ngo0k<#&Fm2W1WT2czqoG?1>Wk0a4X-1+D--b?+pLKg)8s8XN+P?L-H zQpgNb?lrA0HG&;*FDHz!UHx;xVGd&aOZ*}ru0!)Z8~+;o2l;T9J6~SRqSn==L`q|~ z-2DBOv69tKE0%r{8&8c{(@opsy86!W*xM1V5~tfHu}zz%8`E1qv+-?AEeakWQ+j7P z!aY|4!YJvG;LnvDkFmvPrHiy`iS$lB&l^Kv`turJhW<#NR^Q+btGS&s|8~IBm`dl^Hb}o@UEh z+mQhB1!`=ff}m;gXuXL??V3F3_jdDAs`^qvb<%}#bQIH_GHZ|KOY=SD8o?XwQfq1QE8Q+J1YrqqXb?98~~v;7j4 zp^p1BAxJ&6lhMV^Ah~OEIl;Mi>#*a*Ow~?oKBCbOEAi^Rr_&?RL&J-kge={RsBHJ{ z(c7*T)YQ8?X5FE3_83#m`*^-d@P92X07Lf-Qtw5%>NiWtwLu4j(ubkEI!hPaa@5wO3vI*@SBn z4zn^rK-E;!t^G6bR=R;hJxxIslmmzu`=)NbwDenK9P!nym4^oiy_PjL#uw7Y%aO=Kncx1C{yQ;d8VCJ34gMt(96%Ey~N zwfvm8s{FykhLkJ+af%ZnFPIMQn^0}X;AWNW<|u#F%<_S)q$}}pl6!+RgC7UTDoOW| z;FfKub*`U(DNX+u>Xhj0VI9x9i}l%7Ok=)1EgywXgi8e`qAu!0|DNr&{xtsTWK%-C z<>?s#GV+H!njIJI748Ho-P;Im^m%lDsW{(xD@$XVgKGdBBGsY{?=2iI5j+G zDq^O|R}uX4u<~qIzh6}DW#F7W)Gp-^65@#z3k*w_V-1ggf7X8)l|w!=R#C7TxL-;3 zxT{=qM?64H%a>c#F3rQj{c&zr7B?hk#3-DkqoYencB$LOC*~=f*)i2>a=U=^Pvs|z z!5?h=-}*cfp&E<7GvQH{WRB29(*ZfRhxPkz64$5`Lrnr3 z4Ipoc?8YG^-t=6%?TZPKFU8T&^}=uY1b;>>{&mV|g&d|h$FsIxd44*ng-#~Ns7?{y--U?2m!L-`5X z(#%m_2lL70P#wLEJAt(f$#6a;5dIlO90Y;}2#8R^b1~d%XL1+*?G63+r^6D*DC5fD&yYA2h!t4o zVAaBT&PD5}%;Qb#rpVv1kyPY2#6V`&G*-0#@u!eGZ%<2qjsD~=1@^5(jd0<{nXefn zd7b`$6Hv44&%M>hkZf{Hss~_~lhOXFb=5n~iPoH3y^n@oR<$RGKGd909dXC%b%e+= zt+V-5ul|VC&(x5P(rZaNsVwIf7fE&F!y6Z1^si9UnApRMy}06v6a%})l80It`ZkS- zH0?*s>Hlks^6~W_DlnPsD0eZKQ4i6V{#h+*k-Lut%%zG{)gPJ%sVS+Zip?n!IAfh0)oAV%>x z?;=`0r*qfjC|=5Rht_zEiU_?XQl|pTUeuHw@#s{Oq~#)tCJ)b99OVpc6Jq!Rxwq~b zER%ds>{lePIrRg|Z?e=or)Za&h-j0lh+20PY&~X*@I&_-*|(8JubfV>fueJ;(yhVe zjl+kc>GJEe90MLTo5lkiV9b`I8bl`4Zmw++4(X_jH1QgYcm8VG3r_8Ed)cGy1f#5x zP)CiLTn(LMo6v$oeU`bxk8ryYRc(g+;Gi~;CX92UX)q-tVq;@~cF?Z}R3^m1%A`3E z%5C@j6U?!Eg&9b{!mL8&A}tAHUvTyY7;uKY5mdgR6hgLF>hC7aKx8@~$1b%I&ER;wgHE0?$2ub?5Hf_O(O&xRPCq=mzS_baUkU5#OM zaWZvyaT4`Ym){ysCF{ZEe~Xqkf9UhIlC|bP=OA_)H84G5t@In?b!|H;Hz z6Du=(Fc_&r=Eku>Lhn9lz>pAi=*|=Az2kODl_1dzu&dn_jxD`*KRW3`;|Lk410rn# zxK%~v?VxH!M}@hmz?GhEpWD<`1^YakUu3el>bG11gSXw_qBNpDfpqPeg^bm8XEk&G zx*7kbPy4zWN?CdpWY+c+sS5==-J5HOw3{m1(`I#^Kj=@ay^0j65D*({)nb0Ge^7Nj z@_~yogmY#K4XwX!rA>K_j;0=m8GcoII#v}E(fCcLaC1>5vVnWIX+E;b|w?iZ*mBHfAc34Gdp%pNA$NmCgnGtL|`@>Zr?Hy2sK@5Bn<=|FH83W zsWMvAg$Ez56}Vcaw;kWy`bAD#+b85xWD}baAykmnpIsIs)e?PiH#eD)Q``25Sg&HNoT@*) z=+dHFHs*>??Djfn0IXfHuS4BJ+aT} zhEf{4q0$r+>_z_GSU-R9w#3GedS}D|A9OiNcwfv{UgJ@fzXhKpSMl~_QCAyUNE~ec z|4WsWPKfM7mT>Ujux6t3kWLUPxWSQWfjei}$|g#*|28)C4wAQHqFaP$S=Dj8=GC2HACTMESY)PQwVy&9 zy|hi$QI)EXV0EhgIHjC3^Wkby(p^4abKAaVRbL{F%f&%hhnoY*W8-b)ROqNuX0Od= zq$um+933n*-5r}fe>e8d$Y=(0==vxBL;VZ|vJ9t~a;x2XIGRr1LNX7(>$$?6o1l9||`VcTB8c_w^YmD9JIOY`TS)p0uZYw*_-t zhV@;<$&e#xWO51cHH>uGfXJGKeZ4Y`(4o}ada~lZ7i3a&-5c=Tz#g-XLgt-Uf%qgVn-Sz$S2r(n@1Rks z)I21f35hf$uW=2d-qYXD*+B;dtQW(oaA3; z#7aRON+l0GX;_?}oMmIJ;=HG%tVq`z03YNpH@nu6N@!}OXjQgwncCW5Ulhqj!q(27 zq1Cxa6+)GX(gkM`M3%&LXzfX6j(e$PHeTu^euOvFOYENVFK$__UDjpeKy30;l%n3k%G!uuB#^5 zjlk=+O+>Lod0GzLAJT;bFTwbO7?y?TCgFh3)$rh6*+f06pOazqnEUOVRvys$H(=v; ziF-n|Kt9O6hzVX5kR7H*v#(LqcT$f)#rs}Q#A6!L9T?|jjS3|vFStHG^whtP3GVxZ zR|e$xusdF7gvihS zk7kf>(`*HL3@$z)bQ~2ERKU??qjy#^ZidEu=NnOtuU0JN^qRHn(HI+XsuR=$wKI~; zPH7BJlZt|oLvU*$O>iCn>dbW7U@GJsDJfBKu9eX-OPZiQ;7%qjrX(8zffk1?y#uBd z#|{%QD(+GDvrUiiyT13>&g$Ur*>RC6wftBs4U-sVJ@|W8U(4^_S8qk{3w^+NIZWS$ zQ{Tf|v=f?u93x@_w0_qk6y9GJ4ha_04mM^}6$G(Dv|v^DQKLV6ylW+QRy!)#iiPOg zj0`q}+FXc!kB$zuq^6(X2w~KdOW&!2r%)|b)2RWTYAp($x$HzCt4-C+!_bIfWL`Hm z(?e=!!7Q=b4T(8E0Gnu{qlb-Pn_=ii5Sxf`uv#D|2InYZR(xVTARCRhw-A%!XbkAo zA_swR8t3Gf^Hs~Su~T@X7Rm3vjb$GHKjq{qrA@gNsIN$S+)^KKVceQ{}WPFlTydKFU|} zXhBm=7XRGN)G;?+5|wZ1yv#2rfhNHLdk7Q5Dd!VOS>cgy79lI-8fKT8GFQrlj}3@} zy_&q*S7UPN2G$IU`Q_Y~!K$t!q+<5l-CbHI7qkX0NJ~fT2p*Q%LP(-)3Fj~LQCR$< zOX=q03Z3Yrpiql)C;JtxWuAU>s@N0he+w(mvOk{bs3i83C#a1dOaJz0nehMdSOmwf zL!@IFp&bM-q4PH!5>v27`d{M_9Q7|{;Y>;|uUP);6bqvNXB?`eJb`tjIky@pz%)&! zKNfeDpGASK2Z3BcREe+-XK>U=kURvJNJF?_V7jcQ0WRUrNbH;v z%qpO77aI+;{=0XwToLe3IPU#vqy)5}Pxhi23I5XW8N#cK5l(Cmq}M^khYIP|K=K|nki|*uhYC&j$WL)LcyM@so zkRbZK|BdhY&rGrP7kCu(hx~7V&vRMvzTN2_EzFWPFq)Q7JHr^~2F!_p2|7OU2j&0< zlx1`p(8Na2BR9xr>7E{C#@#?n?09(~mq9Iz_D}>KFgVn&d;+>F9$h1^(h${*d|-L!es_Me?6bmEr&5?lX) zK32YQy#VhEv~K_bPp|=k3l0DW2s}aIoIl$35&+5-nN0*>qHAp1jWYvq>R)y^!2cX( z6?VO!dzKkoyBBdxrstC6QMbBcs57QXyi?N%VAU2_Pm6~GOCQC-PC~GXpfriYxW}2u zup%;&SpZ`?1c3!h|3h_+d9{}ySmBA8P->e4!aJv0D7~o<;L|&pf+S4qYX}JmO0RyM~k`Px2{Ra|YjQQhf4x>Q@ zx}fnNV6a*O-?R_=I4}XZXR4@q$4Gw%rbg9HQ;99DWwNrQ z=`4!{0`4uvf$rS<=)svO%I@}8*~?vF&*XK#8_GYKV z)fl3#waR%*jE$J!0iIV|1@U2zM=Qtc6eTJQ&s@hL3VaajAc`Z5!7T$4Z^pfgva=F0 zdpnKh>so*LyoiYAzpbAP;&OwzKC`)HzgvYwZX2}gbd6@(Qb3|SNj74KVy(c~Nei^t zUc~K!B{x~ZpT^rtH0|PR$i$(2VgFCz#Ww1AP3(oS6XKHueJ~Z6iwTiGve5!G-b$&w zNZrEUt>qc>FS#d=*&s2S?<1=TvABV0;<}{?Z4ymvm`ot%n%Pjdn^eq8dr7-|K+-gn z&svjVRN7Y#wsJ6vc8$Ym@-a4Hoy&w|tk{<9w+YK!7!d18<92`YG6Y?RleBr=$vX0p z7Ui9lIBp#!j~#*A1!ivjFRyKT@?!`g<(ayp%=prJh|FE(E+ZpTXjQFe-0)#N4f9WC zPBe-j!25J+$~aSRYeR_^t@du$SUE$e6rY$&_E2f28h^=rch_O@UA+~1mohT?;HlA3 z%W@JPTzOOBecr|D03PgNn$hgR9oe;*{84dHYce+l@%qywI(W{9{pf=Vqt+C1t zxP&p9)dH3+as}RL8Oa#{Vi#8n@p3=Q8mGX$u-=l4LN#j<)bezLyqCi$*mQGbch<6 z%~P}7@NAMUsak$J{hJxF$!nH{(0)8wsQU(p|7)6riJKOFCPuA3R0a7~g~hSB5<8W;P3 z%Qqh8&dG(PDAiRK04yE-5mXv~xEvmu4rWA#t@%Du6ARD3XSR-`#}_?C?MGb$4oYF= z)#SV}fuz}c<;*`bKi77Rp!9PU-kdqqSJTOYFOSeI>&rhzF8X}Ly;^WS*8Xum-udzP zsP}_YI)itk>+X!0=0lA43fK1??IB12qw&`LF#gzC0bScxMC@jXFagPBX9@Te0t}h5 zw%az?%7`{gyDbZ*g;PbU(>2aAd6PzzD0w|9PmrcHz?q2CB{)=1d*G0f~^fSGk3Sjcji9&+KlZKLcM`2I3>gj=qtSzqEwt6Q4$586-hw6V=2qKLNQg=Z*vy zz{sI~nwB0ajV!dMFrWr#v*vFlq~CoR!6AxiW6uiK(Lk}ET5@xdb#r=*%hhq_4r2?1 z6C1E?aLdnydO&lBS*WWJtO_m0dZ(Iob?_cEnVxKyB!DK{mXOva!VZKH9YA%#k>#RK zpPoQdxj*LS!#f3=E7eXO<0;cEZtml?{Lm~E#Iu1fSSNKkW(}194Xkw&0cy$yVX>-g zz^3lUgpR6nnOkxQE8vS$eLsQ%kieHwRm<~=5L482d3J$Xn~kEkT(_!BM$|OyZb|l( z+$yV1Xj%4gw%2L{L+R};-z}s*YZ5OV4)cu80y?ncb)yBszz%@Bp9uDqYzo?Rzni-qgo%l(| zUszwYyzOM3Tv-U4B4)t~{Kqq!8ehwA#S?|Wmzct*%XFb_=_M%BA6J2isfK%YDdUuV zU;g#V?#mFj?Zex3A@EtuY;L_h^I4g%Xk(MG#xw1&sj+G8ekuKOwY&ts2?Ykz_Wfh@ zv(9uKJ%m8>Z@v2Up+JZE)G}QdlcT}%QXDp)$dDu?X(XPXXR+#U#rq>Bknuf|cm$Gc!Ov>XvLW?xDXTw+$MLv8vh)wqFlKG6CHL%$Vm(~}RFbNb zt{+f;f-^LOMrL|kKcow`80()2A|20~*f{ATf|g%fV*Fkf&(madaF(N4r=mW`B*|>8 znG!{S@>IUcR)rabkjV5DR%lh$w9jPWmLm#%f}GNfZE}|-5PRXn!A$^dLqYaJ2mo6_ z1a(#N5lF6Gms-V)T+}AWZ`Ns9WeRC1$HhPIR(f)*G8oTjvHbZxaf7peNzF=ZNe<}z{DomtCdIa*F+8mge$qF&`>kF@O!>5Z7V zBH)z)RRUOA2pu*%0x8dmm-vB}J;OX9Ps%CuE$jJjM+!J_-Q9H9>wU1!n&LMz_&pOcXqdLOvt|OGD-@20d z1#O%tiW{^!=Z_Nl;fSBdjf5LmC^qwUnsi-vRsinM;pAfE{v@QCZRVfLuGk4! zSPlrP*eZHp#ht5e)Xd-0yLGi09bZ-y4SWigdCk_QjtCiL@wIG&_U}stD>*4c3p4h_ zEu1R;yXF)};#NoVDhA~Pg~Gw!Uv{=0prd}Vqm>UsaObid&3dv=oh4*rwc|d!sC-u7 zdEKxr91FC%jzg|`7U#ULIT6j}k`w=F?2oL$7p;jbx&#<(GchSIyodm+zwTFh010A@b!J}6C zYw)qmp;VL0sFE0um0mHHWG59xuAifTsny4u^umy`mPGi}Y1>ll=c4neG76+ReZ1k* zeqKW4$PFn9A^?y4S}(4-eOz5P1xwNt6o_Ie|%!N1>=eHfz}busMtKAk4f!sHXMY-gNX?oEmqv;7!9);Qge?s z3l=01gR_e=<2`5=qp<7w1n9DU+Od~OV6b()@SZAe+}&hiz|0^*Q8-Czg_CR^vTxfn z4~2dsFV@t+tB+W%FqXg8wwf#azEJN5W^#Fu!f#b2@5xmg zpCa}T4_Y!JU02}IySn#C@C~C0Sg&;i7n420174l8B=3vT29Y8YGG%$AAj_*FClOgl=-Xi zbIa!;McF;R;1NmB$Hmh&ttlh%P?Sr4gzfjAd^J%0gy&nm7B~tXv2tz%Jcr5c!AY&` z?e>$DKK3QVFGd*(kO{)v7=8(r)>rwV_0nI3H^4+MRNe z)aJJFbVgCfHHH6PKcH8RI{fib{jL`J&#@YC2%2{F@WbGFSTEKW1<2Z7@T-u=&hg=6 zFQ;aW^?GRRqBgclqt5_zeP~Rf?D!4AQXf_)t|*Bs`oXrD_X2Ob(cBFEXie?hCsV27 zYI_g7qlX&uUzRBqH)ykhu#ebBOBDn%b4Y9O0&Qw2RH(_FGisb6{zvx}OPQ@w*F!a+ zY8UVpm(B-5mUsA9hcd%O++g~G(!|h6#7`#Yj0B$Ae3iOhstI8&=BIpAc~cum!n1Io zR1-tuL(!(eH*`H|>}WWDHIu0=SY0Au-$(LkYW+fQ@|HHZh{70WyqLd}l2$jd3+=tK zu3_b_!1zj<*3Za(+&LJ(8NWVV>BT?U-w_?Ol1P2%z%!5wb4dL0JEZQ7lb+`Ge*`rS zsMp~Hmg3nSw(DE0L&U#umS4!)oxjv&(wJtJ&eE)Nlpn`J&Nv_Ez6ctImE1jYJ}iC6 zAvJUzQ1emiF$Wrcc}j8TLocSMDlwf=`30B((nt~wFt1@eOFDOmxP|&S^=Q5%GvmSc^}o&YJ@x)18C4LWyClwdt>d1e9zT+e)<*EX75VlIKBPa&L8}YU9|R$ z+EfaD8QVu$0WnhAXoJ-VDuA$Atw`5Piz!RDR}2MGbNp3mS=Z>v;>mOQx3s_1L_X!3 zH=U0+f5er;;^%1lu}zA*>=G?ugi;{YwzyOCKX^?Q_5TUkn(8 ztieH7wX7(#25dtn-~=ZPEJT3TdIY+sr2(5hUtlWxAm*DI9Ln}pA$H?Yki{YXbzsWN zT!Iyb8e=1tT020_;I}~5@EFhIeDS6PM1~PNnLxk8wUT8Xfa#a)j+@5^2&t&?0&_Fv zwaMdU@*>rA-fk`zz+#w={D2wFghm>IqXB;>@3R^i6akJplC;l$B%zj6E^pI4=`veD zR)s{Pcc~MSTssq;gPjA;Py!%=I>rM(<3Pa_f5|(f93E+g$RfB#HIpIMvZ^)R-W@=&~Jf8Jvj`JT!eDKz}vt;KWyKl;fZa}*`uS`Hl>rJMBp!KOG96=&mFT@GcF zmTf_~0Wt5J^2no12p#LViwL5eOBvBECQunhQXYU8*6flolpph}&zNxK_{V9{WLM5b zq{Z!&Tbu2K1YOQTlXrm#e(_lMp1Vl5Lt4}^6`=_~T<1u28o6EdjPet|BngBwKwx)h zs2;D42VFNY6+_MX82F@P4@xm#E7w{#Nu9K1^U9rv!S#)u&xRLE;3>hOJH zPHfGYKkkp$FGv=5*=r{w)`k(F=vG0YxGrvryCZJL0fk`I-t~Rs{)+?zU=A(O1uE&v zH(SMMAmeC4fGEW@f|ax5ElIH$E8pG{JLXMW>U?eT=$Vm&ngcn#>bCB@*S4;`%eGg3 zkNn?@{i|zx)hr@Vf{H_A?OhD@MvCB88DK-FENVa=RgW2FJt~uzlZN9HyprP)+`IzG zYWOzXAzXzIzXT~!GCA~$!gahh1Lyr!n0S-pXl#pfj_$Enc{*!23fn6=M*ITGUS;9v zDFRikb09M4ShXsSPg=52sHGPll@Orff*bRvQU|`8bXT8pjaTPlo#yKIAtqqKq~7K z+kNF@V28LCzlDb7VJXA7{G$(7yfPi*StaZA4)iO@1I~`WgWJP;UIHcznAdc}$C&xP z<8!?A*W>EGch}#EYx-`A@piu;dA1-cTa|SC_Z)YS6|DDAm-IiW&K-%>CQer5$Gl3J zZ?S7Wb=xb?eKv8^^ZY$-hM5{+K>T;?9(i>kF%99g$uMmJQ#Sbh7b&_rZE8x#+;8RC z!~aiw)bqRePChY14kU*RxgM@|MG8QTN(pMjk%G28E>P??xcFaG(zP?s*mnFf57NF2 z9)hv>%O~9|dlvME6@e#qT_{`*+MRvPt%8gx&rrnwZA)0qIW7YkQtqPpo-ew`T$|k0 z0{O3&eb3dSA1Q-qis$3*N8*E|n^)!+N>)q$ub!(z-&g)XlRY1F-xnWGJh=Oi^s!g& z>#+^Tk`Hrp(OpVEs_^XYzEd&Tpt@(9?hdiSjlNoD;isznk!WMEAXK0%Ccc=#;%OQ5 zAQrv7^@@BY?E%O>xRINWzNh?-syy#^-&?sJOPegztE^hG6Ph8r0686S^Xb!&+1&Hy zMX0e&vy?VZ{s^4vH;?kayVnM_32DtW*yl1xYg?E50X|V3%C1p7nfcvC6V@l!Nbb$d zeXjqk-uIb!D5a3kDO4@5$39FjkhwN~<1wlYiH}ETdaG<8BxB=8>*8H;H}i<)+xse! zc}N!G^SL?tCb1<^k-~VE!_Yz+EY_*`1^!fW$OsasIX+QW)?~(&yr>R_v*6O=PCTKo zMqK`4=w&l}WlDYEP)24}h3A)BkKeZ2m=CKWkV$1SbjdC%k*o`@c4T<1B}IqAS(b>x zBB3=97!{pU#1|F%gTf#(FHUZdRG~TgN_1iZ&(vdJ;_9m4e*)p7r$X`bn09y`BQdI% z7L!T(R*4;v&9Fe%@mtC=GfQTb1FU20K)99ZFdk2D3iZa;qzD)1!*EIpE~+e7P!ABq z@M$}Y{B=p7jJUfkn+u6&=wZu{Z}^5SxDA?wW<_j4XA(A5w5|+b zT_uOM6@RmB_At&*LV0nS*K&Xc_j8uO_tgrIhVO)U%Vd_S{vx@G>{-m|7#ao~!tSeX z0Ip$kIBAuc+ET6W0@Ze+G$107fipufg$OZlfe{e_44esq2Y_@ZKpZnJ<1EP|CkOL8 zyJZ|V8Z_f9R|CFxE-`;Ed3+!Ap!r}GIs|RI8YwlExNALl-)$5;RlR=6f7zzHKRa@{ zEj@*NU*AjX>E#A*zg*vMKcRs0zou4s-%PcB=+tBs%!0-ye$(W4(z`}OD5rqWazeu z*#k>Q%fl8?Z842VjkGc8)G%9orp_%B3uQ(sBy!9lP8~JgF1<7)s`qb;c!l09*??3RkY&U?BotgiH!1&Fo}nH`%J;2X2vPO z4bpsr)QumRAVLjUJ#we1SU_SfpV37iqB}WN|7Er~)hp+l?OvDElP<30UpDf%4$)56 zC)djj9!_N37S8|oaDGZRcjB7Ik&Ytpv$*c@lt54OouFnqAK2SYm)6!Tm-LGBwyCLC zsbS`R?*2EZT3D>iBQ67CZgU^+9xCV-zaU?PLxhvxuiXx5@GcX!*Kb3gIy~KSY^!Y7 zcH~M$u9MJrV6p?`E=EID1zQgvDYQgd5d{(>)(h~of+4-i4K%~Eult$!AliHpyN;y- zjG=6u^jmD}0z10vfX%i~?!W`(^)c7s+OPj_@2Ovh-gbDfr5X^XleSr1*c?dzy3EEf zn{w@X*@@{STmmMkWZx>`U`MtK8oLuAZ9^rspL5nufEJ>QP*Q$v9y={;IS_Y<`g6vF z4~OP`3l!huWnXv;pEhEWi=0e%uoax@p)_^%+RSdntw?g-b+KB!YFrB&N4uiQ$l5FLH6q zW=08u>@&Acmz4Ev;s-72*#_`C4ePUOB=`IapX*!e1O9D29TyGmLwk_l+w-RS_~+eI z#{?JgMBrZ2r-qTo&pylE7v}f8*CE3p;%K0Q^5VesK&=;fYE#HJfVg}9{k?zDhqQ-8 z+X^8w)|!V z=XQI}W=jH#s^T1;kD6TY^&&^z2) zaNM;5^Cs-6#aO$ykw;Z7X`Fm)Aa4Z`F2JXqa{(5TF}++V|AAdknhF;4!JZoP>@&$C;p295pf=ZvHRH$yR%q8r+Ej=@ zYnr^r4dRkTh!ko9K3gr%iAzpajAC*qY!)tsC$iw8Uhd->C;kkIC={jr7@DJX48S{Y z;lzC(ZY6>JR8;Sis{Eci>k`Xl1vhTLZ533l#i1pa`tEnG!@HfwDdgGRS@CE-3B-|g z0e80}fD|*+5sa^#vU4c68y^*q0v9S~AQA84QD%sM5CxL)d}2P{c&Kdc+J?LW{~m{> zJ0EfS9r?m)uakA83O_&=F)<+#)11WAFf0`t z`fYWsNhtFDc_FHH1JpHOu``TtwG2huYTF|tXEzk1{?@Z1bE3q*nKfICXg{jvbe)X? z7w&lUa9tK%-9*J&x11{`e%3c~M>J{~-`yM?5 zO)K(4Unf-VQMC zxXS3ekw|a~jL+StCw_!~-Y?m{wId4$_ zd8iKHnKetWF3e%{)i%Km2eWO)ntMAb3VCPuYySUK z`D#OxLcP%PFPvdvhogK*k`7Bl`{KV%{}AFm49-`Fxd{Um?!2V*nE!BGb+reCCVgKq<&Zn|GyHzh_Qwu(&T-MN<7sQr3xE%b!7snOn zSRQw!86sm3c}hXRJ_wt*P|GjPmmUZ#=6fBri`1Es8H|zC6W7CLQ3zE+e)_fdclZqb z-ub)J0ZFWFJ=Xat;EY%r7#GZOR1V29BhncQlfS(Pbs->A-{(CFu7De#@axQ4u(Z9( zY9Y?*nDu9ah!D-iW$zkFK5-c?QHQ~$xAgeIVwbUGxF$(zVKG;Zs4)BG7mk6=mCtW9 z@wKLGx#{C06}4fcVPg84{s_JRf56_l(v%hXE1~XxsGSPRKxgmFY8-E@JU=*{`L_tg zr82~Wov=tx1+hQuM`ivlmdHRd;8mWRw)n~LrH;lS-B%sI`y1&K=2P$k?DD>@ff=&2 zvE^M7>!BW<)o+gi+;&ZF>OOLnU}s?vF|y2+lm!%1xY}8VM-eo$5mWuc>Hk^J3;Q@+g)tNV-T@4eUHGraL`7e^J>h#i8voT=dRf8ue%;8aFZ$eJP? zjw9C@n}?Oa*+S7neV6`QkaR$V-5(f`yA3RW**^ddYIsWuJ6Z_n_wCyI7DE zFIgKYU5<(&*`D_&oVffUR&*HL0JpuLE0JF0p<6^{49)KDM3}jq&}?4{G4ir-+e~#5 zJT!39V#G5uQ-0zs4hakuz$VR4;f8pP^X9UQp&>uJ!tWE_iApWH zN)Y_%3Vk96UC*c))HByxQdU3kX%sd|7128lwgYsAs*&qB+=_sd(_%mzdjEDEd88?!0yD-S7o1%v9OJX}FE@Nwk`lU#WH zmueB54A3!EbFt&1pY`~8s;aiKq{uu(98@V38R^=yN%UW?zo?VcbypYd#Aw$(eM2q~ ztVeHTN)K#yL`VTDqN?NC4*4ORK1IVUZDMp5A`PujC8d&WSEPm;u7}iT^v2asuVCra zR?U*H*7$4;BcnOjtEy>H{bEFF0%^tzx%5fbb*h^>@488^V3&dgqYkSjwtB33A{ZGN z>TlM*GLv1Ws1In+<)DL`K#AB$)u34<=1EDofQpF+m!d)ewJi2s2CDF@r2;isg;j+u zcysj>D-4fa1Jrl)%=MU_JuktWVL(@GkXsLCn+k*%siv*>mBtO{U*YjNOc;%j;DT+Q47F9i)I@A9BmO8-ByOXvB6mOq zpOfZ^md7Etjm@Ga!cAn)@(?aWY#ci0{~ihg`N!w+^0oQAyxm|A9?uX~wg91V4B~gN zlMumKK6x$*eAx#&)n6Aq;gf2zHTe#$r-KY8k1nSsp$&fz*cnvUQjVy_H06e5tK?Y9 zc~p-K_K2Bje*GKv9ww_26&M2d$VgELbE+@- zM9%I?aF?E&bBub|h~mh!_+ynQA-o0m1?PW&A)J4)oM7`FpZ?1Kv!RN~=5Y=RCdwN2 zI~>bUDFvtBnHNJ@qz`)6E$-&6?CO^O8sr0cG;iS+l%Kz8nLLIq8Co-B3U*$CON9CB zi;Um^Q9!Q03JCjAOSa;TkBfw`r=OvbDdqSWO6cC=zxk&O2!J1H_oaLettmKD&=_oS zPMwiC%M!~J7AXE$E=usf?1tdj|L_R5)12jMYfw?No6%`q-@5;HSN@qeZ8+ze5jByU z%MU8TC(gx?f|G_23&RdMkYc4OLYT1;(Kym=uH1JHz~*ajXfJ99{R)wpEvYvaT_cnCYU z9+7^-iS_RIBgqTSzLUgE%FF>O(}1}A>P2@2r~6QPDs~koCyclh0wHqkP3@;*Py0t) z6EwbpzoC8#Eh+VUmEh&>wKzK~%hP>vcW_pg6fSlT=FC+Zqpc9H<5LXJYG$&`=2014 zyLO^~!SPHdtIJ=f=7O0z=~B}&pGg_vbTXoNUc;iU(EOR=x5J6pYTzk@`;5?1wB+ranvupztN4kan)6%`1+!A^d!%WwUzM-a5^T}@rwkzi(8A^`MHT!2U-VCHV zpznXwi_V{=Zxlh@hb9fk=A7uoVjt4ET>y=*ed!qe$08g?4JDrO#F#wPBoRZzqv9$8 zdq1OJsP&{1t395vaYba4Vst+K&Z;BHq%G;zW$y-e1XM+|L*nccT{O`5B-tU!wiA{Q zUpOInxdviiu&kROT6hEha}Y=25fZJ_H>i4$DjNv0a9!y_@XDS9WyM zzu1Y3+lshb@u%qs+{Z_eCS36wc0=Zp)DeBJ=RSwvAx`f}wRAQk<+m zA-(J-$IW>`ae)m68k>SGv!n#u=UndRW{C7;Emq}xcL;IEGsw({rNAAOz^q@5N&zYTMP3Z^wXPxaWt6apL zuy6;c9j}-V;=uw1?hxKl#xby#R0ZW|C&Spp7$j~22l^QSE#)CtRAX6LA$KC0+)q|L+sKdkZe4HdsOkL^M_j1Sk(kRWu$e_ zp45<;L=DTdH8udB)w-Mdj(GD~J^IB7`o+sl_)Pd~v}0m6(S72ux!4@lVwH$ZR5gl4 zV*V{f3Mfp`s!U?FB{MX0h$zWQjEdk3JNl0ZqJ`a7@%<)n^cs=q+Zo)l<(URmC~eA} zl9uO*l6t=#-C_^erv(t(MKvEfi{@)&hf4Q6G6GZSb>*teeh1uPLo-Oa11xh!>7hg#Eg$96yWY zD`n$fUHI@#C&n1B-iD~->wX>|x;&I^^7Z&mqTXFgxr(D7wHh^4mPfbph0MKRMDKJ# z;&(Q;K5b3w!D&z47_}TdijnZx+XL#&jwKvF!dfT7T=*bF@}{Zmp?@zCHX0rK(^GmySI4YOmK&Uq8rN9i6Ai+PK%w0;i}W1>TY)ez3rz{!nAx4LXKyW zMA#{h64BrnJB%X3u`>CDs(vEHFyuJidCPi-0$Gw(h9NLPS#fJ(&4s_&<1D`eGz1*s z-Cw;XWArPo4Q|@dyI4Pok~8!K-X+UM6xvQsJB8YAJm~n*HqMv|8qPin^2z^Q)*5u! z(?7PLd(zc zn2L42FP8A7=@}`uH%X3@$3JSMt@kBMm9EHXA-TRA9rzAUy%lx)$5S7v=<6toA%5lk zXQ{T0mXhbNjO*TEU!PcU@*viiu&s(l1|+;+bTF3j7+k$Qy?5U0V>*r^89ezP%NlFT zjW5jWoK<~tFV+QS1~0+hf#Ymg*ge6QXBGC#l&ute+Iq6D1pfWKC2!U|I*JX(_-%y? z{lLU@`}xWu$D+*r0vdnc!~{nVrNnaWU=|FcaY|3!>v_*r^>q}t*G!H;YD z_0Ag~onku$`x|;MS~pf2m1>VsbWyIE*PO9XQ7A%GU8?7p)+CmvAA^39EFSRnEnr8L z$fKVnkF&-p`SVz^9I@$GXH#pL6UtU`6xx~VV)nW-pVm4t%|6grPrquIb&p?iZ5A^kBm^npG(oI!KHT{4Z`7fH zXTer=lx>B(I8%db7lEQDU^72p5~O&T>;RM|Eid?H9FLE6&DFsN8Ei zVq(l-Fu8A}Gj+3n<_YXp!HHm}-}`J3 zIn+Zpn(L9^kx0g43l3_Y&+|HsCPPcG4{dCx;{`QKEvQExjCs*6>8r`p0U;r6h`fC7 z%J0=k`k2kP=sAZu*n0cS=dQDo`?O>u4Z~#h4w5m?)t1S$MRN*KIO8qiZ;_#-ffsxD zH7y~@S7D!D3a#)F0xx(E_hWN}fU*UwSRFVUvQY(@uhx=V{H6vqVA^oNVh)BOrUr12JzLTPX2n{R; z_lKIier|BEW|&0kyJneIp({&mi7i_iy*kbk88|Q91pHc;t+Qc$;fA?h6SOAiZTCTE zXdhj-w2Z?Or_DhliO}>UXydlN#INK6901I<*jGKfKQbV2;<(aj;qGw_w35E<>$B? zd-tz{xtVm6TeXQAst~78C~Xv0a@H&zv1NP$oQ6ZD*VCOIhz-lPrtb49-|qwzc@$5= zcKwMZSQ?enL5#Pd3XRHi&deYZb0$gDjjPH(j~%|zyA=H>i?BD!dBI7FQT#$~nJ9z! zHo)AE?Vk~RDdh#oBkVDR{xIIUDxg0+QeO+Ja_7%f9QcLMseazqhEeRX4vVLn^Z^x$ zwT?fJ=YNZq#}{T_fDgyc{vI&Nu#5?DL@)d^wj2&jSF~9Dd|PQ4mn$q5!%u^-%W(bu zH%8+D39F}V{d+&K0p_Y@W4Rj1GP~%&8;88>W=vcQxd!w3RpdX`SYq~#Z*PNrYCN%qpsHXHXOrnV6i{}Vf=p?YAgUVcpj^dH{)42~#!XPDLY%i^lnrGrCraiX zLTtb@z`=SG)a&U&`{F}#tsM3VPJv`k{_=9WgBwgb#=&-2oCQ+s0$%EsF4tDh3pl0J zE_@(tw%{a{R0~TgGBcYwQP&8gUAV$U=lj=GS?W<<&{0JTyKqdG`#+-UXYO}tcYXDF zT%+!jO7Ki+3h;|;la#X!=4_p#T%pQdzJO-oN5!l)M(Q_dk2WQuKhm4J{SlDE!8%y( zi|~wS42gH^FL;?-okeRc4`46O%j>M7ras%=jNHEuR6P0<4fzy(*75(tt)pvgqSW1z zgGj;=(sh!r4nUb1qcMC0ec5EKe-^E^IV6SYBA=u8B*A%=lZyr3mGxFvSy%U@5w&|9 zsB-;rp~N#Rjhz*12BH0yaoF%K0?#&TYR2Yihwg(gsd?FFKn(Vxv9JCk9DdQSv)dm6 zNzFZF_E9a}C3bv9bAqx$USx6b@9+7AbT@#ddg12qXvejxy{^#Xawo*3jjp=+$4zza z@fS{jubk^C_1e3BiMP1qukkFf!c7;`e|CpEv>!rL@#su)yVyFY9)k@#8!*rK0lGjF zP^B9p%iPLJ_XW=?6-K|**nUEPy(VTBlXiL?4BPW z-Qr$XBdKRXnd=4U@Ctd8*?4%d9Vef&D|K|EdUOB8G*CflO1&c3Lm% z>#u!5^z-J|((RAZMI1T-mYqgwa>uanGveup%@Z1E^_EfCi~2yjZPe7eJ774S{9c(e z(fG;TW#ftsj_9t;AU?^0PIEP0T}1-Y6i~bmZa!;WzWoffNyf0CKS7hq`L$x^$flcq zBi;TGbLfXgBRdU!tr#P~!^FK3(b5LAeoGW~<++l?ENW`#Tku%(l+Vzd^ed|UET%Y| zP}$Nh34v@53h*Fqi?x+HEmrn^1FhaV5&L^(l-?pVrEwi_BhokWyuYGbPwss7B0s}j5I5Sc6yR~jRD23puo}2XMrRyB``$g+H z!82FXdTjRg_pb2`Tj30=RpSRsBP@@+PMf6dh7gPKGNXJ9?ePZ=PTSz98q150C?SRlh#MKQbi7ar7@Vh)E#dOfmPN1tfUX+JZOKl!RR-o0@?}gb0=O#Njg(b!S4kQJgzj4{~%FgbJSvU zt)tjQ(xc>a1*Q8jAsm>gXtwz&QGklY(Y>&CN}MFaOtvr;mn+OUA9+x^g4OEhnu~T3 zwdk_bewuy#;SI3Y*EdnS5LW4NLN$v!UzgWrQm#^yQ?w&bs_JY_LCWIF)mA`;Wh>6I z_nu&gsEPmy+*{T0)pcmd$!I7yzzb-Y4JNc0Nsy?7>hx2R@^FOC-~Jk9poVK6sBq0|s+(T?H455?39&$;1DlByKZNs)?I;1+qZNNE#bb^! zDFZ3Z#0v34=I1qcLM$y||IeN?Y~0E$X3JzJkUMVj9+@YglR7-BHFe;lQKu$1U^T&w@&jCnfQ2`T*U|^yYk5agt3liXL;tYz*4zv%y2mHCDcdL!juH zk9UL;`{UJ$u2fNfW&NzFwg}Dpnk&T*J!#9GXz;c)xeIS2Da{v9J{N8b5Dc|d`9vlK~Srd9Du=O4_9wS9>k1Nly zy#JM^YeIq}2=tHp600g8Ti`W5!Ekc!eDuXm}!Snb@L1Ykr4$MV?=0JYus)c5P2ft=fa_xCB zv}bd@N_OU-5ZWGoqfc_BIVyA#TAt-C<5o6&P6N3sc#$7H6+1bTrHv(xSpV(CEJqWN zCP*;ZvK|kPKlDd<3QLWld*k>ai!$1H)t_>EuTXWOivHc{cYrG~&u0n5B!2`{(*2O~7Cr60eOLzu^FaC?;Bw59 zcvwX8Lo{p!LQ4>{)%eCMPWdBEoMUK=B&I$w`w=x=bpQ1}?sA;&PcIyLnI295pMDLv z7WO0FLrk2=Ph}dSY(bK|W=?(N)V>eets5VyCnEMV#B>*mMns|ARfT4M$%zmDdEUV# zHe9VziAeA55Q~qXRlw`&)+0{w6(yyz$)80;l={o=zeVRn*MsaA|My5b!V)}uqyi?I zuB2D87RkA@dF{v*~4( zQA8TcOJd6qDqj6t-7@o%O7ch(>*yULiiv*D`i=UZ8t87@S}4@FK|Z+1hcWNqGMb)M z8APNpyhOGXq2P)l@01RDNx*2(`xTIH6Ma@TE|9d6!=Te(C(?C9LtX^S5FeLN?#b5j-Hg@ zcqEj+v>oAbNdWbm8AI+{O&6(2$~cwB`hQ0q#jmVel$+>I_x}fB#!0%^@-7*8s7DXN zkCSQrcz6lT&c>i(!7!E#Q9|=-`+hVdn-v_!orqU!k$~_}W<||}Hgi+6V;E!um~!*7JzZ{om&VA>3OU;4=(REf0OBDUkiG7Spal=Aii#9XNv`B^JSnIFhCQlGqogYepdXt>C@* zp{!fz^f^F5+m2USW3TAUTy5uN@p#Fsz3_y2>E8{@$rfu1Ijd6?C|Vy+9;R?c(LSiZ z4p|M!jlToT5+Fp98=Sjd` z@b-(Kft^wwDn3nH`BtYPKI=yxIta5HeDo!Qi{Rhl^OTh@b;{!Nel~WOtfT1FpMLPD z+dg4E_#Zt$G(?C;#H<>EpE2e`j=PvRQIN_qz;}2ukCliXXIBaGmkvU&GWtu7zxxRe z`zKDj=~9YF@2xOH0H0vg43`qhTgeEP_+^WEg{9mQcZvVD1Un?{Rw( znZlB0e^n!?TgdV3wih4`j)}!Goy@8o+P~s;Tbwa{`>hs@lgN<56DdH9)QGkQOdm6b zHTxNS0DHwMMY*+j#3?+^HR76Z4=WA-xfDNzX@FZ;B1?pqN^2gKnpUzIEJ1!$FxmbF zd_lx#7>*USC@_Or8pa2hkO2iZ(~wmuhs#H+G(+PgF=g;XFfmetWOb)uGI$WH>jfx+ z^Whv+??2qT+zB$W?H2{X)3~jfZgO4@S5zOJh@aeG92bQ^adDjH;UFGdRc?%nMRZ^H z73wMu7E&xDZXI$Fm8@*p^(URm6j+ca2;`-iyqupDkoU4tFMQ6{v{Aj(r%~)Frc1T{ z0{vo0EyLeL*KTN_d#&+V*flha*O}gzYF**m0H&|tUVE-M=Pim#jK*ms30)mPh@g02 z&8>79@g3S5=I$uiUZZif@ZBh8r$AfbUZI#4ulVyaYcF|(c&#Sp$6ISTik9Cri;aVb z{RF`eB+{=5K8pw4h04-Ef?v(`RHKs+@IW(gZsPU{0tE8Xp9>}nZdMj6rp4WwM6ZuY zbB%#5IPN-g$t{VH!5|3ZWHb$E$m+>NU$D3n1m6!!xgh|OK2(s}%;k3p`|Ed&@j}OY zE@3hvf@QFTC@Duu=`!zwKDX=Rdo&C7w~;e{CXa9Xok(}&D%X>!+zv*-l?CeV&V}C@ zT=RWFNhMP$1_+tPQli_qC{5rl*{YE`GMvSYb&Ot>>@PV!Xh9rs2bWlS!>M}rP9&iS zB19~0?Bp00HQWJiq?Ty;36=0vgVXcRrzdmp`JaU zUlH6pUzqPKKj02fq&%Dz>Buf={$D0p!N z0zPXhw_Nd$6k_M^zT4tPBy=wKiwqU=%@Ed;oX@C`7+~K$NjC#ZfeNbkloUM*KC;xj zhAX^^o_)WVrx;pVOfmr!$<0t{Af=+Qp9(|75+(%=r`8f1$O{|uYocHwTvIi*eRsuf z8{g3{9j3+bxI3IIU55L_y6kY<#2rozce$>k{ccmxJ8k#t7nZKJ1YCOLPa$I~7zdUz zbYB>h$7ZjkGL$vtFhwcIF6rY5nuN%jKs6I?jlI?M!rGr=y$ow8c77N3%tq>%avj4c zHF&0+DkCy5Y(3P8%P(3L`uA{8N?ef7maM0;l*>VS7L=4x#e{N@u=bMWl2vV}yUz@v z+|FukHSRvZ#06ObaRDWmN_405n{K$4djHV=3Z;#FBV>7YI7Jh=oF!G@ETl+?I}_TObW&6L z6biYX9HUq>0JcL;jT;>G4_}f-w$LHXG)U3kw9e)tWW`-cmQ>Jb{J9jbc85@h_vUld zLslmDO9t9F1w&W2!>njz*2)7mLb*=KjcU}L+L+nLspQ0Go?m$nYtW#o&9i%(Qi&wW z|EQM+f?xf2&rwffH4wUo*NsEBO{fHplyAj-$ATY;t4xv-JGKA^Uz}s9tzPMXL|tQF zcED1g4djwU=z_5^#2^3N%|T=ttD0a@2-^=)Ym)D?zzbrg-@25LH=r>kI_1h2*7_BL z64m*0WT}G?@Cu=1>J%Ct;df!d&qt^}Jb?Jk3zC3ng~V0P9(Qn@(gD>-CXmv>o4~)a zg&fjy1B$o2X7G9-9r$~QU zQ|Tq0$Y1r`_uuY3`;!eI2B{xgn%r*~XtfH4t{zRSFyx+9CTO7*aY}CRx~}a0cV64` zGI-Cu=SNaGdRA@q&UNf(f*Z_PObCMSmi$ zG70Fs#ubBJjyu@~-f|VScT%;UrFKI5l$4@SevaDIt6A?Ar;Uh;% zJ^(F*+b-b_`1?g`AsSnk6l0cX>!Xd<6Zz)TXm*uN+FFxN=5ffax3Ig~{><;}-Z(r* zs{Oz7L!!9zQomR{viLOJ>8q7s3*|YwNZM9zA?!w zc0!qMhg!3K>a$u-4OJ=&n!Hh-)|R@iu(0{1uJ)JSS)IgUQcSVo;Dt!@7E6 z3waYqTh<92x6O;GQSZgp1zWZ?FYlhHmYl0iF1P4xbb$>%Z96ga3%aGIiVeR{vHH{LOe0*|G$to3J`!0(1s zXYg+Fh~yHaO+t`l&=IHE1`|;o>S`eAzjCcv@r^m@2wf)2l^>ueJ?YpXM#)jALPR6Y zZvmaV%$Kb|qmDZt&mag|h((>G;1SK+$=$p`o4LDZY9UU|T>#Kp?C^!&6==6C!d@un znT~|0LNH>9b;`AgAk zF!A0RpAUqn^fq!N1O6RFHc8uwnQ~?jq8tclw|yqvLukpYd1?Vp!PYD*CH1miEE--c zt7n=|tmW>??Ct@Gvr9(TavvHZO0U$g0R`wXwszCDvT9lN@~~WDJrhwGtX+if9bM0S za6Y#>#SmK+3sP>@TzN6ZRa}J7I!{FlqHB$WK`K%D(Z6Ak^%|t=o8+hck`4$06(OeM9_W zg`S59oh3ZeLSmb6N@lxrw7`q`_`cC?!u2Q3R1xkn=PDUfwY&smj3B0FV=5KRK-kKt zsyuwtsBhDJNUtR872KpR{Qe&_eYbTU+tN$1uT~{%^CtY!wMrzN6aZ4bxKiBCJuoH~{UoR10v>!9%7KHDZW~0DSEsqLw+4 zIl!coTuza`=j9EHkM1S?=l2sn)9*pUMq4@SL`lsd z`?c;w^>+4krm1B>PA}?_X|TYwsjxr&3Nz0BJDZrmm_6O$Oet95It1o zOg3QW`E@in#>rI9vnNT#uEeSP=#rO@RX#Q6twJT@QC>B(Q)+%J0ip)yh~@r4dkV?> z9QWlZc7}vmw?H1F;ECq5mFWLDeh%bSsc4+-^~zGU&EC{9i<l0Xq8v0s9)U|(#w+puv5!8T95g7+A2 zI^7V~6)EYZlG!ml;_60QrJ5@f6#M;@AM3d%jf6NPJFi_G$PSJ4Tc9S9{(yI<)Z(ky z%p8sxM4adHxYMrtTqPh`my!B8>Y9p89Zs!+M@o62q<4sHSb0f8h#N#w9_rVg2+nfS z&&^>~IZ{W1*|v%hjYyr7LF`!%tgq6EX8Mj6)iU#;S;4lTpR2g@pP>B`W2WR#JW zG}{l{nXiar`^5+gdyQ@yM^XM%cp%k6rvYx*qva=;PgST^ zw2MKEE-9$siS&IRz*Eg*K7(__S5*5!OmcX8Mqd|4)ghQ6fVUU8;gCSdQK&#f8-3^kI)9NXTY@ zp*fCX%MhDtNkJ<;(8=9qzc$k}Br?37=3*d98o)Yc;k3mD^P@_OsRbd!v>?NM_Iotr z529>T;xpzLScuPG)I8;iGxN=mu?rJo`lB(BiJ)6(Lo?kB7)T)GCt)@zcPQkBlBvw% zYhT>d2HQwqAPB1|;eQkxo{H|FSs4wo5x%5gkf8@Y+)r9AtK9?P6=7G9dIcQX|jnH5IT1WAi)dwRP3<{40}nQc9_TA>163L!Y5V=-8SW8rG~lO z(9!;|Dd>Z?`;7}LpMArd3Gk}wFD^JKQ@ODg6T+*u65a6b8bAsw7vdX6yWx9h7xNiU zPZ#ZPaik10k&6sv%gf`cpq$KAjvO4^7X4HDv=r?$D=U|t`@z>>+fVl_UY)VUF?+IC zN7?H~hWApY9jCD%SvyWR0&^&Ki=C91z46MGEA8Fwy&R_Q?)B9c#L6wDPu+;Q2|Ip! z^y+jC%v>R~TH50K5&#km!u()8-!ip8bFh-+Qw(i2Cd5`266f3&8;l*Kl<-MLa0hd| zI{jP?MYDKvRe#pp-?HMA?By-H<^p0nf4g>8!pnKMWpq2A5$A&gRR=%@_kMM9^~A|; zFvLiC0Pg78W<7kB~ z$gjcdkhsf{<}a>*@UArmHNM{9@%(XiQdeK7TQcCFaK?nTWGvc9~N`NKSi60%ZII ziD?v;tt8l1{**1{YTVfEQl~^FCru#%pz~ZxAs?Q1xsb^b~fL~8kX3%Jvj>BustCnCrE&) zTp+#MnC=j5kzi-CDEGG`71<#PicwvDJFiT%UR%3}5i7rS&k3HZj>!_Wlh5hVqV79# zeA=C)ysMamO2Dl+XoB82CvZzVBu-lU3)mOn(V211j(BrYGzHa-}mMpnw5SGbT{ zpZ0DN2=b|JicA3K^neg^=Zpd%ESL-D=jPkO1E}D{$?IXo=_%K!|41@!c*?EM3;t%^gLRDVYXJZ6|iL zgJy84di>34M#t@YL$UbS8DJ$G zh;I#Y)83{7QItjC=0MQ}6ZFOffrnJT2j~#>0Sf*Sy}zGo8jBgLa{1s^KAo#k2ZQQ+ zZi&ac9-%|rsJ7rHCvC}Q%Q!ws#&%O;q|`=W3U({(y($P4_&lo@{eu3`C`vbuk~MK+ zd?*+BQ61f8uxaPGCW7?F2?55tp9B$Na84u;P0#jpGu>U@+RZYReRA^OCm+BeNc&x* z!eF^Y93$4yR!HJa$B*o~owOek8FDsPItr@Y!sAe~1&xO|Fl3S4;cjKQ(PeCPcD z9)LrlDBB2GOY11=DnCXs$peGlFW@;iA^QH;e z=D=J9tCASjRWw})M1C5 zeU~2wFou1GMsWTg`GI(L{gJnUti(*vbmTeWal$!dbxuGCMC>fbB@1GwuqU$yqfmsJ zu!ecCFaY`HI$Jxi3)L0S?0E#xB=F@#)@C)MX7=T*)03*xPs|1WK;y^e^!Alelp1Gu zQ{Gbeu!70wS;Oof?0>U9aNJoO0tAj*ow;c#{-*IH?`OSaH?uEhJ?0eBe1K0?0FS#u zwLKj77MQj=hb49@k7K>%3Hv>}G3yJb-#TBxzThCukP@uz?*&l8{ z`I0ZPzOp~FuVp>w%+B5zknXr{vHd|;-Yu)F%snl%#4F$3^%tK8l{&9?gT-#A_6ewYw9P6@b2qeK)UasgBU ze5YLJU;ujhm;zRmzrkbxY4O9Y?-5Yu&$Yj|l zq-Tz=YNsh_-UPJ3GOPddK0#o9*}pam>LlOLwDd=bc>^@x@g}5U_Eern@Gyst48V;) zL7v206I05p4;(e{}{P&^r znl62Fb;Sz)wF@*0Ko|HHbdWtUBJaY2Ka42*aQ(%e><|Ubuj3Zb0e*HY*DTVc=~SY5 zMe1X>vJN%-(B9T5+YuG>z0(; z6;5e{-P~WatNS*`kBtqY)LxKzaD*p8YJedGDL^d~6Hfm|Aqk+EubBahZvK+Vc$lCe}CV}6EmFmOqYjP zv4yEq?3b-JeFvAVltQ)6uB({?#UQemde279`maz!+NGyrnx`*vcH}O_74Bp+b)Ws= zrJ?_sHUs!o1@XA+leWj;2F$qCd0|qqnBz}%vp;)46}#RE=Ufgj#Di1j3E!4 znZ3#+E5yRPdCH$G8D!9bMoAQ7ly3RlDTvCCjaQ>u{Hdjrg#K&>uw3NDGtnGW3HQNfe__`NDqT(*dGQkb*XKb;+Xli47bT z=#Bash~SPS6LZqP-yu>E(3t2tQYBMISSusMhe-m%CvZv<#pwDGyXl6&yhmB4zChel zc*0-~c}o)Wr1@)B97ma^O;@iVpQ}`ZgQ@k>HAHy_nTl!KFRQGR5Z?L7hejjmoFCHw{ zCsFU-HMBXV8NJp85#aVqp|k80T(NJf%$`IA|KrG2##Q)A9Qz3SY3_hD_W>>To*Ire zbNc6?t`R$Ka~b8$z3@fZXwG1%f@XkoTOUHmD&q;U^o&pM4d0DusapmPppQhAV8thj z>aGvxP5zijll;`&mZHmGA)FpEF>-Mys|#qjCr6+YMAXR%o>&OHsE2BG#57>Vd3Vk3 zKBo727O>t&t_U>{YkkK48Rn+k>GBZY}(T58cD~xSjf;aM4%7F9zyd+yh=+d0^Bf7Z#)WEQ7YB*(I5+E zj$c`#XWr+79un*PB>f5T5~2I0j%FaSi1#Nw=fSW>Vqch0@_)y(_lhNikTOUJrTa~; z6bcXTadV17?VS~tv~lOn(^|F%9s~6hmbD-`P)&93_krhEP7*G=vGT6$P24`ycEp{duQ z4h)>(b=|IfTQtH{$_#16(uNdTPjtN%$Ta=tU`yp)PDFSJfsNi@BRK)>IwtG0ekhrC zH?<5<=N7`+?lv}4zs-SoUWy4n{u_F#pqeFQeb@K3vt3h;b_QNmOu+&42Wc?iRJrrG za>3huabzkQ9XaSSW4o1H=N+RxADgOIo4U!9=()x-eEEE)g(wwLiKRp+k7jimsAD$% z6z|}dF9;je>6$KnSAG4Qgr@LPPDqe^RHbx&T;0h72`^s{#ZoQW55ik2`5gYtQLSGP zUw8h%zqRf_I%WBW9K?U zv@}_o$Uv3X=O&y_IiEZm8T^0_;&xUima63MuR#-4Txj4+z9c8<@`)om?>Pw>4@O(g z-K{|0VjQ0{)CYw8-%_s@9D6jMJ|g2|1*~6^93R){H)mR+;h=9VucK@<%E8&;ce|0l zybtr^A6DZ}g!^|{d>c=QWgQ;mpf8MQ4-TNW)ovoelTsrRE0>k2182rtrIXy_LJ13^ z#F7_zbL9@SvF5BocfqMG@}`H|ieVv~6}-@8@dmja?xp&v@6DgEFxqR%yfcmX9jz*@ zVf~)b4GVp{tUK8y^L{i4t_lVr+}3JUH4a#;*2IoC9q9*)*`XT+JVmWM-d_^lhS~D0;7s4f!!%xsM&hzPx<7vVnF^p2>2)#wbYQxQ_UWtmjf$ zZB*YOxoe``y$gJCaa>bk>>P7Npy;{hWaGD>&;9Tm!FW9& zRc2*bfAw8)wCDA@OWSu=rPu;{Khswl=ay=n7$@4|<>W+77`r~}x4vgV!7zit`*oH`V6#PXM}?A=jeqb|ydV zxA>pUKGp*IceJXih7HSsE;Sr*MCaWu;OG1|miec>>KiWY$-Pn?QJqw_q<=JcFJ9mH zcD)`KaU2`D?8)jDzHM7iWn%4|`b`(!CJE`Wj#=m}bwY}kpm&}?ilRO<#}tp+ieAPC zr$bXe)yULcZU2S8p$x<5=|>OlYFkll(JKno$1GlnwJkX^pwmxy9F+a#O!e+UmmSE} z{1}kr4jVqa(M}O>e)s4t_g3f+LbXQaG|o!+mVqvlPVdvL4Ze$F_1SYlB5U5()YiN^ zfd9ctgZw^9r~N+e?Yj8te+O{~(ilQVOhs(u7p)TVfVrxj3_dVg>j4h#OPrQ-cOs%9 zW=~Q46vgX*5bl|A7KoJ9{w!lF>|I!hC8oztaKcZJ|LLHj8m*TX2L`w@t?CC@nrH!X zFEc67m6@VZM{Q;0a1R`c^6|KiCNy*G=eDmdLv9ce%oo4v ztjQLC+5LKfo4$s-YOB&rVsfh7jJ|P47Awjk)i|`n2@o{$Vlyo9_&uFeVSB?*pfDBe z^#k?@O?5kr@OFP1Q7^Kl`U6?<0dDtvb6A}b)X)Zw0iRl&$Nb!tx=GgNpu4#OdrU`0_nV1~mq% zku~{~Xun%3!0N&sH~kcJyxMG?uZ>vAr`qBedQea_XS{skT#*jLxVlpZXaIwybvIPj z`@u>D#>IJ4xj#eeI4=AIJZ#(g+=>y%1MU@;V!yo_#+?xVb9$QA)Zeq#V`v}7)J=FW zE>o*GBjEcO6%d;L2MxHY`*uMm$#-SVPIG>Bj|HjLDUIkCC?A#m^mxBM#C)w(5y>e_ z8-`f{D!`0xapoQ`e%;&ad|M`pkBGg^@H>iweT9-;XT)21ch3Ie4`fFpRa#2rUEv!> zN+|(hJu_c|2hb);eas*46b%_H#~EZCNBpZ2*?+ZtVm=35;QA2%FMyrYr%Q@4-@j1p zZrt-pj!XTuiHaJ=kpKR?+f7up=cw1Ni)-0ONh`~UimkgpNi{qE7^_6za3(K4Tj}xK zLH9OPx%;s19w@B(){H!Sa0Uj}@Aem1StSoWeGkzXBohPlb03du6w|R6cTKcxz&)5&2LoU5OqbGVrX39w2I56K)kxSf zq1&=_GF8nWq_Xfc1U%)Fy|PcS`3=m&1KoH_So%EfjLx9RsxaHW5$=f`hN;>1`q8+n16X@QfPNa)S?*MC38_#^FHZs{2`_PJZ))n-~1AW z;fm<{b9>faHNu4!du*g7LN`bIBz(X@iGU))w&H*IyY53rVoBWk{#g7m9Xc~(*II^U z|02;2iUg>oqmkcKbHf#PRqvK&nejcQcP0@P+waXzQxt_jvQ@`APDnVPox=X4`nlXn z!$BO*i*tg-Jw8&+6Hv5+JOM-!TU`u!Qq_yP*cZdEJGQ9NewO^vuiP9zSW)bRij7ih zQ)xlwg zkDzJPqk2RtZ1&Nt6$WV-Kz*(ilexlDigLBkbJ_O5DL}DBk>H1Cm%);9CsaHNu8L27 zP)#WfaBJkEDVtSB+`cyv6JMW5*Vpv=Kxhes=qHt{xGu% zhk6LTR{#k39v3@JBV( zPi5@7+>-gd_qM`w$-leS2A*M?ijT5g58b=)1gwG|&zbM{V2;-6+_Rd+^5NdC!?jO2 zV_l#U90&poMW(K=gE_RQU!#VSDi}o20gwFyRN;M>&fbpqtYJp>MM5-o0S%?%u26v? zp>}nrK^N!@N_Pg$X(|sB$7t{PNPTE=9P?wB=Uk-luzT>4e<&q0o$-85%2Dm}6X%k~ z{MhBrU*W^64^Ac}v`l{&EWvj=nCXLmb2wTuYARLL5fogGs1>|{NAv-^1gmSl7msU} z)tB`lPm6}V-os9G>|fvu>}vwdc;lV{nl4PcW?i}!kK#S%?Y?aKpR0nBZBbzT0`7Ck zR?9~RGIj?Tf{)_Hy-?%eIcFnUix!c^E_egT$xE5=DCo2By7uAB1zTs;*cGqfLXHV7&iJJq%pj_=KgLy%!G2}8I-Jth{jfd8^McwX!R)>dx z2(M$GvLwgRaky4~&4gDObJ`A|BmQ}T)_F(9#<0H^Xc$@SQ*;7hpCkTyLfG=q^m+oi zXtOPC7{3D-4S0xpna$HTOWEGdaC6_dn@*^B0Z#gFoaLy_9Pb@;F|v}W@Sjf!3px#@ zh$y)xl7(UXYHl}Ey9|!S(~B6pYUSvk-%R7Q*g^^-`A4K1jW@$x%B>n!8agUT3}a5u z|0qX>DB$ER<6ZO`h}@+{fRL&P9(RLctW+{gP8_u~Ta2Qa<4=7=gCdfb6G^)fB>+iG z9VP4YtS4iu0??5j2+A$KeH2#tOk~+0NB>OXrXY}RR3Do1WLZmU4$`_oq2ny4KBGVF zUKTl3_~yyco3}~oicxDQ9{E4kY<+^X4fS~fUQ++upC)*`4^)DcUDU5W!j4*=BZs&K z=zIi2jyXTuZ9TLPWvbyrkJ<)ezZ8N2IR3@!>*B1P>@VH?5q$U45qgo`2Q85mK{##G zl#eUGdf5F1|LU#L7@n6*x6-IFpX%t$O=;LwFMV0%wADfwDz*v5B%4=ojB@FHGqNR& zH&GLpP#ber?F1C?vb$bx@-`~1Sv*9b4cFu2{ZKYat(gjow|c%?&X~0 z$Y$*0)=}%I7cQV9Y^`^WI?S2g!IPSxwH)nZi*DNN_46njJ^Wxp$rlN2V&8z6kl;Y@ zv47_4sdutis%guAqefzjkE~v!kq4Qu{yl^B?LM^WYiq$B^*gLq&zDIDEN!1 z5V>1#i`WL@10qLn!oDjFX&gqcSjM#>vA*zQ%mDwu$gJpDH&8oKGax2t%M#4_OT0=P zqx3|$^|AaVZe^vFzHX=n!jS$eN@@SORGC@=ZkKZoWGVut$lUCYaMcprj zpx1MkVQZtbRu`pt$Ak`9qaEZ=rb1Nf_n+lCrF%vQe8p=TUbBGTG}L1r_swd12Lqh; zp2c3%yKaPhG=d%1|6Ix8Ua(51ov^vRlOZfzj{ObR{w%uY@qiqNyCsQW`Guf=GT7XB1@~7fu@S1Tj zz}CCRXgOjPG3MYO=a{uF;d+jkN_nlXROrStxUBM>@KD&Uktt7)C2G&fT2lcr_` zu>r6tL0>A@P#BC-(L}W*&bcKHH(=q_2>8bs?vaityVH)<@s{uYuee{TM31{S>(KY# z60u)U7&UV%^(8cY3#H=Eb|eT{BUboR@adaOh-F(mGVF5XnvGF%@6E|wxQCa)8=DNId2EuE7^6Al-5Yqc2Ju$I6K>y%+o+=25 z1`=?p=kmAq$iz2(uV>KJ{Y5ujh@y@Eu~2mzmWLj{;}w>(US&cSIdf3iZ?`ErVrnte!hXUIk`aw9;+L0JFfdXEO3SmY09qGG z(Nsv4ZIq1~pK2^wO|7rZJe5mOc8BueyEleR`_=J3mT?)QeUU9D@%s++F^?BX^3j3# zLkVhd%(g=QP)bhkAF)0xkT;~mJaB>UsN z48OOPsG#VL$Wf->-dme&wkf5S>yM9BTk@YQc+UoF~`=T*l+!OQ0)yT?x z47$ik^wXNMnzLBM<^MNELFTeRNL2_3&i0y9LsxYY@V$GyyA-2Yb%JMm}v;lnq76p{|{6~eo!O9m-ES1 zso_IVg3OJ49XS{d*XM%Ijr6$;S}cpwYhX2pw>SYg-6bFf-VwJ@{_hkqmPtMSLLgl# zJU_~!?Ta1*+3JhD1z%9J62~47eDe?bH_uQ+_7;}5cYBJB z<-u(IyEP3HIFPN1!(%Xl^e$FS6i-Cl8kSAz979JFbhiK9-5QqnncTJJJaS(wKhLb3 zB+9PNz%kwB5bFw4TB1_U6X&kkQGvJ97yU-ON}heebBC>J#F;RU-tdbk(hC`<&9TNk z@nr$>%gJ?FhDkfZJK`PYow4?qxAcnb$J7r^u9YaW^$NOa5#+$Or$k zW~Fg-ne#tC>}=4$&T@=(_ye*vccMIih%@ker04w7?!TcI?mXr#dg49v7ti6#d!+<< zXS!8N?5(kUj8+-OG7=TAv=_i*@1w-=G z2hi6YY&t)Mo2#zCjdb~ExMcQrFb90jia$LJH?gV~{m{6ysv?e1h0mhY?C&584OzdB zUz+c7bp?5Zv!#IxT**pOiEEJa6-nYhnm22_lB9S8IJDiJu{+B2!oXq)eIp^isL20% z=4Z>l&saN+J2JkVs)1vrmj%@(*bk1)T;#w8NJ zrMoO=E)2()5zeV%OHJ!QgT73pc-XWSRQPp5Ubi~qBjqD;7xSX~b$aYKN*(KG<_O2{ z{VF9^L)gv%7gd(SMU?QfU1a>N{4ExXZ!7zv+jZjzl^GnUEQteDDqn^1sq(4#rTC`% zeHud%thIaioeUnhp|T!sp!!eZ2Ft8#tG$wcu{;;`BZWs3~To^wYUmgQCSUFQ1vfizY$L#68{zVD*Gjpoxaq*%{ij# zWiPUtF>Ot{x{AE&c?ot*9e6x|@eLjyBv!O%Xw8IB(Caq{aj>ACFAr>>y=G z{WP?H8palP^*V$r&80<0KCp_nhDw5P-l4C^sV%T`TJBb!{z~R^0)!maGUoJ;L7n}f zBu=mT6{nFe&%lYLDr%#iP>;jY+{V8We|gBzS0#bv+utgVHC4Z0H613z#d|>AyZm;% zFkGvUO2MqKS68HW37xlhWKNcWZhQMXkXT)H2rKCz!T#{vqwci-FHT5HmE;>4&Sa-$ z&p0!FN^*zLd2^=>brN*vIG*T5TzRO5vceTa{gIq>s-h>9H1(F;8=-U)3$&IQ z=cvG+>wJo%gc%=9qg|N|Q+8b^0*s0u=_+<($yS}y0?(}z&oEk;VI$EOFT0KI-_$w< zG!oOP8)%Rn?6t7Y+*_|r)Uvz7XhNCm;G2k3$m4((5d_5|v-FL6(MTxGf8rSUCh`>W zGH8I1QdF2fQTNec$XXb%65c-N69Dk}CeQdw(!PH!qH%rlDCQyB7+=I!T zsf!KiE}yv^w1QAi+G1XZeiqMJ`Wfb^9F*8Wk+}${mYAmUOOnQJ=FK zYvph=!PN(e;9c7R=N3mn&wB4YcLLAUe&BAVh&HSXyJW1Y+)U6Z>iZ*0g2UD0rhr8^ z+kt_lWyeiOn)zfxn93CHuV_vPyl`oH8u4t#M=uN7(-yn9S$5wQQD+cvEkNJHTxZlxs^zHYF(Cq|7x&qV^p z!{E>A-+DY@W?%n_pZrn-Pg&!lUgCw{hM5T5Xl16CoX5bLkS^`_C(O2aZbQSzSrQy*H;5?mC3tz7KY&V94!tCM(&;G!UA(dt2PL!= zXOdJp~K-<@rv)pCSvy{9pTj5 z7j)@+Rx@W{?fpqQHsLf4-G#e@o%i&!E~xpTRH-Zwz_b^(#PvHZitxIRN*~# z3zl^dyFpk-od+!Yhf;#aX&qMWM!!qSU!EF6Tb^B9zBk~|N<->JLRKP=l zc334?=$chh-v;D7yaY}|&Y9M)vN%lePalMWBk$i*8Z`Z(LNoucGmvX-5wz`|+CpRP z8n%t*;Ytbap2g?$c=iRMxom@tb-|~TwZ}V9eACKyCXA~*DkJ9S)9~NXs^S{9F;Bt} zU(Q#5d@jW(R=+8pux?odN6J)Uy*m-1_iYC`q9vYuufu=$SJJ($J>D|MM+1S}^7h$e z9>@ME?@TB=h<(MdPjQvru6e&(^kHm!{8G{V7r5wVHRsP|M9Gfpwg@c{$gRv~-@}d@ zAECU<^(}bQU{3{404h2LHYD{^2bzDr(tGSSEO%*utEqIkS{*A3Akf6!hAPU~di01b zC$dUEb}&W$rO?<_wAC*ogc7}Z?_ttKYb_Vv2cDl< ziH+SxiGr~5g*IT~KX7U6^Ia)5)hQ0Z!Bzevwe`qYCxN9>e+y|VcZ$JRk(L-Y!#YeI z0yLmKecs`}e}gA}u0Gjjag#X7X1f#7**e^20w&H^)`S&0n8}*Euv8jfgpznSqGa03 ze>8~35B8X>Sc9y%8R;`$Y!a#_<;DBB0wrXwLLMdApVX%OTSbnukZ+SMJMord# zESBe7b1;XI(AdrE;37GlepORd)Avv@k4sQY=53g3m`s}$K>Ki;t7G_h-uM7wWj^R8 zZ#}AR21$3O(fKESa<)`7H5Hfj5-snVXXNqO@bu1GkC{l7~h%oVwXX^Q*_9Ivszk@D5K;8&;%sCP+c zIW#mL4thU;i3!Fvh%#GN@~Q*PKU>KeyNxu02*(SQhs*%icHI^z-+Q-iwn;?wqcz8O zvCNAk7zq1{z@3^ZPoo5eEbD`e;suBZ7OIw3U+o#F4c%pvMUxQLfH1Iz<2lfr$r zgPTdDJ$q1R^M940c4C@O(+oF$u1m_^vki?mSFa8&C)C!19N-Ji>p#<#|KjB((6Z)A zuxJUPxQaQz3{qO3w-WUQq&S#Z{tb*KWY&HLin%4}6~mARd@H_;5e^P;rD%-Zqk7|` ztbhTe(-+0t&aySVjBOtZctO$i%ibV}`l+De9m_avJXN2n?F~f{4gBDS?%UZPAK8?x z0|nfy5|}dNbc2>vNi-QW1w?AFW02qY<@@e@sH?w*5S3F>_31D-9@aN;wSwpYy!Tr_ zvTvQ_FtP35Ve!nTi++_;>YQ4T5|tPAf(D$`9dB~sdt}2|Q*1${URXF6EA+U=#?|T^XQywbhd2 zTb1Ifxd@S|Xnc^+MHK57HBgYog;Lqk2!vl}y@nm6EHP*9>2&`g8a(Zzn-=O!B1Q6)aMtW&{0I7)U$HAr-(D(V9j zxT0%LGtlG{UTt5W7_Y#cbyPWh`~6yQu^^|5$18PK zL`n2NXux@0;MI>_vN<#j@sU6cIs_N37fq~!r9~xF^s*0ZcZ5_7Ed~BCCCfi*N*sHqv3%tW64f`C;mL#D@N2mXStT} zHIEY<0oX^_BkI4@Yf_3i<8$c*PkcN*fsx1G8GwxHE1bNdeTn)}OLRB=I{je3XpV6F z*e~g`bVO(D@8SuZy!0+kR3J1tQH`6_&6MRvBYPb7Sv{g*`n!4pCllXk1P9@e{@M>n3EkDcBc= zee=knG&r3W4fg+D1Xsyg55=#eOi~sco9$D?Giaqs?k-rNz^b@V*NaP&&E22P%gN2> zWj}`?@TU@I?t;Y%yppJCs-i7-&w7#TeKw(#^r^baHo5I1pTAmjg`NGP2e|q8Mq_J1)z{~$93u#Lh{F! z;oFF+HdV6_{quf|v6aLVO;Gbb9OdNdsRGjK3z3ekFqMUr|vVcu6&Tj z|D7Bicgdt+9aF)PA%c-6=!e&lN7&ytT6A4{k+qiR@KVf$GLai-_( zSYcvwI9tGmm)F5d;4(>ziaPv<@EKcR9LDHWyL1=sO=zq8BJfT%;!f$1+CdEhde9Gh zf#a1_C+R51PkjLs)Tp*tc@wIWT{MFl(ms@E3$@IFtVRug)o6pe`TX!ayW_g(xdP^s zMX5PL(`L-Z#QBaQGE z<}LEor8oliqLqk?mSF)bPu5%~B~tFy=68U7{$WSnovr-sl1AZnj5!Z#dZ%&?}LCE zby?O}FL|e!C_{F~Xn&Aykqr=u711_f8;oJ4x{4Ll3VgeFHJs02 zRXRRY2PKm(4JBPAI39$8$BUOuf% zW@F_6EQFCsEsh0$;B+y!gvPzQ+z7gFHe?f8F@1aS^=cn6looalXn(4uNbw;E1#;z@ z67J=Gr?{nD9}O3#MPsMc%9~hYf8P`BMF|#&@8`#+s%5+=)r}oUH3o2TB2b6T#drx{ z_kl?UNLv@z<;-|m?a0{Nj<7EG`!ZFywBZ2BdN%kx!%_Dcs9PXq`%x+e5rj}v5 zKoiPU$$$(#c2B_HJX@~a7#}XshU@Zi3Z#u(Yc>|?B8xh{$%Ymk8b9>&@Cr2p+tgDC zBW_80Q5cwthG&MOVQ;7pA78ROJ^P$zEIL2pb-q{dE6r?f4if1b2}g+7N7(PxE4s7B z@ZA&sx{CWuWN41RANvE}F=BXGEGht>AZK${qH%OGiW==0O$nLhbAU^gCy%Qu?z8Q(F*dpjb;6@&l{bC+KKo{aHFr*ksS%3`US zY+_}aFC$W1MKFR#{_pjeY}EE#bo&2LO~8rLf4ue)6qgbBD(wKYe>cWTKT9mI?urFs zj8^DH9s4u?$_HNm2zQoO3aN7c_h5{TpffW=I1u_=O1lLshxi+KTZ=f_x9l#O=Uz zvn~xfs6S~Xl1AR)y{6yuAf)jfI0L9|h~flL-aVt=Gol6Sb7b~gpaDwV3yK{;ZV}{{ zsjz16eGh$9f~n2&wAF4^;YHviEmE7GAdQ(R)tbbI@wE{;Mg3%haf{hWzV5k_amhM= z|Fz5{pT)cDzG&h=Sni8{@timPbuX^w)#)}VCsID;`=~}8t>sjiwd15kA|Jp>3=XETvm_TeTY5E-M>JQ!c8ti<4awh_n(!nC7 zrvBv`lX{LhEQ1@<8ZU>~g~(cO2klCfc9C7oX~hW_@b4{|7twD*rtq|bHw$8vo?kx&A@xJ`EQLxNGTWUYGo*kEhRbh0uRNaDB$;CZ*f- z+u3hzn0ztDcrqYR$NreE>gM&22yj^fYa!f8w33k5J>1cZwTIX}nU|J>sa3APjvA^K zRpXnKUwH47QCYG(9)m0?!`}B$qv>X6GultrUFpL;75v5gR7;2ph=8>jaO#eXIv#$S z92(*4lRTmvJqC+@p>KMGqZ^F(XWo2m6+6r?DXnBkq;gPNQUPuxRiQ`vui10`#&T*n z6B?}oXOEz&ROtQo-)GM;zQg?0LL^LdvIKz(?|lz{^xI?yD@K*uyd`?d7n!;(`C){f zkvG&M8VyPkT(acQKAE%7Fy6DzI9^|wzTE~*F9vjhB>8;8Y_Q~hnQ7LWB!mg{!X!UQh?Sk@BYEQtamaT?Da`gf|FQoVva%$)2{4}dRq5i6 zG<;VG!r;+|qKnV?&$e^*gB$q9?*1|jh27uw84Ssz_O<%&@|RrcRBl&^kI9?ABu{sv z0{o)OzXa|^o_OT*=AX;cm6(BlyMWYIDk5Q&RR7VJRFu=nGq(#N#E>URDPca&)W!dj zp2SXmH6c0FGy9{JKHkIaPp|*oxQ?AlM?vgT$wHms-4$c=lzFk8n~foAe!7Ju{qO~D zXv2t{7mECP=%GLAwQsJQEvIe=kiDR5kNn^WO0w~BA8g_hZ4nImT8#U`Dx+8{MujwS zt|j?p0MCnZ?VTShf|Ts=6rOINSF}S4o(T3RCS9J279<_(cK6yrHl`hY&*WK7I38*} zAGjlMQ{blm&Uk5h`&b~*gbp!0Vn#~BMJVL$k&jGv3`f%+jNBFeNppAVhPvw0NeDoW zWXpNDW(t4N)yuY=kqofI;*=M)BOm&~w)lo=eLRS7nx2|Ofbxu`V!n(2{CT-~!ZVl} z$z1Oqqv?n}Utnyo@L1y#uIq?tgW<~v7IFMb%2yOHppQNb zguP?=$RCL$y)A-E80DoEA{Z%iEG^$I5rUO)dzb2P9+ z>MJ+ReLgeBclh7jnMjx>$?eI_F)X+k&KLeAgy?iwfHGV%!c0urZ8)%wU*FKc)LGPy z@IxXqVHO(JO)%v*$oc`%4F=y4mkX&9mP(T2^9ipt{z-;=yg@x@zc1fs5f>s$L6NEg z;k&siz6xKh{Kk7Z!oy`(Xu~@r`IGAW_`ZZiQNq1yJIJi^1a{P*ENAU;8fGWyUC2j8 z(+HNk-)03SHqVZWmq!XoG>&WS5kJeSHyzXI5|U#aa-r*yl6aVt zG>#uHJcmCqNB?}%hpg{>JL_w>(&&s*l#O02{m%O|BEuEDuRW5{hn;3P{?fx^e%E9A z?;{Fa#T)HWqW(u?G}CIpc0@}KDO|3ZRSdhZu->K_miLYX7?{b?xIpnceKCtj_e z;=LK+4w6=Ie%w{45Y4_cUKcK&(b%XwZQzEKs;43S_k0pwYX|T@t0vrqvD1Kvl`aG| z3vkf(c$+wusC^4Inm*GW8*UMb!1aRA-yet z9;}Qz9A1x-iP6xUJgU~q=$h3#9Gz~$woXEPqlpN@QJSs-u&rF9tAVow^Ws%cHep-e zt9-*k5xJ4c+3V(G#!}5)cHY!@!{-nzm_pPL+haw@PQn#}BgzTc$|R$5UFXc%VOtNv zBSDu*2>KGJb`@53neBpHna}WE=Duf{U6?whc#=l2D|s~`_*@^ zYJ5R20kxARE!v&?J8&oU74^P*?K?1#_)o`D;MJXBUnqQ~7286wT(BTBXuY)8h#7}` z7%(qhvDAaU-}SSFv=mwpet+*ys52jjI;hii)%XF%Nm*`>qMnmSOq}eBTomb3&wIhk znxcpouvkUWGMOft#UcdqQ2O^+ukYye@4T?z4{}>nmLdxxvi5q2nBT&1c&=+cBV|k5 zn%>K-aj4c3VWbD`Pc2`aq(?_j~$>IPJ@$G`1x|6|Ur*I<}eT1J}8(!-d*_N#qg!iY{g|X)6&2sC=ELLhh;Vp&E2TRTb99 zqVE^0-98Ua>~q?%UapS%Q~4)v6Vj#XLSNA()lG6W0TP|Kq_u?Ql^G4I`wPhZ~CJYR#xhCi_0Z%Wg-%H2orqI*uclvfRdt7SaoM7ngO#)BkPkJno! z3MSn^^+|%{tT|YPG)&{9n2&K?;&sW$AS>2dVP+`QPS&$BnV~?g7TZ>$*~WhQ96$OP z?YSX9hhdtut_Q=V1S#ck7>WrpCAl6LzbZ62?DE+uHtD}4!MtX^t$XG2WUz{Z-Pw>2 z7mH-cj6~Js_RqAfR$=2D32=7sjGxbaOtvI-uA1FF16V0A_Lc4*Kx!|t)Uio?zwJ~ zVOf}4cMVl}QTeXA8}JXhE~`z}0haCarg|ve+Pbypr@nk^`OdoQ&AaX+7%BEsrSAZ>Wa1@aBeE^kV3RO|;zxt6&|9K|I96F)SYG`2OwK%%I>e%pvSL zPJ87`vI9WyBlqs;^GD8Gacn~$;*5-RAc>l}1t;aZrn z>;>24r{sr{>YJxldS6a#*>gM!gI^paRZBM|*P~{1J6YoUx)GOJYy)9n)wFbrv?%!m z`h-3vO9J~ELDK;cLUOT6njzhid=NcAPm?8KUmG!5OFjb-g&o4Eu3jb`k(MW4Lf_Dv zWQl4_Bj^BM(h^vTajCv8#ExtP^KKR% z{g*4?gv}#eDPF35bQ$#U@WJCFkEvBBU!DB^dF#~-mw5L! z${X~ zO6!`w{+`r%=vBA08(;alTk(sX|7pqo$~xxSMhJ$UYeyrlM@+sj_U-Dh2^=yW95!)~ zpS8xEs}?esheWN?bnVY3;VkUk%F?o0>GcdShh^6f1hoZOpLTeowX|Y+1MB9GD@qRt z3S$oJxZV)l7HoZH)En*aY&%Ri7sLidFTu+4|sJOn{zs=wJ(9iy#2XZ4_jmjmP0??$IS<+sH*xUsznY5lZ zG2lkvLPqK}T1}^1Oxu|zS@U7PFcpt%IcjAV(CTg zP1rFdEMji(HoY06ORIN0rf&8k_C`Fu5j1q_9(DZ+hC^cK+i1Qwat7}!YnikF`fV;$ zmI#wyW=)?hY-A9a49_eFUL5D5W)5Yh zYI8#S+^XCtzpNMfjMPtwf2=sS?+eF#dXKKCtFq|-xRyaoyyELQY1Shuj*TEAyr*Bc zkh;zb-5XpQJj?ZzTa}y8mFmc99G>A?Rb8``YVAeuJ#0E`DNfT~3Xu&};ksjPn$Ge1 zh$){RCtnLndLB(=^D9fGxVn5l)!Yl;8*`r+pR-2gH0L8-kHSaXOG_%0ZZJbeF@mG* ze}5g3D?^J*c7K(JE$uy?aZs}JJK@#94pYMeyck)0XuW=&TB<2{(>EXFHgXr6hBtQ> zwu9wnU7DTmZSF8rv|!~4?S~SPVl>%X(_oSDM~!S%mjfF-i(cXhm0F>Cx}mDm1?j7# zqbqKs$fak#=+VvxJPQGoAeii4;2T#!%!I|ap5{RqJ~1RbD`Q(QPq&x0+6U5|Q_aN7Cy{GT z-hR!sW%$ayH!@C2c7E5Ges^rww)L1~5q*xKW*b`g%I0g_PVQop%g!I$RxsG^FjFLh zPu_O>iV%}3h1bJ(L*A=w)WT{Cn-tshchgnVzr8}oAh68)O&qW}-0@XErZfbO*Dmgb zr3&6j@Z3(FB-6M!fruyg1N`&Z4KKXR+qraB9E5k*TA5E7^w{&)1nw%j2!f5D1exxNO;;1fjOE?^`3$tiE84$matJ(;xao298{KOfxL3IY9`|xs8k@un$=d2Y zi)prdu|k?=T>hmS-A?V>*SWlb=E|!ImU}sLFk+{g(f?FD_g2*`; zL8@m@oZnx_AVyv&L1bdy(4!W4bHxs{u_i2$d9XC2^gX7@2@-zx&m1gphUWq^7@yA) zR)o)-gAIp)Yj6UMv}3=%jzXqS`}O_%R6j;6xGI@E1Gx()7rE0&?VPVjj_)SE0{M7> z93}>1cUDcA+n>uIYrLWba7_az>~ zs%)brWB;E!J_f@x4TmB&PuNZtzYTgg;5&;l?hOQ}FI66z>}&c%kt22DKU2%wp{F;% zM)q!T0#y_nL~j!lS7^pyIL+a4zY41z@p|&Ji&JXZ{e`;oKj$M)v8SKFk^e(C$Ebsb z3lVN{zV8~dRuHE#m-*7sFhlh~VubcwrqeMzwi}t|vQjqj@1BfEyGX-KV?*O+p~WQm zHhv~yBWLBRbUWLile;f`H8I1qcyIk4S{qCHyygB3B$AQu-(1f2IMi~6D|JRbpG;;N z=@gTpK*e_(PR~4HK&1P9eQ54y(@%;VtQ+;kb{hOMu&nVZAbmwaJ_JMW2fe(rP}>m^4D226@^C`gi5BOq5M0??=ZLL==}M(bT}U}8%o!m_=T%-gg^hE zG8D-V97|4)@OmGD3rWJM$yhO`KZik9d%+JQW;{*i$Bv{_Yu!x+2~gMsI~sxsdCZV_ z7h3e05Tb)thft07Q%G#2ng6clv-B095wxx2E$2OJ>gBh8 z*4D+;QL~@+JsmFsn<4Vri?{IoiKM!yI%?LFz9-}HrX0?>Wxiu=$e8At6|e2=4LeMRkw3JBFFS8(hCFL#OnA5yl#6PB{J@lqvwEQ z!EED*Fi~h9J0g>`xEXRiE)tT)hYB?HJZ14h6iSlQOV~=?4KwbN1QrH!ugr#v+Gq6> zZ}W%9y0vwbb9Z0&_+4+|!Y zMI|RM=B6*w|Mu!t8r@eg|Ahf7RMq~&pFeu!n=RMJN1*#f)oK|2*6M!3WBU%icmE4# zI=ZQ_Z^(V-PdYi2z(-xGPCYJo#``v1HCRR7Mz06cll*xpX%iIn7tx2QzxucKzYWoS zgpp9MYd(&-^6oUg=d+Okx_L}yZzdGbExk#--K4G=Za}jx^&0=f=K4EV_OiyMAzjR- z-c68qWM_=-3^c=O-K)p+ssaU{m!!_@KPSz37rJ`Mas$k8@xi6~IH}$KCH&@;tVIUi z<6>b-q3Jyc%q#6@%Tj0yQ_x!gN9n(N3#q_=i3W4wAs1ve2{ibT2HZI>^2|E#ssnwq18HhXVn-rN4M;x8l=YUuWxBI>dvABF&I_X8KV5p0BR1+E zX1)NmnOA8RVkFOH0Se274B2lQ1(Lc*XIN<#m5y;MEGZ0OwpCG^eS&1Yv5 z=b#;~>7stubPL0|)jrgVqc2DwWPdbSA9{k3Mpcb80Ww`ze<4Sz0bn9Qs48xrRST zUezu?T1Zqc$#2aU*ZVwd)h!CeklpHT^cFR#o5{?Vf^xkqocY8DKYGmWqD0@S7hm*w z*SdGva)rdz8tjWi-I8~``r|b6fA#-}HMOem7>@i@Va}(d+gi7YPx(A;E$;)?k#=f# zBtr+(ZKTO(qtI|{8XsK-R+NoH?+Dm`VNhXj<7#YhKSi}gtd$kdMt+f%1DH?6z|s{y zcUrf8G7;np^$YY6HK?1&mp$|VCQ4k6Jr}+5VhOV!&~0d2!p)#VO2>7fBR7wiAC=F` z+YS?9)iYu;4EJKvR5sx8niZD6e{Y}!4800SqYaihMv7!+M<}>D6W+q6f%3}^cF}Zb zSkj{#kBrZc)g#j~Qrg+mNt1KmKY!oc=ic=XOoXq^%?+(g)le1CuZBv7ESL0M0*=u7 zZ>HL?8vdL24J9CH{*(S>xYuy~{p;n<6aCDv)bP&FDD?;8 zE8(waG=>_({^nL_gVW8G^drN3!}YG{zizslxAJYb^cU+I7_Yf-G+Q-7D;a~o{uV1K z=H1P59wz2{{I8IqSK&ld>|Vj@qfE|F4jgR024?WB`4s(RsCX#KaH+F778-gO9#idS zvWBwYK=T!N3y+(R(fW4r9+w^Tji$<8S#E-BFUX0 zpd|Pu+emN_=qI)_O$SfTU&e-f-QRTwjG(o-uA!lAGthqx6%5@yH(h@na+HjE^=Q*v zL68fQWLfElM|mTveZ|2}G=s%<&qlhOwPT7{?z{kOI^|`e0fRV|BNDI9GI~~nGL;j^ zU!#n*Gvz|)HAP~%kh1qE+SI-Jad+!(R$_q0Kc^^CPKP2+<)xIiNv8PWeFf8CJ~r>1 zw7j+a2fsf2jYgvOlw&^6TX(cxO(M-nRB`+Yw&wdjBfg0US&Ai@t(oHbl#8eholmLt zX=~lwdNuId1%xES7BxysT1&)7Q*NN)=v~T5pMMG#0cy<1Lc)`X;82JO5U)X+)0!jR zo6>@wq5COke4ezHEtdI_Z=nlX0E7D-gxG}5x*{oVEfzPX+(IMK$CT?nA6oY%e$zx4m~$N;?{lzNVbJjh*BYkg0ssaRFK0iT8KXs!3_* zmrnZNo&VA1Pm>GnW$om`Z|}Yheu!zShAGh2M%Lb;tNN=bw;K^RNbCN>{@w~iGFqA-dn{xMix_+>p;@*h4L0Sz~4rX2O9>JXZz_JnBb~Y+Z0A2H$$e{yw4h>UpyONfIgI_#)tKV zu^oi%O}cEbjEZa93c>KOO=^{RDsigA`;x)rce?KYO+d20pIv&wLFZ*TgE^GCEf;== z#cg05aWi7-l=sDhqkQz=Z?2`p^m}yWU?r8^RtHOAU7N2_@Xg?<6W-q#OyLKMm#F|h zC$_YS$CZT$Q60T;a3ht_RsjD%cpJY*c0)FG-22SI_yichMJdbi=RU7EY?;?nfZzNz zH)u5-x@KeYk)$JS;-k~L_dRJn%_fh+ssC4G(j(BAo>{Z-_B1QIjafdHvKT42T>WlD z;<;Zd>MK5T?=zL#E1CO!XY}IRJ##cutdG%m6H$uj!f08P3fcRwS;=;lYYW%P(t0(!tO;e)f5M+8 ztnH5AVt(;Ej;kV7uaLQ_y?ffv{5>ETz(kJ+gyXS4n=(P~r|-sTMOa~&jP8vS;y

zZWSkrzFf1lhCP(&ua_T%b>w zWRrsAj7WEU)RFpf#;s1EOvxP_^ZA)U4xU@&x@67)DtlBmI#9Pq^L>NL`X^(`#@w*- zt;b3<&*=T^EGv6oBp(T)<3`8ngAT3<@ZJOQ+eOq3CjaLuPp?>pl`_>*I>I1=ksgX* zIB)bN2d{l(g{f|3?VPBHw)JQX0GQ>5R`)8`%LPNU0RV?NRHs<`@THbo)BiR*qOQM!2;zCKb zNM22n{>S_!WLlV^5>QdygImti6t6#WEc)1GGl~smNpEI%T61W&MQx1Tqg?jWnLF=^ zfOG4kk_*n69~FTWIt(&(^7;ESh8Foc(s_W)wwz5K*tbV(+z900bX;@j$%Gt7U;MRu zOBv1}_vnN)Z^SV%9cUS75&s#E4=Ws%y)kg#3IC21jImpE)yfjWZuH+Ld(So;xp)1s zAUe)6&JxtP1|Qtdywy|4P~2ehM|h33iVa}}tB<+L?4zZk>z}l6*p5+;Ju}gRP3E!b zU~}1Mpa-OhHxRKS?C)RPUj%ye3m?z^XUO+$H(5^b8%OewhGZ^S(o}8D4&PT7zgYQ% zusQmp{+qGY#Q%b#*4ASN_5WsR%6$m60N|sMtnas%~Y{#D4VnZ}_|GrPWqF8Ng82l-0`c$-v?zO zUhPC}io1ffm>4++cLKCOq^s$WfRnZ;8^mmKCejDL!TX;vi@)fbbbocy&BmMQ)w9TI z4PsWe(p$F?l_cJ74l)stWX9)Ca5sfb&&b?-{%7WAHPi@0SnWXeZn{)YE1uuj$$Zi9 z(_}vuA3v;8LwILGW#OflSMBIFVirzNg_E%pV@=^cC#F=xyqVO7yZpO54AP@s@)=BD z|K8Z4wB*{2nF7nz!SCizeu@9q03+EGM&jI$zIuC27t>xW;Jy#PHMDFABySEa*Z7o> z{M1^OXz63OW;!j2;CC_3--zE{f{cCcX8#!1Nvoe{8ZD1f3TH$ODm<@m7kk=Y=X;)h zuk_e^y;I-_FppRUuy`O^_%B&l8dksS%ZNrUj=T}+BNrUKqq-9eQH3>gcW*qBx?z>b z_3jk=b=;!Z&SbDdkr8pEW}?OvCq6OR1;HrEQQK@xf9Fp*$X&*LtvA-Jg}|-mvE5#9|Ocid#!XK&v&*!i_54Xn&+e{*zp*?$=qJfU*wd@w?Y~wog5<3s5A&fzlc_!l zllTb)Vn#ffkPwDU!lbG>Ga^{pLNHvUT#kIWzm6YY!9d{(g60Py_kc1x+n9gSE?Ls-VkC_E1`Dl>rcOxc<8oMfsWEo5mOV!EB_eQV#A zMv+*UDAN!Urs;SSb6nTmv#8WDP-i=cPBIl^#qT)t6e)VSY~G*$hIfQBVa9G%SiqwJ z0r_}RVe!CRH0oGgIA=CawD%pyxpWJqC%i*O~ge4vi0 zHF_&1WY4?SOCelLJyOKQp;$sjJR#*pPMa9Y{#3lb_$~J#Q>m|H9&R~CAfg~sI52!T z9vH**a-a41YyVko*@#5&ptDL5Ul=b_=|j@=ZslmGHI7TtqTBI!yC|~s0WudI!U^N# znnv!kre%_P8BVeHWm<90arN`=%)}A&wVFJus?3z?f#>#68CQ?ojCs#}Ah;$Ne704d zOdow9q2!g)RjO}0L%oQvLb8|+mb-3GT#jc)F#&MvH2k}t4MoHmc{{j&Nbm{-tJcpu zMD~-?&R+(R8umW^qd+@L46z!B{KCLDXd#3j5WnAMs@NU$ZR$0f8pIr6N+7bwTSxoh zg&-u4Nf}CkJi2XY!;qyLcn2a+p|{;+H*e+Zx09CBkK%4==3(}+N`|yUW5CW9Xd%pB zH#4i<`N$P4YhnwB3ZcoMFui3uV+M&@)x!AC34XsZ!C)D;fg@OM@GDZcKpz*ZRJ{LuT_IuqHU~CD4)=Qf~$Fj zT>_3Cp{#*^Y}QB11kecD-P8$|eu2NfqhaXj)}eJnQF`cwv!ACyQ4?x3^(jN(@KvRD zZy8wg#q4G4SWq^X0R_yFJ2L&fVhs!hzK)R~&&(Y6!e*;e{Yx$leUd!l^nwXbL{fWw z(npk+_eq+i=@y(rgt3^Pr1`iZo^(zSUkhmz=XHaYj1~MB=t#So7a!J2{a-+g?vE}( zyGSo)`M-;r7M)vok*kYZx{Uu8n~IW|0;P0Mu+&W05r4s6p5xo2)meUNT+Rh_KE~q7 z`59QTax3xhyvDSoTF??)4o)?rn+yMZCtmQK9)2eWtj%u*aC{=T9E>&j?w5a##TI{6 zXP5terc$T;FCLd=A2+>_#O*Tn-&T(re;853D&9@Au^^sw+DWDr)QPiNpe3b(2f2?V zPn-G01}jUfF1ThORBd!M77RlHFO|0W* z=~1lT6go*Nt)bKSNb4zPeFcjpQIZ$UV$@;C=2h$?fL#C&roNxN~Z%9J#iKAvEABpdWfI zBKzL`bwmwc%Gs4u0x|!DqLX&vl%lFuA8J6HCY8u2_T3*X8M@a%C z_nT?q@`3<5=RBEa?81)=7TqRl7(wQi6IvqjuKD;N*JP(=rTRX zaps{m`PT_^9RWXKk9;~j5B$iEXL00_|G=X`^N^3ecK~4r5AX;)02UB~<7wCPyxuW- zr{KO*=diP9VXdc!G55Y#=Y=f8Y|-C+ft|-GdaG=;^=Pv}H0b8**ufj2*82;dLK|S& zOF&OXm~x*|8-+ciJk*SCL3{8a0q0VVj$&IE_r*zaHDh4Fw7@i)$QJ=Ivm4B z43GlSkI<`ll>n2fzl#E2K`C&7=heV0DEf+G7$se0aA!$@iV*A}{!#M~&)_t0rXWUH zQuU=LQboJy$(`E9D?#l|!V+}vSF`{*O{yl`CF;iW|17zqgXGEReSAWn7~kf+vjhF- zpY$ebjsAgN!)uhSjSKOYC1lnaND6Xq1fD<(VA%#(Op`qvcAroSg%#2cl!Hz}yYM!d zy%poc^VUufi`Phl`Ef2To5ge21Xvr&+&G1^`*Qy^i)AAt_OCHB%76755*}=Eo}csC zsjePG+Hw&KImUv4QEl;F?FeySZ(a|tD+s=p?tSNk$YKQ_ru(sGoY=-PI#LDfC%+ zj#Jz;pWNTl5EaHnQQ3;pEs41vd@cRi==$-5sI`_;9%#9qkypa>x{;f^EjE-RgJHUb zF+G7eNR7GmV;7>;dj1imV#cr?vplXmqGuLfB(+hTV! zGNj4s{$J_8h`jn$N*KHqld-pPDfmeGZ{s=$GC6hvWBoNsiYy4%=}!~X1T8YyxE4f6 z1u@M;mfQ?_62`&k&8`oJkt#HI>{07h;9yX}v>Dyxo7_;o_kqThz+C#Nag%Op84&|V zI*2!o%xt~FJTqeZ&4m(7b{m_mfkkSf>@~;k#nGZWsP~qCF6VvtT{<*MZl*qw3;5s5 zvSYAk{Qs{@_b2uv|6P2~y8Ms!G2A2ImzJTAfS<^8Jkqnz%pQ5oyEmqv)cM7G0Pp`6j@_xjR~=7LT~%VD3dg7BD_q+l^%HsXdURw($e z7Z-&YJ_}~toYQEYiK*5Da#%kg=ij^}g5 zM11~oH&?e{eBO%pQWfI!GaHczsgoB;9Pq;3SaMzDPdcb}$Den|`m}{H`GLK(p$XK+ z$_oD6c&Ddu-ZX?hVRd54!}k&68^SLyNiOxdpHm*a4;$YUFYMDwmsV6L8;sL?GWhX^ z@k4m4GDeV)*?3JS)(Iac(=$hCA@ilM@;b#2sZ_M(rT?$4ujZ6R=rtMRNQB^_Zmb(V zMP_A=(;|q`R$jOGu>%mPM}03VGUu1}y~k)KBSO3d^GjF>&LRCWBbqlcu<}acn_x}# z#k`mExGVC~mW(ch60B^;+TkiPIcUcJcjmHR9^ORVlc$?9RoRCBMe*(T0o~ z!WS$_ummn8!!v6%J~7J5OT@QJ3vEC6Z&NWtmgN0ly6)0YW9wE`pcv`!05$+ufPzt0 z-hlY=0ECK>an2h=b7IHiBH_e_X;h30D9J2JNsaWEP&$7KsB+z}<=Jv?r2TI%#cU7# z)&3qfpvTvktIwqG4j@&A#p~&ok(x`c5i61^UR&&0B2Bg)S+_97;d#W#4dJI3CKvhi z<`hTgVJA1m$!l8C{ZAfYx;`RaNPp7L#!4AyH2fm4npYj z5N~Do@YK=I!39G7`kV+~gs1-60drXY7%~cCTMsIg*gA0g;oB1CT+EdowiUxRzzcIy z!XU2^#_)`uUgT=ep_&>pHnRuh*TRIqxJH##IsAG(@I}AsR+PUgeFTY}EovO+j$dy1 z83h9dS7PJMTQ6s+Q)@1{CtI{UBS!oL3pJ+UTrwatruh-0r}8v@TOjksM#}O1=L2Bx ztZc*D0Auu2UK{?%;*(;px0JhnNmiX)GFX|JNM17A99Tl}00 zxst>F#5fz^`pTsEL7!m(O0G^u`5FMq{5o9o`0B`4eZ~6uuv)JX2L@XX0t7R zv&XJyN>7& zzij)&3LDFk7}NOaC}w~9qZ!GuAr{r51X+b|(u!t6eS_ny78te*D>=0xvL^;VW` z170lO8Vk>{tL?EaTg{S~e&y4VZo@k59ZUwZ@&i$Y(O>UMsJ9 z3VMjBTG(cIbTd32E1LDyFmp6Nl3$U?bJ+JMzW$y`X}pE*C78o~-K*m>Q=N7x(*NPlA3HKL=~Yj{(|%)W zk_Fp$1}<ZN!y&0%pB;&-3$1lk%~SM|?+7dmTY_g2Yj&u#Ydd^+bxM zrgT;7yrBd}=u%VsibS_dzl{m>Sv`kS6;BDS7gvxzn^&nJ*h?ID!)=q|cr)EwIv9<* z=KY?oygP^9^Dj#WCJ@nrY1%1_tw1>=CNtHBTOC2z?LY6&TkSh7-ibTwtL`$i1(Kfm zhkwp>TS@8yw3fVjw0TDA;~nXkZ~s!dDO}LtR<8(J?7-F-Ye+-h+w-#X#Mn3I{Ai3~ zEcquxhdCCDSF5`~IXf;tE}AM8F=yYC0JJhRBH4+UJyO>Lqv5`;@URP$p7{Aay!oE= zn}LqxqP(kB^c;b5R%~{x4OSIP*zfQ3_gNkI`x+-~_Mq-Abip-U;aOKR7@QqJQ!_Lj zny35e1IGs=sn(#mkGHM5#LfdLoVteRP`{0r_Sj@wyp3)?D2C6v^xclkF~899t_<7w zDzzJzBOkeG?8sizD&}rCnP`H2~_itAtV32 zPb_m|L1?1fw`F=DpOe@+1@b0_N=dBeMI72R-M=swUg#iM38lwuuw< z2bPqGu*_$I7Ax>u9GAPyF`1%?DhXQFEdOC>?KcamR)d{DIWsyl&jzoK#LwO!3RWwp zYMijy2D(Gg4p((iQ~f7<=jD*M+q&Y=MPrW9zZu78{~l3($lHs%aE7RJIt7K4qS6uY zG3iIvLelBR$^?eJ6bDFmj`2<!l5E+pOp=m zo&CLI#=1A959i&Y4Cgxh^NW;z-=PRDaHdabZ=n0AMSFvdy7v+VV|yv~km?-ay_WD! zG*s|}1D2`5oF5xf<9;4iMC&}rkJ^vOnBm#DiCCqvfdiFkcxd0Ly4^Y4TT6KDnUu#J zIGOR-{J#x8Rt)Y`L>5k=k87`@e^8tDJ{9xBKaTFBG*dICVU2oo<60}<4fF48h^0~dJBKjA_ z#g46cy!^HKyu8g|4jmIljcL-!b;^G7*^U^BCwjWDJFPL>5MF@PbN7_=N}eewpe={| z;M%$Tp(rM``nBb9V<=o-&0M}xbVaSyHi{(^;DwE=4GhbU9?1faY%E;i0r*}Mif#(O5E)^x)Z9)80Q6iu8;F4Z&hcw8zoAoT~*9<_e9VHt~X z>~UXGpdBOYHZjcFoo{er#~}gHpSx5vGe_8?SpM^?;LI&3fwa)UYo^=B&N~XoIbu9h zlR!Ggs_YZW?L1MU%9Kr|-ah$5L(F&CnlV%bD!5bK{Y`MX*+hVKC3!CAJv-FPKOHkL zk%%pJ1}fEz=*&DS3b&>CtNo^Fj-<1TIDH82!_!Dz)b{qoEsHbT@ceb$4|n0&x$zS^ zMzaM!f_{wA%(YN0g!#*6+AP{aC_j0028m6JmK_f9@&7)_>FXt1rt^-br>9S7ebr1i z6Xw?@XbmtWw@X9@WWSF2X3%kjvAk&-+CiY|iYRP&74f+JzRjGByCaLb-xH_L!e}7r z{G}XS_B}gX&=!M4V+)f1z(bqm@z&s?h<=^SP3ueRi%C4+x_bquFM$mBbsMa-f%g1w zXg+rV%)#A~!-+^`mG|e1+Vy;7+Ri7BK9rF;r1{Pd`4nFGlPKuCimx8EvcQ!zOb+0L zH+^~7xMk=j*4-yKeHms$u<}NL25=(yOy*+$gN?^&C)-+eefLr}*hO&{9)`X~(Dw3| z!4P|}k)MPaeAO7 z^H`{TwBDT#Bwd4{)WLO_?&!_OFdftbwm;Z*S9c9Ii8+mCy&%Eqdg%g1cjK5+XxP( z!qJvW9>l_v@q`06Q;j+OJ&w*AtWreiX3#GRE1J<4+xvVa$ZJ9({ym#ccmSEt0SC|ge^8;7yO!)`(&={adP0c}E|o-BEknk&)qRZyfeMIqk9zqxFQ{HS*Q zfp>HLP2P=f71zF$YhTLm(!KXmEDk?=OWg2%T-QtuX0MrS>lGuaNQs+)teRVdJz?6DqUtq)$@86h9AKHhmo9-IBI>jI;8t}>)smMvCa!8nF z&qb1z8`*J1bVPkT{0+tR+>n49qA!7|-tdgkXbJl&s7`z>?W1hDI z57uJ!8W;!L>+yZQRNK-Z1^Dpx6|nMUk8qCPcW~-sp-VDRDRp^bsG4>*Lq@~@2<;q{7=JcZMQ+s5ylIv6DS9P%2r)y=Yhhfs_{9^aoWwP zPl6G!zfM;3`;$%S??PQ`WQ&_QZ-rCC(sRR%vp-P+XY=}g(GomUohWfkjDQqQSsOQ# za)IMDGT+&@swp?)1@r+1yc1;~thN4VV!0s41WQoRr^uZDOCeI%WfGe&N7SF2gUT#6 zu;6K*wFj?2Qc{j#yFwP-6E z{Z9BhbFr7T2`2Yh*@4|T%scUQ?JeM)sp2~RJ(e8vwAmZu;JpGJ;6iK=x{M4Z8m3N+HD+3yVO zJe8|&S0x^A6R(ZFwuhM=v{lpn1&c?KcDwR!WXlt&YDWd^G%&ihl^)B^u3?HNr?H^4 z)%PA?gtYU+d14h`O3}t#+lzT9&=d)0A3KTjD2fDGz8YU{rW#x<-vcxMVN46OG$#>r z{MIYitEkchZN$~x=%m2y(vFKnJcX0Mw@@s}UVBDq z>a)*&K~tphT9}HF9k3M)kE5}YC^d>)P8Io{a{}m19b~QOopoy{oBRKqS*xOZZL+I4 z`p;H23Q%t*zqw{mF{Bu6)U`cmcHrmoImd|v3NMM?k_;=n8yRvnwG2q}It9F4-I1<4 z-fPV{c=4lwp@DlZ4(L_%V=D6MZgOi`=UHqDS51?vsM5i55_r7?EaCYn4I$)~>mKg z#Y)u&{)89anW2xtD++CKCB2BMKFXD6!p|d+xN7 zA`SDghi`BO-iJtuprkdK-e71&99#7$bqGHW2!_q)MEYICyys$Ess-tT?d?`fqB}#2 zE_baDKE)1tUtO`^s!f6yLZL=QV2~|Q0sp2w&(#?ywTDM<{}*m`>RK;7lWYpDyx>A6 z%N%Df@zCn|L1TZG?c)gx(xr;_I}D#JLpvQ>G0Bu1N!+#L@iAnX7F!vnDhV*`FQAGA zfB_^b(B_Ij!IJUg*X0X*bMvTI@)!SVT{;=gm?TAV$~gJuOjo65WtG&vcSN}rP!lE$ zp>Nh27X;Jp^!4w`M_L8_YSyW6Z)$0&Zf4UthfBAHcH)f`xSl_ z_BlE=Y3f&LQIzEL4vS)B{#=ri#PH)qiah`6rH8CTG=_d_{Y+V`KOO6vtf{h02}huO zcXhll{vBSsY?#v;d_Ui>7R6Yf(Gm2g$R$dl3yH5&)r2NvPcxOxY494=WJn6G@~J0E zgxLZ?8F9p>$oheap*1caGKeIlg1L*&)6PYWXh!gu6+@<>> z$*QuHgc1_roP>fAP*tIYP!ZQ$;mA0KPg=Ml#|QKMk^VVVLZ4)j_Tg@s0Bf2fZF~G0 zN%`nRGPPm`Ru!I#6`A0qIMgaeq?K!w@U!?J4W|eWV{7eDk7?)R>^-w=)dnOr?Y35> zzNT<8?Td6sR)=K6Y*6||2^L91oQ z2HSFko7m8HLO@f>V$bbG z)d9^yg_){%Sxl0Vwi2{z$Ltt{K25AoRxsb%9;mS=^ zo%58HQOQR~fbcGbX&&%AJ~lbKxx?MQ`!{cHdjLC=@cU?uMn#aKZ?%eQJI`Y&BDW-} z2yzjXZ@K7vM>l~)rTg{dAI0C(k;*M?5hu7dPG~eE*8^|g714=H)Wp>iwo7z2;e+MJ z-xasgrYun;dzoH!QLJ75C3ov8zRCGpICeX8>#$C{bn1Ir-=~G7)yz-q|{v#czx|1M$@ey|9irD`Ds{M(FyGZ4mfAYTJ5a6Yi^> z)IaelV!~@eqqC04pK!Z7CC-4RUF_65$!4^fd_3gF&wS+*^vX&EGA$pBQHdie?RBE+ zrJT84MO8}zt;r!Fy|G$pyIMVQ~gk=4BUt#;sZ+Vp5P&iB+*B1RvW=re& z$1~{oPyQR+p-6hBB1V>KO#C;)RBI?GS4lA(KS==PF>^($Vm9Je@Sh!T6`QAPVh3$V zouU*&Mp|NvC3}Euug#`m+|(vuzyoebH7A>uBy9Xwq%n~P?1#sIxn50I7b?1m9I#b- z_sC76u^BPGZ#eZysY($fkYN^``hkUptu?CZrq_E)7Bpafby%jK)8FICl6U%i$L|ZoPMJp1Z z@#T~Qg?W=MT6}xzm?hG5{ca$7ZgbZ~KUOZz5>5efp^ndU^Zg3K7dgg_JJWtM2=Q^HsY!~=fZIz^awZ8+ri4z8_T>01Jh zm!*isp~I}GA})hnLpE47q+hj>P~gUCr_9M_MUr^@2t}r-FJbPMr-ZSE-H4bcUTcm1 zTG&0c_u)eCi=9KShquzlPR7ZNQK3kvO^!&b(5a)AcV<_&1c>Y*>0=G%lARfAC9qesx!%!VXEPlFOHAoVS3MK-6g) zxcZU~M#Oy6rN?7U3d^)Yv+7Wi%AVHr5jZ`z-MkgeH-(R61ljp1$>lONWJOnM^)ye? z?&sTa>CRHXPY*g8Z_WFN6z%rLUr-JQ z6{`RWAvAVJAGLh_e=Ni-zA0mPTWi1lzGV*HvId_aBiYcg?EXmn+`jU{t=IdE;-N|i z8%wGZNQ@hzgpino6jF}`RaZ1e*#t2lwl;OL3ZCOYU~vYZ_O?s_k`9L z5TW`S7tucycg(9ZIFbWf->#4}&G!75XfecsJ3}dt8Mx!|D_;FJq-g4CbE}+1choX* z$CG2bpJv(GW~8dR(qlQX9kFEK6(gRZh$b0(TP7J*&N%m5TX?gA75gT;)PGMtb82?f zz5P>{OwxRS-p|2MJV&MzGT=K_38s0XEa+d___(-Ir8cS0kI%QR>2sp;piIQZg}Dl% za%Hz3l@Dv74H|1cuG3$X>`2INmp(du+H3s1*B&CxsoUwJ7mxYmcgdYx{>;Y*Z?8U@ zI`Ok4pm4*z7KXk`6!`eE#Rpg+YYjmUdX-%`@vnZh-O00}d=y1%D_5)2w7S#h%IuuWnqi*h=(B$rlAkm_QN8D~ z4hz1X&IkENlGwJgMmTw_+{lO)id48cH75cnw$V zL*fwKW$;o5#E1FlQk+smuE;+P1HU9JzOF3vvmNk zKaQxG0wyTb$jbb3G&yauCjRT^X>3t%k@NT@;0LUg9B6nc=35t?h#nut48qW8&174= zLLF0)-5rBZE7Qb%^Jkz&JSno)2_K&bZUWQV;wo!CnjNvnH1su0xJ>(OaMYEHqiUvF zunINWRT>MUxK%aUgs(curt8DA#wUR^aCuTPK5pq*54u~7rlVgw3R`iFLEs^;G;@VbxiE-Ww%oFS|EimK>vY zweiIfgQ(B*7lX12#bTEESSV(xKLFu(^2_kEd|F+RWav=I%sHOV=g!wZ{-O-o zf5s|Rq_(jLGmhaaFgPlmp<}I@oJ9|_6Im8r{n-eqccjjLQGO7G7dUi4?V&Kk&2 zkSVd~>5T$)YMjoyTtGXrJ|xAWDxU*%Tl40M@*B;)~^ko*|ie_scypc!J*M49$@UUAAOjK<2dK&Xvb>-Oy_i@NxdT+JN zhETa$c;bjbf-g>sK+z=SB9>`;C~BFJ$+X4zTAvnIc&nhP%kz@*_Di1N2qTg1yB`(AEb#&kTvAIz6u=xaE0tFx;4`Lob*wGabk=dT7Wp- zpmuOspVpp;GjhufX0=@qoNVDb$_hK5tS9*R^c(CaNd1b0jK%+85;aobVPa1Qmb^8b zpzJ!;Y+SW5r+ljYkA`PEGBViaB%Zq1^MMqT?Vd+BxLe>4;UuwS$UIZKDyOrkyITr# za`TK`rRpSTXyAlJdT8K9|Jj6o3e=qzB`ifj7vmbmDN+PgstOC(r*&K(-t3ml;Xkxn ztdn>g)jOc>b3BkhOgitqbyTWEP2jM*C4u`X57`HCL=s0PGyR!5?LrLc_;zKs$doHe z=!8vJO<;l$F| z&>_MC3|zQoHrH9@%UjI@GbH-A!M;o-F9g>$pbP250Lk$;gDTp~MI{#kwWVUsly5pn ziKPQHDO%WY_SF6Q#5J36JJ}rKD$_gh{Rn)ia2BE8r8>y#eR=B`gCw(ygUKVxuGi5HhCbWGR zriTpSPh&#at6chjRd;!AypMi=g|VR@GuhlJsD652e3e=y3CyVvj z-!fnwMiyvE*TRPj2QHAhU7PVtaz$vPE24Me`*LGumJO)&dsWAHy}Spi2i)AyM`lcR z2&A0?kDG6#+`MRs?Pa<1KKV@x<2-r5V~&>llx1T#65#yR4f->x`#c&iRP{|5AL=n= z{9wOAuC7{_f*1>D4xVQfRry5aJY$w>M)eHU^i^~`?S8L1*Jwi*vHRvZS|WIHDBo-7 zK)SWA4a*aqGe>a-g1|!=vg!)7=co;lyJC$8Km-W;-aI|FpWLRv;N)3G&bECI)G~cp z0Nn7hLr=dC!R)dp9A2})<>1X~e(unDOAf#D^xVo7USC{smTnnjG~`|3P6Ao57Gw=VO+;#w%}dUf6LOp9?_Rs0rzAwgXtDo;k_`|>IE?7%SYLX{IjdPq~c#d zT8@S?%r%>wu)}4NG*#nH#qVZu8!D3Ix_0XdU!@MiTe?2ye;@s2;c8LtMu!N)*4^~f z5zrkMF{2Z0+yWkhG4Vz$-I_dNVb=VfIj@SR)D4bZqNQdjq^_?!Kq!F5uF>0LnYZiU zr3<;>@_6gcZ&bT^CtReg1w`ZD00R<~&thXC9l?Mfj%ZHWO`A$LzE66LS}pY zR#*IJcghzDLlAZK2M%&kq=ZEUa4DGm=!GBN4Ax3k&JX($!gKNQdQ{+0fN3Azqz=EK zJ^AE~{C_LHb>7#Oc|vvQzg!V$6=Awz8`c~+%LUVhkI=3^jvLNxQ=$&M~ zlE}~;qc%^H+i2+IaTQ)wIS2g@iAf>2w#g;cWLzx-Rz&N)C5K)QZqV8wvdJ?NF^Wfq zf@nONTht&U)IIhhXyj0YY83o#z%YGixo?N3$5DcL3zfB-C_PmTaXBzP{>XQ$P3pGQ zzR|*66$E>Cx@_g5te)$WiNMc>NdO*vd`a4qWx}5ZX7DH`Mg#9~|4FhM*ozon3bG zAxHGwzJP9^;(0n^;6x%eRgAcF+OAebCjMq8dRq@F)+J$$-I2{tL3Y5i9r4u*LiBOH zn_L0Lp~%ORBify{7rwyF7?<5r{z<0D2zFfNN$HoY3-v(R%olV1TIJnoqpsrUTc);p z=2L9CNAhZuez{9CF~%olMo;9#ri+n3aqW6l z6uH_$@A5FsnKYVESrNPwrI_j^zFC0z(0ZR`-1au%&UER@HBmi(I9za${f8Im>FIE= z5S=B4iINLT&Bn($JL#h=eFiUE=D`drzIyd{Yll7&Bn}dX!5Ar&1v>37qui4whzdn{ zsX2TH@t!L`{#h~dB$)84AE>K50pzKave9BzGQpm1l*~gNJA`T?-cGIy9k{(A;6Gbs zGQkk&azu=Pc`0J{xFm;mU(~uES6P;OqiewzCGXY}V*?)|Ysn?k82~98$&>Vz%kA5T zRahLkp}0;d!P%Kq@-CX!SA|~{^I{{xefcY4`SKAu#h!p``1deNO}9S6iv;_|M^ya>x2IcfslK!nC>WCTtxoHw89zotfHalbTlr=q!$C0Hjt5S@e#B6grp5 z3sV}_O&uV2z5yra_a^}?*M{by|2J2AH~2Jrca%vS+uX4UI&b>$<3D@`jcc_@%*z7Q z7XM+;JY2~u$nW@yiphdt%#6`HKc^6BN0i7YENAg!WEmE|tHx|dT_&92=%w8I*iUlLG?y<3L;`Pet{ zq}<{CWN#JPx>q91<3lvq|F$Vl%UC&PyLke=t~yMIOY+3<)zsAgWKEE}p!P^R_s~{V zhv`^psTi@Gwv{S9ZJ>KX7DQ`+XR;W&3NEmJPX>m2{#mcAW4yA&5v$b1{=0bPwXAbX z(sE@T6O=BFSfebAd$ReTbyKbF+%$fzvX1r25l602rv9J9zD#FIr^w`xcy7_D8-J%@ zq|Vr+V6l4wwMIwe+b7P@J!yA(7rzOjldR(}A|~^lQ2$aVmpwzCfZ_HN0Hy zHVhH&bW=z^4))s`tR245Bs+_3ZzZZHFP_*wQBRj`R9+}hVDKj}T(Wm}dP@z_0`My$;C~yQkpQ+#^n6g*$soGL;BiZpS3*w<) zpM&0RUH!CT99X%&8NwCZxpd8_o57#gu(c42EQ=>$>2pd0Sa9crbm*=lkUhUNfI;y@ zRO%-}kw=Jfa$_P+@yKA0WM>=&(xrZb7ksh(}|LD zNleDLA5ohPiQW7xwkZFbI=4K-@1hrTia;Gjlr(B08Ji}Kx$J7!j%3mfd`qT?Vb+A| zMU22t({8ky9dG1ic`|Z#H9}eD6pHJCV&p_32ZUBlJn_ThBFcerA;WJ2+vH+scSiB= zv_&iU`5F0`_gawV5pLE>)z zEM0!<(eIfz9SWq4XsZ}Ak&7rv;;?tWB0Oub)ng18Fg%;SA$YpfvTuW1H~Hb_*RK^mO` z^|(O0T9}0U^0*>+v_MXeH%}eoE#j*(qZeJeuNlVt?oyl>!)_vsKfjPQ19|~L;9KCU zB`|O=o7g%(o2B#opo1(5zaaVUr|2b98Gvi(J%0lG$1hxrd@*v83*93ZH*Bxtok=nItR= zNF-_@N+QtD`@KlK*Heo)9YxvSuTPara)J${k)ptxb*@_cbFzxbN+6yqnyn@A&uJDj!TH=D3n%h3 z_{5laWpuPk86zuCWpy9>I^QBBpV+X;jMgOw$w3V_q48r(s1KcbKOJx3cauDj;^?G^ z>@;2RoT3aY9P1R-o$^wnqvQO$x&JWO3&y_abvU+yA;WXMuQf9K_uVjyMfVcyN3 zuPUzM<2vg6M6uaoYYF>39^A+!y)t*f5O}03tRAlzuM@97yFMd^NiJ%m$gDJ>yU7|R ztE`(M6>?HrV1!AcvVcge%K{RS&YDb&>q3vr{sw3X_2hUKTha9tfvwd(S8_HBo;&QY z_5jeV?5fd3R{W26jaS9Tk_0*}kpyQ5NU=)!)vVLBUISUf;Uu^l#(XxY7BV6)YUwX_ zjMfbT6TH`@mCYW*w(k>Hg$RTqkBOBF1^wF#chVE{YZw8qbRW(=g_C|tTBuwCNoK!8 z>jpF%lGO?aNq>&H7U+X+VU7G83+z1~LwjlT)8}x1hMJq82`Y6{gNM};|LRV9WA1=HsMkeYnmDGe?p4X5 zI;i_#>r@{b9(y*s;NaM5Rq(4+#gYUp z2m~l6ZOm5|@o6ZDa4=h2DyBBeZtB>Jhf8 zg|d^mnF=XNMhg;Q>uqhxpc<9SQ3(QSI0b^q05%Xre_H>E{6_Any{jr%I>Fixx3j=h zt_?XnNFAP<%FRyAfN-;IN>pu9nNUP+*b&1cS11X;k$fFpLWgR9#+REa?swRUXvx+k zUuC9nvtU={j72HQG8Wass@#^Ur&6g0)rb;8NETbyg&S+{qo({s@}=>;U;+EAJFx4! z8RbY9hC58S2=^?!?r;6*uj{{(zsY^Ie~$I#u>gymr(on{xo&`rsJ(?Apw8NV$Y~>} zw|^@v;`J7JHOFF+yOPJl0zLmTr!(p$wh&cXc8GO1gRw`K3e!|5pUc8wW8ds!P#v_j zXMTCHWMM%ij(qtz-eJy@Zb{n0+nfi^Fx*rqlJHHpjYS2Lr{5Q13IAx0G$`1(1{#%# zec#P9^6m-$f_z1dgj(?Usal!5PO@vV@_{bd9QD9slQOsGjPIp*Rd**S3g3B+9N3-?IWAnWjQJ7s0bU?-?{f}_Wg-#aW z&Gx5QJ17D>O|(x(+CM;Qc}lai9zus8>hEnp5F*iJiuR`Xw765Yv}o(qAAFJ`ImpGt zIy#@uwqZ06*AS98m?$nqf|s;QmaCU8$x{Kho}WFrdwr4@$9ZkUd~_@=OI;%|EJ@e= zhisyz^Ol*We12_(BD>S(svk;>OENUA$R=sJXqhURY1Ay8`a{UIm8=hc^6-piManhw z;F|7Vui8NlhkY`X3oLg{B07v6n&G6uM^q=m1=VH+tRt6XJGO$sKmUFV_kx!0rg7VT zZtBzgUbmf2KO*-LHA`^8bgX~{zEm0qwwA%i-c(}I2YuTpgO^jOv# zd4#m%WUq{{n%1D&cDt#f?gBKz&HbT1(TxJHfQP9d@IkwNvd#0_a<4C%4`$`u9Sm&H zpYUIUdS7qAh25#>S6|~{_9i>@bQ>CZw5~@9tJ_VZp5awNzpu4WJ>RGVcMDPDEeT_E z*Z9s}ejY@#PIS2yhQQP@8BKcYCIN}mu$ZXZS!wY+wS3I`qlyW;bxv%h@FWyT($q`< zP&Y7NM@cSTf@ZkgAgA#wL|#GpKYg;*Zzr1 z@!`M~m5fwz9I{nnIkP`Lt<w(}b#FUWngD#h3!hET*-oo<94m=)yV?E;?vKEUxX50_(lv z6FWA9D=m=nm-X;2AuIktJ3iOAlvJo}UZq&jOr&^tFiNWpl;urT_UbhlV~&e)QYy-{ zy>l!2Tl`=z;FZ^P=RNPuf3-g7Zs5@bXMCny$tHhXE9DpxlVkzZwkIuJqzWs3oOw~x z1rOOdd9RdjeT3DfwWBspu3GxJS(Fx1n=x5Q6cJ-R=pC#+k{R3r#3o}_^}LV8)8p0j zH6?2*wK*A+nM5-y)|1Zg22N&h4-_K_S)O|pG_2_zddKMco@Gq_rrn+XfWU1 zhskXe%T$h)2jib`!#1BHERQ2UV^vBaiVkMuU7-r1FgVto-T@ewp=%*S=BTPRSew(M zpCve_b{5x)i}9P&7$zsi1;)71dE_Q!ul(@d%u(05X`5^r%j8T?SX>4+#&6EYP!it4 z=6m`i>7#~aA!AfQHV4{YZPu$3%2A(qI0|6=X0?X~6LD6oM;QOfNPOiLK6SApI4H`{su&2XQ~QyJCTN80rtUOWBr|+|@W<=zX=nH|*1kB){A*wzqO) zWeME{8k)?+c?Pyi(*|j6zrFhP<+rC;@c)6Z?hWnyXC9cK>G7XL|8OpT0?#Y+N?U{ucrr1>&F-bD9+Ahq+Fu^1jTpmLVfV_EF>lyeT zmOUCwWks^&=Qg)AK=SMC#;UUnLNlJZmdy?9`oX2}gyOHDF~9Njqa^Urk(VZDf}B^y zle!RJyP%m~C2t4z{cijxR<2nt{z!e`SVP&w0x`d{BqmAauxlC3>QY$Y=mMK*Wzr~K zN!R`j$6V;-85kd}efQQt5AA#w1awSahPfraZ$BkK$E1dvi|X>GQ$8hkN_8K2`whmB za$OaulE>H0O9Bg*%u{((PbrPAYtmYITx57iszAx(btL}0CN68bS5eq3#{5clZxx}%1j5Sim~dM)r6Vjqn-PVkGti3 zdNVQ{<}X}HC1f$HSIi7y+%m7m^bJLvaIJG!pvOnJuF;d|7XN0z2@6;Ba1m*|`PCvz z7`NQ3GF_x9m)Fj%EibDiZLQ;03#2}iCWXgp}OctS~ zM+qeYRowmq?_m>a8jsx0NrL9)3ZOhCGI9s^$QO6RyoXYGv#!ZaeKeh>!L21*y_tEX zW(tg~LDCa~>ursd=klPQuEq2GbFKay~SFnw0hCZ#*rKm5w~NKrGGHEhYY zx=n5^paiMVy*x)k!^zX*CUhq9{B!g8{)yE=<6GkRzF{>y-?+tZT4Z*vM*Frx*eoLS zZWeKjT#J1#j65xNLVF_rb!ndvX`oh=~T7ZbUdCRoLM_vD0Eqla;n#-DpE!Jn(3Jf44YO~^!70?#kck;8udeHw=-equ4XE9`ayn`OH$!EjiR#cV3!G6ge@J70!sQ9TJkJmXm{@X3kCm z&5C5(u@PXJ6){7c43XaorHQtnVc(ERkI1}*E z_0f?xrS^!TLTSXYo3p0Z|K_e9k#zev&cMZ8)}2&b6&F{f51BA=BIgGO-b5etL$l#Y zpL5T3rh&ac+&Ug)gdkI4ws*~EeC?n`$k?gO9nvUM819 z+UkesWjpx0WzkPve}KFqxL3bm-`PHN-w`;!-0k&(?YyhtUGN!SGj={AdMdJ28fgk6 z_l|punlay}boxyzrZDC%dE?SWhi*-nGSd|o=S~ctG%1)z0bTsbzPKX46~$Mby4I`D z7_&j_>8BXxW%)Nr?)8@&Cqf0mvhBjx;_9H{j8EH#sf~NE$G#Qdi z57xjurYU(n&irwob2;I8N=10d+Ehu$4wW>xLn=LU+bG+9rdvu===qlb3COMI+pPYQ zsqPvB{+8$M>it*u)+f^X!-m>4k|uUOV|ORoe*5pYe4G1bEORvB{q+6LJx%@7TvUw{pXss|Rk^gjKl(p`-k(0#Dx;`RyeSh{D3E ztK>lhf;^}St7cJ9(9zxGUJ)dTVSy5n2!vQM5cH$Q_oq2ID#ZO<%j~|ynHiEP5DmcF z&yZ0xkqDy6osmK%{O1!bFbQz)Hw;?e7+qX94vZyg$l-R@cB1MeBkd$fx&6ntzPWbZ zUG57`WN%moU^|0^G$_#YNgyp)7!3%h>B4EAafm6SE_u=@ve!pD%=(p*dWMm9h93Lk zbm@=V;@Uyuuw~eQF^pKo5#v4AXZR%ay{1B50NHVtT0y-P1(=Pd3V*VT%yP3)rYkKI z$2YNdkkoCAv^I)rha%cQ4aP|8a|7P+&1*4|y60h>N7IxI+yRyQ{#z$LYD=KZV zH%2q6bIROZh<|h<|*|3uoBaUk)6)N*|oND06#C5ElNo=FC*t1K_hnt8lXvFiw})wY|CmB zDL4PPHV_2cnehX8vqz02F+}iJwr&3kfg-RqKTN`qJ`)y>zX~Qvkw{tOJNJUkJ1@7# z1aK=er>@qVDhHlhj{gr&-iTTA9n&AuXbHb+{RdEg`Wg}WUJx2imltsVhD>0m#r81- zM#kgv_7+m*q9GWWLw=qfpSxRp2rl%dL)VZ{EeT5ag$W%<%l3@{p&7wSn8VOVW)YG} zr4bbt<gq0N z5O0Eewe&@3&qYzAKEO`eVn|>JHgDYw!&mzw4<-l#OXNq*t*dDq?|Ao|S&zfVis$wH zHKnCpJS8v$(M_20&hw}E8`Vc$-Q_uP-uUV{$x%VTzO6ro47$aGYy;5j8mMAnVKkJZ!QveI4Z~rWr zEcsMw$3z0YX&lo$re$9kE?;Nxj1Eh_U85?)YkYfxcO_DtpvFp%YwXq9c~K98?dot2 zTIPmn@mJo6*#?F@XUS(dgJzTl=Xgn#d3eOgsH&IrA9yhgUH$I^FO8)5h&;=ehDh0m`LbVV z+1V|=mGw5Z7!IRcueT>kyi>NTT-+c=P1gM1^~%c{__S{#I)6c* zXzW@-QHqhXG&Uql?tGRg@Y8Ym{FnCHUD<;zijPMbZJW*tQSt-=r52Lyh!O}6??+>; zW3#yV9VGX?E1wt-(iuy{J?|;<(LUHzV=m7c_3|6Bp%^^F@+r>~4MgIV6O8A?T*|Zf z1(2Nl5|ioliD5S5Z?RS5fK6lAu$5NF?1#iddwBhWHPg8 zh#dY9^?mM7girp434Qe;jr8;?ad}F>j`ZEfeL+!%blMKdeu+m3p?;4O8IIKx2$zpB z9;C;fXOts5A>FizV`=77{gNJhs?Qj5+WjoBz=FqcSdR6r)JzVBu5 zQd`80v(lCR*iA6jUjQ@3o&m|2XY`owU&Kd?NZgaTD{8+9zlCytn=rnE8`=?1LGnrA zA8ssJ>Mp6RLbecoRaZ zct4Oe$s0 zt|*tDBsaqL0g~?aY%ti5aI)Uu;!rusD28^JMEsF1boEnWt|5OANr!1djK6_XgC|{N zu`ZJEodZJ^%)v?2C+^_C8i9uKH$Yp6Oi&#nM}di6H;3GApH*(?=m%$!-YrXn<6F44 zRuiNNZjIEI1O#AK@e$6!hdcalFM@%{BXXU#Rd7^bKSUl7Zv#rIe8>xnZqJO%ibrEO zr3CVE@^hR_*fpqT=ZqDU)0Rq+3k34wo#7jVlxdu~bEa}Al1a=tbEh$Ff+a{6B8on& ze>ASf>#v_SznyDldqrqJ7q1j!=V~*Ra{LeMCTl&eHiqjUpR-a5LEeJj==3B_UWaI#lUk3 zIyQEA;wCXA`HZC|SexEoWWFnGl14+bPtQF0{~4*{8nU~uXcRAdEC$DE`>GG4DpbzJ5-;7rxCqZ$sj7O$1EsxZxCr>xF*Rx~Gp=KYYr^J;h zqirF5T=4T`r=f3~n{7dcM1EI9_#hC!`$glbRJLPQCNp)dFhU zr&M88`gK$oV^hBMP}ZT4ucRVb@B61nQW3MHSTd4UgVrQIRG<@zgEXUYi&=@j?s*i` zKNkB(8VPY_!cC?&5|a+Hv7T{+)aP+2w{rC5Zgx*;YJ*}ViJ?Jj0uvXb!eS{!e%{tcOrB7;#^(j zv)K^2OQ97TNQBR($Wdt`Fg$)gnge=quWUH+ahA$Z@Op>LcR4(I`2VHUmc1U{9c_j`Hq4vcc`4i5{ zj)y+=m)xny_vA_8ocI>DjC=HEeakB42f+pw0*!uo;M9SUhpak?`W!l^Aa|ZMqz1P> za{V2<{(2u;n)DAJ`rd3p&+*%#=bfS$={rAkB2T>4nDr%Br^nQtKnOr6jcE#o=pA?J zS76~;la6Pk5F7KfAxaZ|U-A+hay)~9>rh9=(SXq1h;)6Wx`lDJ05{a8(ZJNfg)#e? z7EfC<^*T};BZp-XlNGnuht-)cQ0qb(Y%h^SVKx`gMYm4v5qfw^oBns@?oGzDuN(!= zA{iJpPq46&l7McWXurp>WbouD7?~1Jc;URf#ywf$3GS9Op(bcvN&8K{%QaP-=+in_ z2(vb={C2mS$wE5Ci_0d=UCb@Cluls^8;U4jVI4McZb7yuvS$lXbe37WIQMYp+iSGs z*KF+;xPe2VC_r8r;0>nvA655_$L`QApvA)f*F|5-@NJN=>UWrKhqhaF=ov)QnPzCk z8Dbn`B7=qbdLNgE&KV3wt#hZ#6X(=z{u-S+?+gu+McNX2@r;#r!rz_=x1OPwj|%$Ndd|OX3EqDpn6lFB=Co% zq`U<4p3=AvaAElr?sOxC{J+x-ZelKl+xH5nm_Ttjon93=JnVD-3@4s5|6!!LHA@Tg zn2x`Y7AD70agZ6j!}2Nb(ov8~jW?7r-V<{v?|kYCeB6Sh$LREGo_+WiEHz!fGSE)Y zND&|NFB4Lef?{U6MSO3w&0^*H(1p<-}b@27r z{Oc^&&l3j%-%{eG53=tuWELClwzux?2ZVNXuVET@?ug`Pkso)xX32f116fNxF%sda z4HDwV-wc&lm?7xw;Ns3Qh;1zy-T8~<%?^O2QZIyBF^54C;@Y-VeooVk&}O{WICC&t zz$TNiBby~*joQ3aRl(INJHWJRRzWRH2Ognv8`bj&0_FvTCZbb!d#}5POQ()Fbp@u; zHH%dSq%$eHRh*u|HjG+7uu9DmXcp!$0wOo;QTOn`HNe+fJB9m+zIw0f$2%F*F*&VN z7w^I8^-F?CCL70!3H*RxRxiylD>ELD;SA0n{Mk+BOmEvppslGt0qs1NzoqPsj^ zcM!oLNd)SB`AN!2RZ2`dG9;K>vr*WS2C~4X-lomT%rTArK#G1PO{SrwB$~?# zhSM1Lf%X|`(&w6b{0TNm$X;0Q%>TeNaKrnih2xf@N6nijO*$aM`EB@)NO$0*R9AaT zj<YF-4h(DvRFxVa zO*n1h(m^DLLtAWkkrXmHv7qh0&ty_ok z(5t4h;AmP+gOsfRd3CXPZsTE;*=p(;;jee}{?o9K_hz)6{MC+i0>#f_i+$TSx5IRr z6oR@NHUd~uRYx(5B_-5vPNCl=Paq<7vA##?Nw03_-WqGKdVQ`YN#(t?AocLsV4NA$ zW)SuBVH1Gmsv0TAv6RH-&3kE0wafR38qFN)zhFDec7H3h|C;xiK;p5az;a;a`Cyqk z2`?@Z>i{gFnn6*IO^$W!9Ho;`FaJUs`=q9N_Mbvh?0fG;3EJeZQuUa?<9fMeC}c4$J+prh9b$N!v<%GIN#vs5~fCcrD4dj&8jb zqSkE7H=4T3c%I9K$ORN%VEp@gzS*bdEj5k0T=UdGq2?#dO83;KIz}YY`DacS*&>2De^G{_d-65fuMglLs4P;;yd(!7 zCj&cD>-%>&%ZkC}{R@@)7@r+X^=y9jt{lJRVnf99wwF4JbpQC2fjAb6ewZo>nI)ZM zl%+p_Z2&`}8JyW_eaYGLJ#>My)(SIyI*9%K86J4Crl;UGICuD?&OT$_ms*~p$Zan} zl4@t};MJET9Vmr*<{$N@qu`#{%2Qes`$SetsHe|! zZ2mp>l-cWdsOw8jhf3it<%>n)Fpn`WTTym37=>Q5Ol<_rX;(?N^axTx#0Cit8`m1& z1DfkKBOD%4ihA&okQtXv`YX;K5-RXe9$pFXg_3+{dvV?94qlhTc<^RcO8%S(`HWc$ zCqK2Fx(J`))4fs8D3ijJ`84O1`8mFG4YwPkrkLM0$ZDT%e_-p6T*Fy79Z9W8vllo^ zD>xN{PO2&IeAO(zkAJPx$Ia5l>p7gqki5BR(R8P2_v@9ZUR!^ou%f%=QOkn68_Gs6 zQ#YCwjs8c0y-&}E-jOT*feNHPn}6+PQJW6;cPJ&HiI?5dxd(DsjwIQ!(4q_@Wt~s)iiHMn%bJpo1-FeyzRG!t_effCz%%#N@0KrnN4siV-a`M><+XP)u3NVZ*;b1A|CFie^i zrA@M;e_S#|G%ae{95!N@2e59$q)IpI=&J+)@I~Ra|f4hcy^-vgE~A zf>PKut9E5L#BR zVF$mMeN+(L)ctzITXlj170k%ctF$@vC0tuc)*+$vn z*@y+QhHR!YZ3PD@6L;u|XQb4K;`5KE5OLpRG>!{l!wK|Vn0k1o+$$b2dM?E_u{7fM z9zuX|fgCimMdq+)imHpBSTNGqpvl+tG=8?yYJa)VY2T(1ald4o#f5OA3G_Xfeh^CW zj7O6^m*QHuI{7D$0LEoJBP|nH^452ox|MLlr$1XR9CzQ`c&2v}6w3`|M-b6}!uZ3v zN}m?1*HUynK}r8mFJ(K8eTmsLoxG&m(xA*&_E>we%_$`~1=UbCWTjBsi8E#*QSAcVN=t zLZx>cWbs-W-$+q$KIPY$=Sd^Y(5s7k8ZK{`(8^PHdFD^J5OHXaC&1_J^U;kg;e@n0ah|D&~{Kh~tKH zqX^Vph;*=6=^GE5y?PTG89M&hXwJMu8lX8mUEz|Z26eu=rzw5ydzUgbg1zyexfGcO z8BUyeY+j_IVH`MtybF^L6)U_wuyhz*Bk;*CK&`20K}E*8ThVcV30WF156EJq1nte^ zM)Q!n^F@J$Q^1DQu53O1^TEKxePq~rYe*?d)2=Bq1@p_E2ge@F#KgNkOCO|?J+os` zueGtG=r4Aap z{BudQ z*aFZE3XB;XblyYpYtUi*=^1H6kI5e`i{H$K?_?>Da#9t%%Z&E^dnt^RrfXy%fe-mH zJ5AHYR)B6%t|NLMCtG~oG_CSzn))=N!d4=7Z1TvRJsJLlb0P7dm9UY0%*-4_H*(O> zq`C3C46$&=6&Cfn3|fZd#C_Vitgf4D@8i1;*^I(^2Wh>eJ}z1* zD^i~k{eABhB$l6@%~`p+i9FZs6aitRF;R|>mAc*?zM1U~%{~e5=i_H|d{1%^{8W)FCuMXoSR)_I+CeW2mE@NVJ21bj@`+iPlWCdsXW+>)X5!Ydzf|BX+Fh7o=@`RS;=v0m zn%5ZE8MNMe8eCn0=h$RxT-iK>GXrkdHpk_h5R!1;WM(`U!j2};cUiPUO^ojq&coJ9 zKLi(;eD`z=XXUGX=h!+o&b>4JY?_N$eE;0UrW0^9EgmS)fc!W=g#A&9zXYc1RX*Em zomu&6uLfJ)+QvH@Ox9^0y>er7B#=U}h6W}EQQBz=yQ1~8z}c3JMNjl(fqx=l3t3x& zC$JKBLRn!6ct+5={3da#qQ5|o#>1o|+=4j_OihhzSq<;QHnX%@F1J2)NRyo%{5LxB zWEu_kPsZc9Fis2+cb~;Ngw~7G_7PizdiCebSC(Q+fuM4D`EG`311+tu5i1zi?!fA--0=H^0w|~z1saXq|?$yj5-WTto zsIy#}cy^maT#k!>WnRc#iN!9pFRqcK&T@WI)rRHV8p#~-@P)ahVPe%%1Ca4rjU~W3 zHb|v_cy&Y=24WmRovYX&?v5=zHa7+HiEHqZ+$dHmll*m&e0Wbn16_62S(49Gz3%w> z`AJsOI;=BAf)vdfhD2Irc``@N3K$2!b>O)NI;zr{HpR*r#l=sh29DFGNY6In`^?D2?`yLNDOIzw(7=aDh#h6K`I(|ZvGpTZPUk=R(g+%7P{bcS z6|w(?2b9xiqP!!&^(3WHO-v*(aBj>l+Z=|Wa40u3(IkB~Y8FK1DX1l7FSzNFj#wD& z+GDCwJ-w%nfZJh3b+btepcKIe^RvVr5#4IY&(sKJUk`(sjX7>?V z3gd$l_z^qq=(*KiLPLbqiMD67#B$BR`I(n@)+lS6W#AZ=N|l~*~Tj?D!SluDt!ee$%V622<=Fzf--q7 z1D$J>lCzTW1>7pCv(z z^JEkb7TiH-gM{0&S2sGIduCodb~OdKFcR@63x70MMF=Dt32JMDl;Z>z0~VMK^5DNF zmp2PkPFB~~hk0%u4lhKK8%Yjjk&e;J;+rU%YbQzBPU1n`e@3D@4*>HVIp=$k94vm|@frrdRf;>EvMq^>D)Ii3K|KYf`c9QIm zfT2&RwDKgzNJ-X6lP*7TStOUsBXUt;3mKs;$^x4v=R;;%|8Q7GiFUmM8x_sS|8C)h0&x;95gQOviLcKwmlJ|3ZLuVSJ$ApPvBGNHO zN+yjo>+?UXC=QQL=Ay&O@fZ5Vzm8=wYWfasRzH$OLmw8~vol15uQ=%}drX5P<52$8 zIJOK!_xh7TZBKc@gsDbzY`HA8I!2h@^9_obA+3_O$FCerPr?P~vGuW}(V;v4^q{ub_E0A) zSncnVPa=Q_e-_cr7lRCzS|nNt_L_X)MK>@Z_I2xr*c-94ja5hdIg(bK9MOf2cm}D40&d0mU3q~LU!U zACGCtjfC~@CWx|i=ahoNWrMjzpxzXml|@@QfYqS3na;_n7eK9PJRL&k^$5Kl7dntW z=mGk&XTknrC79T&vRa}&eV&Ps zV%volKjr$}r8kUzMToNvZ)l8_Lj>vFCstTlQjlA##%L?8Do?*ajwpj?L>PT`aid3W z&M{x|l>C#7t<>fPrla=20X1C%Brs65oFw}SfbYsJYK`aT3O((4bk+!+zY@_^m2#tF zJyaf`#mWKrVW8pqPDR_~4W#~3mq2IwuMkQjn<8r*7!FAxQ`yugbgBaxo)|`>0#<3+IJ3 z=`YH9c;38EvT0#p;!A1v3L^W?gX78+sy0F!@u%U$BtjHD6LuMITd+1W3Y$a;1iK&U z1{bUDwjPync)K4w3}nTz0@z_NVRBgfz6mQU#3cC2f1I(BWn4E|0B<$VoGj!%e%m9~ z(8`ljzMa4`DJGaDia2JO9jeBi-tgI@41ca*7pw24hyJ>Qt=D{e6vc_-1@IxB*uESU z6D0fIe7OSSKTq@vQ0$Q%7;0g^L|KoaVz&<*{yXMFZ2oGznLfkK4P&8m<0y@lTfW?#$OdBmv8Q`EwxQhOk1fNM*Rep%fEv#S3{^ zT&e$Cc7)fzM>DJUxDR9wUWq}H{FGb#{U1^G06yu=KPQyw6g|uk znE?w>e!!9U>Z5hPFFRyp1RC+n4whc^@@gO%#}43zdv5TX+HUq*OK~8iN?3#}d4eMv z&=gW|ynAVym*K#ZRRL&P`6xU8Q9EAJ#XTd_UI%1@u>bqtprKikZ7=sLQz<%_E+P?$ z&hhdfqqN?4xejEe;etw;8>=hkpku{x_ zucVG28W3N$54@TmtD4bKubTc*QYGA#&+iR8K5+Tl-5S6rcMcmz%u>y0(%I)Cj7<|R z>}0p*{m10@n^E3<(cTQ+GAplx0Pr(;B%7w-D|*&TJZ~-meD!0n8&p?$Kf4~6ZXBT} zf1P6wp?!Yul>LPX!rn1s$&hK^Tsm+&u;AHdtt{}UhHGvgy`DLB>M&G;W*HrISY;oR zTd(-qw&K0srQ=gH&&U@?(~dAMINOg+u0p|O@yFH;8vgt+^yMmV6($JBa)MO2xI-87 z@7^DqDV(IgrxfHZ8^j&Un;JF@*PvOG94avu24&>}1wEa7CeAC9;Il<(TwS!z3&sU! z`f15k7l1eTK?g4bZoDpflI(8!8&JZabMMmczWnIR&s3dL@yrYSaZ-d2!|Z@Uf75Da z(?k78`%G(@O^hrM4RC3epTzq-3PwS{c(x+lnll1@3XB*-l{fv29%LPUr!#{5qs2<*ghnI2EW$u+t?rUTs2Ar2i)KCrfBLgJX==05GiED_|BV-{o#oY5Ns$ zwy;3?&GiiM2ad5_CIPif$askcx3T-iw=e8>ds1ilV7|C#8AA+4%IO)LXQKRaiAJY$ z4be6Ly?EMtpZF=ytXBR90oPqMZMY%gNtD_A>mq(XGjs3T^H?fb2i19>Fr1J`2xgSRj^#`3Hw(OX{XeDZ7fPQiyt*AkfD?AGR33NIYRqS&*E;7?FD!ybEaa_ZsTS!<4ICWrFj}-{RmfY$|a{2 zm8?Q>edYrs4pBJ*c#lmjEKTCa=^8$W%+=p%^Jzq34B7J`*_OdM zd?5;X zkH#Q0d|v|J)tT>hW*--x(j*ummy%-=rTz6o!o7dqkY!mHYNyTExQVT_tTwO|4H?au z;W2DUEU!0x40Q+n4P(!?By9HF6Qy<E`5;qbi8LG}3ShSQOn<^dJ zLair9zqD(9c=Y=5bntToT=aefKfX**?I01VtXzAeGel0Fl*Lq`aHAxo?BslQJim7k zG4kTz7h7KjKI&P>NN4MkiK3j?CAr~KMHi9%I*RzIj&@<+@KA2sLu0Vt3KuQ^m3p9m-U3V#~PQF3+c;1j7GFEC1xW1~lG zrVj2@1c*)_ND@d{%xGq0M$*JgrV>er9l=czJD%6;FOGqn_icN!=hh17(mFNoL!}!< zRU+%n2@j{P6t7d%Ay8EwY*eq&U#>WV6e-aDTMEfADjQ0e#Mm3k`!!!a`&qaA>$dH& z|1;*h7B@)DKEmY_O)7`@$ssJtbO

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/509/docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt b/pr/509/docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt new file mode 100644 index 000000000..4b3edc29e --- /dev/null +++ b/pr/509/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/509/docs/static.files/SourceCodePro-It-fc8b9304.ttf.woff2 b/pr/509/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/509/docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt b/pr/509/docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt new file mode 100644 index 000000000..0d2941e14 --- /dev/null +++ b/pr/509/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/509/docs/static.files/SourceCodePro-Regular-8badfe75.ttf.woff2 b/pr/509/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/509/docs/static.files/SourceSerif4-Bold-6d4fd4c0.ttf.woff2 b/pr/509/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/509/docs/static.files/SourceSerif4-It-ca3b17ed.ttf.woff2 b/pr/509/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/509/docs/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 b/pr/509/docs/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..0263fc304226d90e224e53053855ad138303b70b GIT binary patch literal 76260 zcmV)PK()VjPew8T0RR910V(7F4gdfE1Ongy0V#(71OZ0?00000000000000000000 z0000PMjC|*8->w)9O@umT~|AHUcCAtzZNo1(QSv?|xg(ptBYJ zQ*_=t(04+BTtGHcW_3U-8nx0;9m6Dwbfq0b)aSFNK#mx@(E@n&t<28R(g6|u9y@_< z+intOdT!e)-i-)p_W%F?|NsC0|NsC0|Nr}wAE8@6^ENx%QYuLxR0UBifCV@X&%6Jz z2pKEPS#Mcju}zqz)H%a45^M7!NhyS2ViM*GTUKN|uNywfgS0Vo3LWmTL^d8%JCR{J zn+GZi8u=(PD0qPd_sa~dhP&0+bFbZfqtJMk+^;tz53DuT7tE1!!VQTrpxau*#^fGL z0Z)cK)?%imX~2edY5p8uLZB>7AFX4SUVYMfa+Wgh#fRmav3bfY2pJg>veu%#u#s@# zLNap3a$}7Za5&Z$dhuc>?49>4rchX)u|PGFxfF&QxRdBI`mp@dJQu8I7Vbs8#iG0! z;53G^Y%@AC3xu2jhK#rJB~D!MYvzr6$18;;J1F|7P8lB9Sy?V{0gWtF7ZLtiC44Mo z725)fYN2)qW|Y-i+R>Ue+rk>!(`f*Paws^#mGV7qs6vvt6?=u!dK<)ZU7B4d(}jn51jovT!8WLCK{dD`=`oC!@Ab} zjfB;-v)KI55!&c-xF=7Cn-zKataKn=>i2cKDTE_AHLzPNEYy zS!c(qSzC>FmV}Aco5rm9h~5$qLJ8;ke8cQ(((LLV7VG!AEWzbKs~z`FS)xDAs1mxT z(-b(mS$u_qsGn5&Cmy#mTJzi1Z|al{Neus0P)eAG$ZqiWnO#{DMTpL07m|V7iYFt= z94>LLo=0L66ovnAArXn@ditVHShAT9!2fkeU^lSSqm1@4 z;1>-py{AIyO`BIla_nXgs?^Y;O>ONsq7&EkD6jIFejy^*Z$EqQ(T^yDd_-weR(`RQ zE-|7$SPe!LW?)4jO5JPwHk0_Q{l7_0b-6_o%ql-|f)yQAfp@JorP+J5FaUFN78P^U zh&qUr5)~?<5~U*Qs7bU4W6%;~gPwyHN60Ziz!Bnzbc__K4HzvG=g1AuPiHS_+9W^T zdMZiNh&A#frcJ!5q>-8wbv?O@K-VP>~6F-K0|d)nKtg4{Kq*wv5t?tIid zLK={0uu+8&cAZ);v$1>le_~H}&-;JZ7DW-8q(mVExk<|yjO+#gryl-Bv(G&MNE3=A zkR!N1xMtp_R7Uj3Egu#!K;H(A9@yv!H>(x^1E(v|1T4|rcYn&we#yFp6oxmR$)KTx z1hI+_T{59t{-&#P`*t=t98Z6Eew#f-3|SN;i{b5ZrQia z+wQjOMP9DrdU1CHOYZ<5Uq}Zy!8W&Kf2Q{8)M|hfq3HT59{I;DB3Fn$XfQ6Ps79{pAh*J^K z`v@uBVi`)G1t0IgY%wr=ZsNB)w#TUp+c&*j!~f32fQF!Lkq~dNn3cxlC2wK{FpvWQ z09(u7-$_bd2qCkx6I$4#WFJfKs>mIVOI{&*l5CnJiG}9=|IPQ;N)l^@ zkV%aalt6wz^zxEl34j|*maN7ONvTO-SD^<$R&?->><7Pd@40V#;eCO1jd&ufCQXdU z$c&VA-DDjvfsx2WGN?{P$1Xns%?7O9?okMY6lg`z5;A(~PkeWOV#!RPB3LX#!8Qn} ze}4qO>hw*JNRLCK34Rp@g+dsF1hvMazC=$(iQP@+&70F#+J70y+s!dhfDA1=MD0fq zBvl(1XaID%5o6hnR|5$C$K24D$&>WCt}i2@?2$E-Njv(EPH7esq3R6LP2YYIz4IYg z4WneqDud;sV|sXSDJ_>&w{qfumHm(uA#Yk!vj71x^YgA_KlkSkkr-N(9GbK>+gA87 z;F)uWC3t77=1k36Ek=c;Cd*v1O_akSEmMX!*Y@TM_e=HL_rwM{48w#-4q$|2_DGhsR_3LImvL(Y3{GoGMDkXP;BlcMdV5uwg^?&xF z{`5Trc>dqpJJVlaDYqJEBG+ty1va3T>AY`7Q?XDD@uEGfXqiG&K}5wQcnT_fC?ypuYS60654%=!(t@LlsX(-t(p{i#wcZyK6P%#Sk!LvNdRaF;@u77?{dW7yNQce~(7RN%z zznT!D8jTu9O2#0wQD~gTd91-Jh@zX;4K5~uF9`Kn8cO~DYTDZl2+JZ%sY(G?zGlr4xQP6D+2FIBU}6HX&8&j}Z7oijSo z84i7H3+yf+;NxqR=)lFEhIS^Xa{wKAbV#vu=kkhD6rDn7D%Yi3SFO9!McbKw=$G$o zHfO$~E_HDziIpiE+ND3r%S1Sk5eAvT+DPc3znEULlhKL%nbf*(4^Ij(2%5t{4r=~O zXLbE0IY`DmjXH{n-I>YuOuxIs#EtQ^dpXtrze-j0e-(%-zzPH*V2vfyEhI;Qw^fo1 zlAs46iw-HcQxXE&dB#Aeh&_qTc29JtI3b4X#&GMmr+It2?cqEj-8Q82ac{>IPfJkU z#0?k>)&6-H33tz#M{=w%re z#{>LYsolx{FL)E$E3pd6__;(FFtS1|%pA@B@C1oefnDobAzQ7=Kfe~f_FY1u3sGk1 zsaWsIPb+pa=F2Fd%M<&>|LQvo69q%H?}T5PvF}3={w>OepMqV#|NZ_~%@R_Al|qa} zlZ)PudHS^XBcgX^&popaFz>8+!Vd+icyeo^Er})?geVE>3lVt!f2z`0SFcOm4k~I; zCjq*w@WCROQx9eRE}x(Uc-P@}h<`T}0K5Rz?_FxxQEa6Z0z&9$ks+P=Gds!3^1gSQ zYn@iskYc1O9gsu(;Q!xcvy+$mmoB78n{6dC&=rT{*HM~8$d!U>@t^%x?|};ps1vYE zS+uX?YvIfBnx5tKCIzHhVe<8buONrCx)Zf3W%$Mq=hrsH1kp3W=!GhDOC9ia74`EX zn6}IwQW|NM(o#u%_@@s~+uMJ=Q})b$d%J|t1PLM{;uKPd1PO9yS$~@$+FbiqL_`ef z;?jtmO5vofM2hDbgb-#qEOg6$PNPr+S#j1xjOrwVb9Xlb4k3gv-L{ zhy)2i{B16`hq(U&LI7hxiv=p-jhe(L?Py!R1YO4=>ONUJ4si)xIS$D%(~bj%00;_W zbV&I_0R+$isGcbzD1s2$Yfng#LNV4@LY_RrdK(C*oFWt`AiVL0P@qQ_to z#4=X;Adj#y#pVio8&D58JmcRL$4{JpQ3@#mf>s&`j>yst%zDJ%l>K5L%Ij(4H!U zj&vb(WeA}+djD39n!#=}++#+0%|uC=EG0RRSqhyE>atlqmJk?PB2o%TnJutX$b+Ru z0VMUd!qT9Fm$rS82p>L+ovAKg>X^6sVk-Xk^_|TkR4Bl`o@)bzC%YDbx;>XAxJWAc zZNCf;DaDT@h#;+mW~iP|nxj-c)$^SSP_wUV7wB-LeHrL?s_g?~-t5;v6uHVSYa)=5 z9rkM>CQtc(HRAGByp)ZE0+p)3d;~I6`TcYHUc$&lY=FR|$WvXq$A*Y02VMfiNK>F% z%h~;Hrx-JB(YigfIget(wfK)yQDUUXlBZ0SCS8VF{QF{RPC&qdPHPj7dj4rcVS`e`@a6*wFC3oQr;OFUh68%$N1$aJgKMF31&hMO`p7G{l>C#a!7CG|0Pu7ar~& zum2@~`s#z=VY4>CrGTp;T$-|tZUlw5*1qoHH_}P}zJ$$gapZ07_h>uqB5P*$ISf2} z63M2RYT6lQo^1{}<&k%gpoJ9^0j!kQ&kR{nn5x6p+~+!DptphjE_W6WFRl?W!{%34 z%dNNbdE^uftB>)1so6|LEmJFw1XkN1L#`s_N1<(TFO9X-UdPrhr0;d<@7GHo{R}c9 zTgGNlcrVKQs`6$??k2ejv(2-_a{S=7#ab(mx7lKwIQtwb)QLKMmmYS~-f0)i+i|PK zrg0SaJoMjCW`4h(im!G}V~#(`l+*k3^k<#*Ztwow_Gr%*un_kaH71E?(i1H1J|+3u zOlrXpE6AyNyk)E8_3GBNk85A|@Dtq2WBcX;BG=ad1k~@_R^ImB+C_ugB1}8i7^Cr= zemv_A$GS)*hf^IvSJgO6SaYg zTE(ONasFTmN{T%)v@x}o#z!!xO5Yafu=&t{6Q(%fjxRL`q(1d&s+INxpv>}!d=}N% zQrn!HDsV*cB(*ukIPj@y(xjYy(-~(D&8bDtbZ^cRx~d-RLZ`~4<(nHuUi+`dpWO~a>~KJj zH`VY*KprN_dPa!~T_lki5)#?#b0S5$6Qwtv7}mI|7dh4!t4nsox3At+Z=s?kN;lX@ z<1byN9HTse=Q_M`nq%rMoKhJ2aw4Dm9N-=c)1{lKR~Ovk_K#eQK`i!Q-1Mn$t>g2T zq`SVt#Y&cGsL>{xF2@4Ptg^v2dmM7ySp`zyw`;`kkrqZtJsR{*AD4uiE7%jUE{kwh zFW=4d%P=p+_-LTV=#s?EH903ZRqwItc?*LYTaJ&koc;ehZ*l2D8=lKR?tM2%{0{2g z2YvoEnEL17!oLO!ejYsc%i!zJ2b;dg|Bl?2Ox6!PKD$ceKcquIGQtbvI0ewm*#=lI z1~6-#tbtD5oLVfN!D>0ReBB}O=T2?1>!b+NBh}gFP zXxMPz!b1c;q6oyqC4`bv(sJa=$dj)?p(4dflq!=|t^$}!s;H)hTI#5$fkv8WMKi5w zLtBOzbP{X6XI%A2X%LrB8Jj_fGs_^O`*KM`53>n+_{iV;5By^I$T8z)`b@{b(8!@< zGi$LRXrVwX8$z(oOC*T}SzR=pFP5v#0ah$XRVaWMfFZ$-3RS806eORNiiVbsfl-jL z@kncz(V~Y2&Z>=GthKv%ZI7MV_ z7OJUrrp_AmUn(^+(+HE6z9BCg+0xT1sM4mbwk3J{jQ=&7YRk%*vRopAwpEEuL zIeY^MKp_lb00vlShR@ImdvIBzd?URjM21M1ERf9-C;MOwh%gTnpdkYiO7!r6_UMEV zgklt?AObO1ji0d-ds_n(8fSD^7t~>hWptYHA^n&%W}Oy|Ju?Q#2ZSi;2c%in%|vI# zI(mvc^XUN90Y#B8MpC?V0>kK-@e%71&pZ>LHlQey+32_i+(17``h4AW&-0I=`Oi?w zHX~N=DdU+31C*7<22f_OL^-N=aQfjz&|8LgA4VySltD11yR2;64R;DhkF|hABv#V% zPWNjfD~4X51UbU0p7?gcCPjpy6*Oq9ay}`24F2y`%enQg?Tcb1b$CRPV!B z`khz@q6NpWB0+qMWXSJs7UPp!iwI4F_9mC|;VBf@1{me8z7e1E^Ex`1HHtW$|n zjng2c#n4#PLv$PrM$(Umppw!Ol=f9Jl?=-ko+viwcobm1_|X!%l)VpXM@7dR9oIWy zeD;2_071&~sbMlEWIVlzcWIE;=h9tYr2f_`w|S=eIijR4*#uU)n-QQb+#N2Xhl z8%E(+nE>q?glifwt@P%hGEG7glLE&|AM1>Ec;w*h;8-RN850i)ITZQR?K`R(J`WL4 zC6^^U9D&6S=GM1NtP>D8CLFPzS5e)Owc6o2XVSMi)`Ko&r(jVuqp@O<2s7ALG8%2? zx)B`#fo(1dPpfh(5q48gvtJ1)Y6qSA>8SnmN=4780PQ2GtPErMBJ$j9JpCtRaQ|xK zwN4R%441dmfJAT+siq}gM07{}`*gfyWMLru)?n#GmHXIITB1UGWUr7mpr7<0Qazkm|5}+9Zh-Mn$N3 z=|jRMm}`t@I2v=_XryiG;`sLP8eQ;6*O*cyJxpMX)q1%>WruybYrO91JqR$Z=iQ4r07RL`xh?1aIne$cJdot-C-}tg$O)2W=Pm_jhP- ztipk~5vBz}U4RzOaZ zFB#eRXbtToC|E`0>2py9&GkH2jL!G7CWZ2;A2=1V2^x&B4rKl(bE6sojCIp3x7~5q zJ@-BE&?ApM@v2J_B*2JKhtyXeU0}m<~P|VmiZxqQ>l21N>5Cf8F82BJ&)1sL#JYf3j!KB0g|F zo533!N-X?2{0n4uESEMG=5i&&)5*(87?6BEC!ERWl8ya>_<&lspv@_XFS(U8vbt#_ zsD`aMTb2YkV|<~s1eWyI-FGiCdE`aoLhg5m?4dnpyC;_`3M-o@{lPLBN?w8^pTLyQ zp=BYWY7$6ILqxp<->85XonRPe!!@pjW!#Xk!So_56@p->GTNFnG#gI4idZq(8#cTpWw(8C!w5I9WLmJ;>HYjVLa&YO!!q9(WMiqN{CWTlg)lqMX?3nVRI_X_zazI-)^2L{2q+NT?(xIdJ(y6o9 z9`m(Ef`hA)bm^*|bnDJqe)v%%nKW5FnKqq`%$UhVX3gd-bLOfq^X7}~13zmd3l^#* zzx=A6ELzN3mMmu_D^{x`B&4il%Xap%W2XwTYq#G0Y_AHkZ@=FCj#>lx<497X! z*Wcp$+i?x$#7TC-K!zgtAtoQ;Hg7s+ZZ4DF~0Kg@cAU9cszRR z2@>KA%#NOkQdwk)pmdazi%LpSRW;qG6?Jr?o?g_~Pfj$5hDOOD3CUrJ5hyhxWTqF& zjd-X?R2V6WldE8&zkIiU&88>nroow}^JbYr&oK+mGn-m&G5=rDle?5}^jjE%EHf-C9A~w0tJXB%n$>mY ziAB`)$f5z&Y()2M!j$i^*^Fxx`21i|e6-3|oBTq`4sy{+$*9 zf7Bd^aVVij(j3dki5!3A1);#nq9&G|(-k>WHK5MipwoumgZg z2t^o$RE#8)G7wj$O!%Rdg(Qs9h=Y`a1cHhnhNyfbkyI3ZWR#N1mFG~QqNFNS#Z{{= zs77r`b?OSCqBg2teMt=(3TV_=Qj@0QXy^qsYc7O=kzI?H{Fs=9u(0xD<1~VcR~#R| zBmrS@twcG9iA#`@% zqYC_)o#X1-lE>WjAnu`30!9K9jbK>0FL*72EadZRQB)yK7pkf@Qd2L~(5R)URa;xT zk#(IyvAA_Z!$KpY)=d=pys#`lI1r>l(ZH}4$9n`8kqtSyNx8auqo*GR20cT=Fg6KO)5gp!0DuGn2NYVtU;&3W2xOp8B^n)A zY{Cev@^!lACz{pfBT8-f2#t&CwD zdx?%w_!BLd*&9;fLTkjGEUX=>`6}qZoMOVE^h}5H=bELmQ*Xo>0m;sbf_erQZT4Cz%Fy>*T>+CLFo+!X;EOfpr;gjXv34C` z6xn{OQLr{Ox$;Xt%G(SdymNJSE~*I?s&4-bt&~Se*XK=#dvUc;u)JTkrzCFdI?0IF zueZn9L9Szrh*;=IJ5Wt-Efx|6>RN^eL`K1oaqwjl9GL=7UO_yc0?Aqg)eqZffHGRa zG}%QzQ_ZVT9WA`!zh9*!)5`c}NKtir5Z>8H5Go(Y*2BGxV(Hy zMIBXus6s&@D0)fQjToQsVmpQ46jl?=3VTWZN8G>vHkmj^Tpm9xCA5G^LXl7;6bVHU zBSnCOAn1_@Efk{>UhEsss;C-dP`_L*r)Y|Xa=Bd9ys9P6U@`}7Mz&iW$ra=kgOXBER*6Sy&0#IszNQAGCrX!n(iu*=$dWpfWFRSMq>dudA(=ob_)%vMmf+En9C>eK*kDl_{1Sbxv;LFN zlAd-$h{uHmhEcG&^+QF$0{SFjnb^BR&gsg(3;OMmhdbTDZVtG1>!#PDq*?AQL&Oqz zn#6v`-X^YQO>nrW5kS$!qVJe#b360(#3Nqo0-m#~LhCO*lD9TI9p({QAuoC;bp5-= z%u`Akdn88D{|YW!xq?o7H5HkiR|RtJE>gJAeb4}ONSy=L2qCP(o0GgrSVJ5+GZ|@d z5y6M`SlVj0+i6;Arg8i_Bbh}2+Z{_E8tJwPN1@2yp)?)wC1`?%)V-#>LwpcliP{_0 z#1J#!r$PuvEU92i(8^i!5ouaWC!oV(BQ%#QTnQ<&_Wb#K;A6I1vpa38x#^rD_uf4a z%+p$#;)J@RFYTdj_y&spyz3x@gu-Vd5vh4bdG4L<*GGUr?voT7iiED6y?k(@V2Zs? z(6CFLe)|lJs?RN*wQKo5Ye#x*=9%c|JGKhRnUWpZEIS7wu(VF~hMMsH*)lYl0~WXQ zb+SNrB6r^tP`T{%gbkfnhTM$bA1*P`*UYsuusIc*5Hb@3?Ia|=`&j!N9Fsb8L5GyA z!yix>Noi>cxpJBGaAtODeOK-Bp!E)KoQ6T1;s!y7D^0@#CZB4DGY6K zF2$v|6qh>sLCC8=I3MTpAz#o2IE4WSiNto056gp&q+?7~kQWMYIv>UP##9A)p#Z1z zQ3{V@%eHIV?`wa@B}jyb5CB6ong|gg0F9P*$}mUFEQ^|Qgvszr23|6pQcfiq62WZQ zc5Mrgu1Hs|%v>cIV&=-+mAS;C)F7?&VIh%NN-QO%^ssU#v6Q&8EOAFWozte12|A1{ zQ*#)EK|n0!$QXzL48s5h$#fhBFc^a{G6paRA%hs1QwCv>^FU^nDN?3Fky3)GP5LmY zJhDFWeO8g#3>g?iTDzk{hqM+)zr3{BrQMVp(m`!uo?2rXSz2XGiP85(6;@hYxaFq( zPF3OZxV&ys;VSaDyskd99A2B!GC&-4!U~Ve<8{YXem)m>V#v4&$jcV3b^e28!m&aG_q~1nr%Xqd|s4kA>F9dy{j~pryzM z{In9bbYG-`br5ZOwdr|MKWF%TF3)ZOo&i32)!@xP&7woK51zUW(t4KccW5L@Y%ZN@ zy#t32dgF=y>KQP+yUm)Ur>-VV9ZcXwFkDw@AfQKZn*_7R<32L&;ZITz08+CdlxD~H z!TBTLuD1|}2sF%{fEwewtMCyMVYUY2XJY5=hO>RRG2+ehzB*0Foi~UG!`w9sE5jv~ zl(TcP((YOT&lIXUM2O@8bpm}_trsmajydbPxZ!=*&B;krk6@}U8tkNfjC7ak00eYy z&oYE}dy!Ftwl5dsHN&{=$x9W|(bWE!4UbaO27QfF>CpXsQz!De$vW@;1lfJ9Bi<27 zKeMp=^(?U!4r@HXv%2!=D`aiG7?$02IOaRGC+$2VWRWXU9oD}P&aD!c2~Fk!Ph_Gi zO(b7RGfx|~`YTZpP53LSvz1s@ULBH)D>h2yUX$QbWTrxyuCMOZuyxpMkD4;1>*&@p zq&E3N9e|Paw5k_^;{><=31aL&Vj6x7IN_=C92reO_~KnyNMOU@9AJBT`%pT*Ey_!c?w9da83@pcnTeg{E&6;WOSmzN@$B>p{=SWMa)ri z6hj@GM-hV)WWHcf$L2~%X%+CLz%B)FDIg17%PX~{QpgmGv8k=9CM`6IuqIGk_0WB> zEs0w;U6Hh@6IVTSUu;W)sJfG~lA{u?YNFbNCN5GJsWBJBFc+zd)EL8JC8k=2QPa`% zhd=}(kQO2kfd~yk18E=v5y%y3A;OhFga&d&kY9oMkL7*n>fy9uP{PBs7qx7nDFR36ujs3;-R_X&EJ^ z{=`5J&;S55KnH*Z7=RAMfClLDVm%<119U(yyNra>PD0uJ#-S+QR3H)0tAu>E>6wbS zs#;S7Ber68s77EVqwL!3j4>;L#c_1avM;lKoSoa zp93+I#*iW%=90R@T;Ro~c(24l!nXz1U{{Yu{#Upn?TJ0nkp>cY0K`c=?kJd>{v0fw zhO6T!br=V{yAH_1i};!H$iY2Q5d0xS5dV;L6AGxUqi^B|9;X0I1MNDn-wn(GJ0B z+S#q@OJ!-j4<=?tHywhYT`L_=FG|9fap4GEtkerHC}n$w@ZlgFniWEt^$9Z4)OR8G zw%{V%)-d;d@M?C}yra7r!l~~$eC8}@PjBwND7TX$nONTghzCgyD-tq6g;1ytj#_q3 zQ*ab^qO<`Q_SU^@V>mEFky`1nT1Q2W$x0COOhtf_sgh2$nzRTTAXlhN!&Eg&;@ANO z2U2~y8a~?;-wdPUOh>e}o~)59TN=vpWRbkUiBdGRF?Y7)vMQ4R7KWIR2+&}#pVHSA z#VbnEgNPI>RTAs#S)cuv4W&4+jGZlaQrL7xfHE#50v2{5|GJQQUC6l>rV(Jkwx$71 zL%IkGl47Mw67sC+kYQbImC$jTlA;_?@Rug9kjAa` zj*pIyuAV?3(DBjn(G>^^ER$)-XF&>5kTRek1u4jeY>G`m3Q|1EfPy?oK{myspsl{P zem$#S8wA!zfcjiIgG-p2icQ5uszPH>SBU6@UX{gi zNGwMthg2ak5_3lwhjb&|8v;3G4CBQu88cOeB+QEsBqA{pnPW+J5)*N{K|Y*=E62og zNGuu}4;ZHj*o#{dGjTIoS>mRz$5S;wmKM+ z`?u9I>jGFWsSeUTo`*;ggEYbxkcrL^6M#y7$?U*PPT;=Y9ak1drC5ov!jTv(wZmJ* zt_}u;tC&o9rqT$EZJH!O+GvugeRJQ{c$~o^1*T=Fp-1p{=bm}N;6j4!K%U!(jL_0^ z>+E&s&xQ_alU!$cY{&YyODYcB@!$yfh6 zvj##ka1j}{F*hGcuQgW}GNPVJlsFcisqGtCpja*BRBBD3oYc3;(>Cssqv>kaDX>i7 zJ~WvgM!NxuU48$`Rux^z?c;Y0k&N#ts9OX$H5l%ZJ*+MU?UgTR;`YO8$H>$R28u!0 zZ1_>jAbH9<4EQxWBCpwwFQ59Ad?uS0=F_dHflAqHWJl#xY@mWolT`I2@)FeLd#=z> z(kW;!-#fcYkCRu|P`|dOt?aY+bQ|ilVL(0YLYwQc=Jg6SP%30`v_!=lXxTDat5&hv zwALbCYKMJyXYq`wi586m^TeD#Jo7hIVs>;}Pk+|h2JcPc`LB$aTa;$|q414&`X5u? zhLf<05>wvWNZ2Xc2IB79FqwbyIDa^3nL)B}@*Tg{Av08%7Vk%K0HFf59|1lrjR_U- z{G4SS<|TdE=r25Es5rw9Q*qu~D;zBqY^(BFpXY@HL|T=U2d(x00c_Cfegq2)OoFzI zI4&mrG^b2SZ8TYnsRbpm@_Ovr7OOG|CTGM}m8HN+M>YvnDI43PjxX9=-z?N9HPoSG zNCTHupk(OW2{etP(^NRLoc1IdOyo5_7+w3GDqBQdZ@Q1T?XDHUeRF1Ipg+fv#@o|U zxlsYq&u~EEQbN&?hE<=cEutpso>MUGii(74W|rbe$|5FaX635yI7;OaHNF+D&lI9W zVf%i$6y>ZrO)&d7jKer=1>-Od<0y{8D2&56jKEefj^Hqk!U!DaNRl8F4XJbVj#HQ} zA-(9jdFl7DYL9>R%96bh?lXOOqm1OEf!u_oO;o)T^6JQ#2>N z?^S(WOk6C25Bo8d44REPw0LJ69QgklPWZ93X=s=SE z&|EFfuHI$KZ+u}@nWZc|Z(OJu?onw7J8{Q|8V>4h6y2aHHCmM6j+Yrc*t{RB)$SE) zOepu$m`WQgh+p^i^em9JXQ}|*gC5B0R02H?QXDdkhTFn@5?L-3l%{N4*?O+lNP|F} z$CHaD>7`c?5~smpj1^nDlLiHxd>93-rWO6A_Z{ahkr8#0M5{g`<@u^r~L1y+BW6S68vy;`D1v z^V&@Gnn+9L3t@hFk?jWXZoehk-(cP?6AWdyLFfVUURng{z=IKq@kW&)T@r|%xW(}J zpj5YbEf%}eny@-U}z!)$Twt?UQF=9%gzA`i@d`rmKY^VYjzcOYwV`Y3l`2Bd8* zzwP{0Y5}Z8ZZsD_@|K7Ieb`^fj{Hgm*}iQeFj5Ox*+PxBr&OG@V7{Vs!{7R0hlOBf zDPd&?`x*Boj@E)E=9hYOJLyp7!I<66UsTNejhh4e!eY-g5pgjlI$7-_48&BiE^Y7= zUex4+HY+dc;jeE^Ofx51B594XaU5w%!Bf?|=y_viry@!Qhro>t%~46wNYy$@7H1pK zE$N&j4s)G~!mc^7nEg^on7rLmvn(#*g!{(WEGae-7e1^5)1-7sAIfIAY@+5Hlr&9L z%TZZO9&*lioaXofk(T?*ftFG=()do}S-e8M(MkDYo&4B*m;3uG2X@$U4tSoqF(O-s-ild+|!25+2Zh@ z{PVPsg=socj=P;U`Tb+s_<`z%aV)2cWjp18sY$Wq`Z zEf?`Xk&TFuLe>*QmSn_)(z7kuzALbmr8MfLDlXYrc$J_Dy-qq*Z7Jzk)GbP#Ka zCJ3dwthdtCBQuHwI@&m+aZE!U zrCHE^X&dMqns1+8MVGA`4PeD05ZKug(GnU*W! z;<}(q&Y5OTE2o=tuhTY{SGu!%!D;2{EaQ5B$`dHLv3VBm6Ht?G!kih3k(WOh?;FaTSbl$Fj6aRXcml> zl6Q@o0wWGRi^%0HzbE+)w^s6hk%DF6q8Lus4JOzy;a%9ODs>QwpZ^8{R@^!aBA=P; z#{+M3PU36kRnuS#3nzulLiYy3*N@6Qt%Ys&DCvZ)WwYNy9c-og?ss~w_ubpd@-HSbsd$WeN3?9G*5=lzoRqOBGpgC)XQBuv5a@x=1Bq;^R>~~RknB1- zi0SS4so9vh$`3e<6;IXo!BJTn!_vckzy@va`#$M`J*|SGT4y2f25JmiJ5}%CP#<@8 zesLK_agt_vc>*Y#Otsm~+xH(neil!Y48lz`$z+){$&a6IhM5zJ=Mu(AK1HjlX=nk%4OgS9F8Hyk`$=aqx;2MP*1C8UFgqnhzM%#(Bi>%#< zUj#M#a$NH-4_n(n-S-kXIVxIe8dbelQPEDi_GT%xuhexQ3es})bV*;S)O9e{0&_Gr zsN{*77EeoUzW=0+V{*np!zfI1i)3cPm04keQDnH}n3O20qH~%0s0i~ja-9zi+GMoo ziF0=BI-|1fl-5?$RP^b?yEh9^mA0m4$BJ&&0#DVnLyr#4n#0Uthz1c2B57s)rR(NC z;fL^Z1GZy|9-7o@(#~YFwecf56P=09^v*U0f{cMmkP^|V7{EdvBUH&PgOT9%${?C>SDYT+%aFTzB~cn60MK}_MAJ%0 zWJ|PJ44Kv1p4UUm?b^0WBDp--@;X*;3WG{4+qP31I9s+$87~i8f_NN78x^tyC&HPQ z8`jrxgCEy1cqYMiUy=ea&@T~Ro_oQ1GDNl6NzJe9$-2kp z{gaR3XW6@7_+w@F3wx~M!Rx{wYqq3hEb?`0OK$}S0-vN7;_%}W9)6$1;kIEo$xboNDFhR46=gXCQ$#io>CuW8V zajhFeMe8gD=O&G}i;Dtmr80co>MJHwZ6e3>@ia)o#D>IF+dRet304PG$=!F0u#SXs zEqP7AtmS^Tdi|5%6d=K@${~Sq_4h0lh@*OwYQq~1Qbo#-WE+lVm4_o$hr2UOH-qUk zHHes;guC)Pguvu(00PK{!h0kpN*|iDV7`yj<3uO$Ds~K9JbRx1H)U6vaC;unm$Rb9 z0l01%6bUO@kUdGD+IkcYa%+`0Aln>C_cYNF;`2b5#>Y+GO{g3bl0mgi4Xx#$Isa{y zmQsk-!J;QjLTjwsz4!dG^wnJcXWH_S@ao4HUUX)Xw`FPtX6TExy>Gwd9U>&RKFP>o9Ir+&Km*1W)9 zMsn4I+uYQM+V;S;zc;d~jY3hgtE+Bh06&8ruWnUL>N08>6N%(#<66i3IX}r6n-%ak zI5rwV(y|YzAv+qHM##MTjA>Deau&E{EB%rA9irp|?649(!ejRHE|%izd+H_Po%y2F zit~eSc}m-He47d+MHvN$h*1bpQ-!XMI|{olzN?;%v7y$t&c06aXl8#WeY9|(Gd@f>h|!sX0>gl@ zp7>w5quqvME}r{{c))iDQI880`Zf&>kxZ?hx55}@EQN9AF>#VJXmxNL^S0ELd|c%k zf$Q8TaFh8Q-0V(qcX>hRMK3b9XsRmiVYkAKCRC|J)RYoKO^sMR3t`JWV?z?Dq9m&3 zl*Fo7;;MO(L?=QMQa3AN^&@0*O(cf8XH{5T31MT)JxhZ{7VjjgK3f;DdTM=R)^o$- zsvkB+BD7c-Nu=_6T-UbP-|6UiT)HstJmOR?Q;oE|Mvgnk_1-e>AN!I*TO zx{3THJ#?I=p3Yk3Cn#jyPu=;C{u9~U2r+H1<=4%fJBsnjrN#r`o!@1s6xBESr>Klo z?d3~AlUGpJ6e6iaDv?U05}5{)A(>25Ci2oEv~~Qid7|0=s!Uz8y9#|T3PKsCOw34+ z%wfQsi=7%Xl!+PX(KrW;qA_c%qzoxT9?LwIaa*Go)Fd29;`VX-fFyagRaHDx;oG!@ zn-U0t5Qr{9AOu2S1cqP;fe;9a=pqD4AOwb>2w_c08B*#og5`RcpJS`Alisk5of$i8 zY&z`_%h<%3u`^>cob)%>exF$RF6Wwdi>0*eWPWzB*fW+}qS&p>+^Np7BvOkKK4>;q z#5e;C)NF8L4D73`quG+|nTLO~*#BwhnAyGM7-T#vOBPt1zo&_1YdVh<-9(UBZYj6n_9|3LQ1Rrhq)T3S zffdw`Qze#>3ZrVm8_o7)|AI>ZlGSn7F9-yWXLW*&ReUtFSI5RPdSwS@lAFUTHUh^=SZlr)G?C9N>63_JT5mVXy;D6$vHFq1 zURprrHrgAd4?juoi_>CRSWeBmaJ&|!e{rtHJ|f5^1*aha46mW|9$i$U8Aw*;wywZj zt+l5$Z{@Rs%_iT}KC~yGC?(ue9TmD^GZd(s+x3R;2 z%6%5qdv(rZE8fi#sySwVhNcuaX;dtq8cnC3m8LdTajqvyg!Kl=hc($*GkI+gWW&M( z8ws+)i8W_Ftwaz?o2L=#fQ@qGac{DK#ZLTjYzfYQ4df)RsmO3eyP-$zPi1#(%31o= zJa^~mnM%V@Q0RRM5aX6t$S7o>MlA;~d5?z=Y?o_~5 zNg3xtf=ixXz6<0605E^)KT*I-a9NFSjVo&7kl@~fIE&9&f>DP(Ap}E~C+1dG1a%T6 zX7RpI!b8PDB7~UnT)(ie&@wibpAQh1h@x8|MuIqTi+7kYI-Zdj=zvj}(wmb9H}Dmp z2^eMOQ<*>;juC}p1R2qo0uNt77!q|7F(`Zpb`RnmDP>InlzRdQfMg&;00JPGO0!G1 zC(A%MOct9%m~t?tLnemH$t#Ey#S}XL1VA#7AppTt8eNtF83G8C%^^?%B~SvzOgR__ z!u59icXDbF6O$&!rWU4%sXFjfU`mrTNt0$IP0}Py(G*FMG)a?u(u}0} zG)Yq=pQHhdpl6Jpl@(PLI3kXS<2=U&5l6&fbzOALCD&aRE4U)!6rI6y(t{#S=K(aw z1rdkUbRw*!=E+#sQK#{bpu7<8oKf!hRepvs(PC98<)-rJa1n1%&#s-!)GKuYe@8C3 zK5|o$(Vw|1eYMhBLjnmAtF<)X2S0)BufHO0G$xSNS|6bT{dCcpJJ zaUqa~F@-~;w4feV)ubw5@TvcE-vjxKx0!o=Hw*FPxAheoE6z`uo!M^Hfj9)RJ0}cF zrVKE1;?Ghz`Ee?+PG01>xXY^jtfWQ?+2wrJw7epez>Qd+K)h3jQYl+*I`TXqby2Wg zUJsCD{|_&-otAEM@JU;oEbF4UasH5sC<6hw73s@r#;gYxt%eLPLhaN2Q(h8&JuF3dQ;=E(V~t z;zRWV_=~>goyW>!sm?v|ly~n>T^IFPzeV(TL3WPP{5j2mIl5(iU%QGKsW>GU1jNVp zr1vaJUJ+TL+PL@YJ?Wy>E@BKYAqD5~Nt)y7x&);`7)yotuGD$8R+SC%>oZ;9{PE4K zwO-iDsrl&Myc~orbeK>_-f|;*IDt#79y#2i{|&MslA_e7S~VaRosta-2k|m|a`D~1 zSFE@#ZH6=FUheqBzH!q@U~3p;(WX;khXa@D2C43__c+Fzy9u&D2Y$(<+C?W|gJgKw z$ivoFVA&7x*kPYpjlK0iPad(DNFAE}Q%6Z6R!?L?>Na;>V(kVpLi%064GEme{idp) zcPO$T8?LN7cl}_jI=S^Cd2VCwv+G>0OS?_bcRcSE=d}l0&kH97UOG$MY7~RcSRW3bj9XykzN$&#?f;`N zoI`S*!4WN7-l5&|EKn>Vh&?$hv`Yw@~34 zl?7#^=@f|OaR2R{OPpWS=i7-CnXt*CGz5eR#b7d+3?_rg05a=L8JgN;iB0Y5;T@uD zurSIMO@naEEdpTk&P^mj3r-SV3t!RTnbnn#MQu+d40;+CJp-Gbg-6eU>3JCR0xWtF zHoa7MFt*Lb1jWKHF1EyXy0Qd0p$y3(Iiv#V$RRl-0i4?)35Ls|zl;Ksj)d zP}rIdxpkRraK%;EypZgD)JkqTS{){Yz}R(t*@Q9-%k0PNc-OD)J+kB|zt z=cd$6*3?pCS@s1Ag+lW}VK6hOsVNldm=ER%gPJ;=3|cCUPHiQh)2DDwpE5b!2;}rB zjnj=Bjz40*jQcHtBTkCnNO}~rDUq>BO4pFyq@)LFQlf^GCdFx_Nf80Udp!@YL zLdT-zkkzi}#K^Mh6rJc&w2+hWgw5(tv^0GalF8~xluF2C)gz=9Qdy~PgfvbjrHG%- zD4*WaJUu64IwN3uE)V0)ety1A`|RtpGDJr8A*1V%QESL3E@V^|Jly%N-0j%ck9v9Q zAHW;X@H%FcF<+HG@jyuw`jWdA0S@=veF*U8)7AslUUKB-FkQu?2uo{W9UKEpCh)}U9Vvrhx67c)h$u1u!ASTEUb zQ~g30g%WB!Nh<&ztltbvXz3A$Kx0*L>szKEg|DIGR%P#z4w~74)sVNpcq{^{kTR)k z-#wk;_gu9w|Ck~3Vd&S3rlL4#)M!!t1X*%h3{bfEDeQI|*#M+T*kAM>9AD z(D#N1!|rGPT6|4QJhD=FV53VnobJ~NPN4g|JwQ9ao0rP>d=-8tR9aslFykth?Z~O% zRS{PzBEMpVBkX`nA(_?5TMU`(RID>9IspR@DJLW9X5p4D zlee3qSEX@L8o>#Nm3J^~m15w228{u1c$jVG;4Ax3vyAeZLKy|hl<|&Wcvosc9!TFSe`p&Dt9~yPhMf(2ex@_DYq}H2R0@+e zUFm-G`Ha72u|n#cRC&I8Mhs65jhLjeZV10Z%{+K;sC;5lkSXfR&S}kg(CD zq)JsL74U|t)rww|R!my4B5B2xN-5K-)##HnXhl+`GJfIAu6=uUc#cuU<|K*#jBMF- zpzqjJ(a@P)`}XYc9HWZnT)%Vs>0BlKY~%n2aH|a9dn@1jE<~H?w4Dt+Q(5Q)I`Zqt zuVW|B3G}Qo&0=O9LHyCnlcMww*^(q!JfScOqcA>3VH8FY6oC;Kg;5xe@i7XgFp9u% zY^}Sh!C8}a{zQ}0g?=_0<=MoZ+dSp#dSA?S3&qe(#f*v=8f8w*ff${y(sfc==ptYX z>LhXGWtks==R(hi~h0L4Um+ISB1{q+U+`?p742-6UyN zSAEC*A5SGJy(862wFV)DCDnLGs&!I#hmV75d+RxpaLe@@3Ke%hq&fMP({8!0Lt(k) zalfXtb2YlHN?h?u4-GUP8mKrl&~NBw8=X0Eym zF!Ik{n4X$YllL%%I=5v|DW`8YR+y2JWS*fGP&v3p2IeEpL)V^bi8|HxZ@Mktl!{mA z>py8$Qv`02S1+PrBRGBd#3SBX^?bFi%#g=$kl4;lY7Np52ZFUtFOI~KmS!oBJQwG# z5sTpgy?Oj4b}!LqZGqhz+$1FDxD*H1QK#(X&lIQCp?g01MKVrzTrU;piNT6baTni? zCvkyUd`@AV|1Rt76z=_5D!ljT&ah^7M_HGssp*>nOF_kibDUJMAHU3tw50DFttDnj@6o$kY(_>-tI?{DrVX-q@|QE zva8Nk{Nj@}whINqrQxgjOsy>Q8q)%D&&x|60<3~&({Hc8t> zlDk{v(^(pRQT`?QwDBp7_(niI?Vlm-i*#}S{=`tY@dse&0!3x;pz>Y%I4<(cA;c?hB6dE&gVh|GI`^j9%ceF5V@WRs=3_%J00oNrkQx zRHvfOLz+~8nPHjtw%Jg`#gp~r1_m}TGC;F|vO%yxt?$wMD|{6Jt3j%PsqZmhz@-7D zL6unIB6eG3h(kwi%q|t_ea@_2F($^u`ie<}i7_#cui($6f8UY<50S14F@$$5A%e6( z5|q@9%$c0lYY-D-VtvI7!o)njf=l0$Vgq3UvJgNxzyS_$KyH!?X* zS|LbRUX8j-5G!6+u3Wh3qP-$52^A%gR?Ry2i4Ex0FM_5?vo?N3jpj6#`o+f5BbhP) z3O>RXg)Ij3BLsvZT;EJX0W`{L6pI;)nT6FTt5G5Q$lHh>aqaD#*4S&Wct|;ravIP=U~ zaD#>V9t+*@Yn86ErL6#J;cem7?8QAqv9Z9$!aWw)S;U4O>@3I%kyzN}Yj74qno;2N z>e_YEJurN{3T{|C0ss2~{O60E{k(r~hwbQtcKjJJ=FqI4HLA1fe*!RIVq-{{!3=s3 z1Asz8mJ1IGh1ep(n78kP{+1S>Q< ztZ`TqSQizA4F;nQePE&SVQ3603riwc*07OaW}(?&vp->HS53eO7Md;l1gr6rT}4Wv zVWC;W-20CE+_WTtHEbhj_PQSeL7eLzZW-5YLbvXMhmm815K5N;mOl3@eO_0VkYH}| z|0?!%BY>%D`iK{fJ=$+S_3yxUq8a~V_~K7PTInrPvcblhZk`o3*yXUZDxkq4J=^N? z%RCs4v_E<#e04tpXhew4`iPQZh;e3^Z>5cPJL23^Gknrtz6yafrnN5mik51q@n%{e z*Cu-$b)I%3U5jL(rN4buf@ne;-SiV9%`g+pvQVDQ_B!Umt}O)=)8D=t!8FxYcm2gm zH{3-3TV$0j_Brk%tZBckKiuEs-<$DYwK{cZrkw%eWEf$RY>Tb7)qW>jde;o*^jEJz zU7BmJhfwh{jWk(~CDz#HfRhTIn&F=Q>NTlH3mx09%GTKygEVIrIhn#kWGGNp{UTqripH6y-kYtcCrkQKG^>)g4##L>xX@>JV zRu`{3ZK2L_bP*dMFsYW`ckWoK^jUJ?#s^ds_{zlA9Th;URHw;D?YiN6WXH!6!Lq$M zjoAp2rOvdi-{*DMi3e{GP+?-gq)q~UvTg?z zea^F8#)hZ)ujGAhBBaPsrcReJOZJcBDtx;TFFyPPi4Y4et+Q`+ezrU%%GGMnq76=8 z58Y~C+k{!mD1Hw;0bs$?f_M7F(6I@@)Qs%B!h7PNbjay9YTB$Nn{4~O zv(Gy98ra6Rg=pE%(9_F!h>)Y+#^3XI)Se?3UVQio5+QcM(|##Uwmc=u)oRe9O^knE zaAp`NKseWc`kmRq!y~w4H3Q7Rvw!V@1gqP-W0SGQ_;YmaHV>EJDOSooFb-61$|swQ z1S4ABaHXtQO21O}E9JH_mH$;J9C4-#q&mS=RuF(i=9p=+v4+c%x+fnd5-VI^-F4Jj za}7n$1y{NIg%&Duhb|v3!8KbEk5oE8mf%BYy@iXHZYap|(IUCOXo5q(v$s6*#utUk zViOx+Qsk-9Wy&j}UCy9Mv)0Qv#XmQYim#3a5XVmih}2(g(V+E18`e0>q>1nBO1l%M&T%tg2FIF z6+>J}q?TEhvX`S=Ryh(D5W_G0p_)aFV^ zL}CNa|MFRy!7|D;RQL$d;)DnnC03$TnNm_!wOSpemR@#+g{!Wy_S)8=PW7mFA%!-q zF@^O%{iI(ZVcvX*a$Q%cRjf><8ugmA>d>N=)vMtk963_-I0+M%UT(6S`HPgQt3rc~ zKQhCyu5IykAv6V^`D$aBa9o$)B>?28N zE$O-viWnk4B0r?)QGJOZ1uc$bV-X;PK~vzF(=;6q0r!R=1R)?G=$}Fe5ky8sf{3&Y zdbO$&DiuogC9Pv38&X8h={XPc@w_G0KT8WKb_FC-cgid!jC00^N7LyQpILL8?Deitl2n}av) z=&eoOc;k&XrEG;1La0HJ4pE4R17zbO17gIQlT?qKWFef!8R0Aw)& zvgRTt2?BhpP&$t^Y0_8Eb3c@PPnak&{}PQ=gYirDy zO*9+E$|m&knrqu4fGYX!te$>Gx;J#}tKe20&-O9`4-H08@WW%O)5FlU8y=M|o=|(| zng=n{jXqds8}Ja9qfj;_OoKIhQn^aX!omZJEl=E;hku(pJn0^5x-G`Jl{}h<|7$|| z{;YyJCr+zJZ~UvB{2|Tefc7p8WNKNqxgjnYPMmYpgY6)|`0@7Ok^nVrm8e zg1|u4#}YZJFe#kVH786mKSAJH*MDs@AlU9a}^ih085uM1P}5ue=Y& zV?Tekw;L-n3oDy#>;K#3;pJ1puT+_G6*+NFmsY`%iYrAwFO=CM7~@EjUpm{Qh>z+& zd}+<(rFW8&6T|3q8BSaqsDKrUfD#>KSL-VG>f~AORAH5f^S7rgcLXna;j|gE=FHoK z|8@44Me8$mAtLNSDl((x#dP&_QedOKKYD!J&v{bO40c=WtLw_ zmDSf;>)!7Dy7f`Rn$n_T+u5-$_YR{8R9OJ^cJNe4(x92#&EN zG2o3{f+yPxs^M`}A13-lbziT<(gwz{({514kPgE-jp#C}+nB6zJtpK#%9~O!t!R~A ztMys4$kA_Y1J}&-Y?irZzFBA%n{~|+EK`>4Q?;651^|M<5a5M9Wp8|5a|--8YGwD+uzQ+(b^87|0LwHs#8C?gCs>q%YPB3>NeHut|l z&_P!f=q`m5X~qau+Y>x!6@ zAGcKQbi%~CngrQyCg*w&QlQ+URH*hWb?UuHlV&f|rrWFZne{dciN5Bd146rUXWzmGOE0-pYXXR<%e`BU|vSX)fa^t3Z@)IOGrHPV*ppaEwkn%u?NP`k# zfsjQYiGo0qhCs3lLMBgIX|;{wU3WuD+8&Z!U%2mqKP|L~U%A9m z%Z4wEL13yFZnac7@ed~wv&MP|B}tH|!FSQBpQz4y8>QK7i%eN5bLjJJrRw7B+vL7N zTYiBNBW;@%q~H@&nMU!HN6PmGssaz7f8{OI&U=b|V7DjZ5($H}MieX$XrQbJ zbkM9!OP{eSizV^j*{w2`=}Z&r3}%RJHnWNOTvr#1VMojS(T~RY{CIR=dmRQ#nLOZn zTd_dM{T%6*#aE}}t)vtwR;p5?CIrHHi`301J8b+J3Ke=apMa}kutWb>;N_b+9!fmE zkHFIy8l1U*od6%Sjo=~X8R~D>zYb>p_^Jx>wS}@sBNP1a$;Y4ry{>|yZK35)ieMNy z{ug%396aMM9@v)4A|uNOxQ0u6lX6|*RE~8)u?!losi ziqBh77&-W)<4zm3+x_O7Nl&C_8PoK`%S_EWY0TEVD2jEu%bw13$J1Fc4L{%6xh92k za?5=#$b*br=5D6TUCeZabJ>-z%wx;AssN?L`?^@-hT?s~aw9@suqy_!v-so)W&EuW zgd1`r2Ec;40KHoYKy^M(jR0kep$ukK4Bu1$3`mmnc7L(q^icfY&i~&d9u2<-JprWJ zPz=y90W<-~09l{_b2}kG(k%f2WDz)dZqi|&!J&41s0%;$qMg~UR8O`e*lguI!=)rNGY^ZjW=XZySmJvrfkP_ovk&b6jQZE~33c?qGNj_&hfc;#P;@+JG( z$!-TrOAc#WYa2q%&*Y`5g1MG&`QF2HFhP5hjB}pzUEo}IyBGA`XFm67+z=^BOtHif zEw0$&i9p$3g6bClrqEBLj(V761~B7Hn!;4}bn(j>ausXd!Zvo4a9Z8XwZ}F0p|(2f zs=twbWek#+#gjal@)Rj`m2qa6DZ&ch*=&zv&N$~!f4MSl)wu3GS@=*ZUDfLMVXf;} z?|y7l|D$|k5>%i?Jjx_3{G1`+iW-T9}>LclO)JRJ@5bER{=e>s82KfL>M7AW$GYyKq~j%sC9G! zTj>zKSW8>mQC#sI=xkRkO=}e`vTfRuzG=3(R*Ch0dt288s9=)i7V-;uT0Sg%UTl2C zt!{mLqlS&GE(mY471l^zIiaG?Z`m*miZ5E7cDg?8wM zi7*JCwrpzTZYQt=VcDborr3`c;I^&HidMTo7dv z)oc8}yKndIo@NtV+P=w^Qjee1vH-N1iMw!DVC&--^yg$ykK;I9vXzgqd|mDRJltFzi)hW+O> z@E4tb3ot0rUdW9!vPD8oI3E2T%`jIUE>u8) zNUM;@kmQgov%X*mxCZn91-n$HZ0%ZVjX>8`FvFK~ZtJL9B+hwInj&T%UL2gyW?bQm zt)MLk3z$l&*6t?TOh4{kFf)G3kFiO}J>Za8h?&hrw`|7B0i>e|vXG)TSP{y{GNYx1Bw2N6$LYi}nZYMj0J1s?)`GqUcT*)2Uu|tIY0~+dWS!?0LmK zt4Kh3Iag3p&HXeiL5sW7nO=3Fgw7V%x#Bw?ti`F2<76!YLl?nRlgnVsaU!SmVOvE=Q>RS`@?TP#EN%+nreRon`nzZjZzyHSagUR~g zuKCgA{CL;>Wb%GG1wWgjpHImzrreFTDE_SG6zP@e+gp1p>Ru^5DkHhZGGde#y$NYI zF`Xu*+m!U1nnC|(R(2NUWZ9Oy+uD=1<gCAIcf z8~?QM%YO>B@<)gwE&bMrFPiX8Q{Of7Lu0;*uBw=-i>;=x$VF6MWEDkKS$LF2)Y|Zb zt?WU$IkhB*mgU&;?zN)(E$wcra%oL&tg^tgN8&dI|~u>Ut1{lm_9q#ud@m; z1zS+JgRMwmunpxLY)4}V>_Dvyb}s5#uxoasyn;Pw1lWrvGuVf62=*hz!2vX8z(JH} za0nG2soL&e|?}bf}J|fch=8=AgN&ki2kO2}By08-xCNAL%J0KBa6M6GU6w(s?zDG_B zQWL*$1Cn5R5*Lm`k_=7q!f8l~kx5-R0ZB6~=?fx7?s5(fkP+gFfq52>zLPe;2!PIdS+pt7yhJKqKF>6Pwuy&K%u8%^s??lRv3(|fDgz1^JNVP5Y%H|KgvKhJo{ z`13Mm^%Zly>J3--O$)r`D<}19SNXb<= zwl4&Hi~{}$B>30}{18afYx>1IL1vAA0+}t$xwpD+y{ z_2eGp(GCl4Su4=%g}4gfFRk(0DMT(SLqD07&r79>>rlO8? zq~uO?qO|=86rjaJy##VaA~Z-MOwys#W+F-^^vNPpg+K2(|Fs|cHY&+q5H#4Et9kw+vsm-0V?FOvtPQ3Mi4L!*dyq1FocXC>5G1^=yv3Txn}wNT|-_-!3j`wsqyg&OPOuMJRXBmA-niU7Wc z&o)B|!2jW^El}zQ_~u6_^AmjcGZX`Cg)g>2n_mc@?a*!q;kOey{7MAGL8D!S$8Ko0 zhXCz`7Qd0J_Cc%P2}nFN*-v;KfB^@Il0(q%Fi~^_<{c#u9D@bNiIWpBu~G_Nt6PoZjxlT;M{GJ>JD7EOVZqfOZQ2-zv0XSk|Gse{6q3QglGQ}lr(ttAIX;v zZ@S;#MHYl!jUfg*LT|LFgP}LM%)0h&E0Ds_TfdHMvgvQO+4U#;?E6=xD)%Xe9Qu}1 z&V9)xm%gVTw?kFj?jR+h_d?q}^xnwJ(ARuveAnWGI!yi(aHv1ktA0OrL(ujJeammP zpF*b1q3yZTL3xBe<6F(3tQmGFpGGyh>hCdpP&qVqrX8wIv*!OpHGAjrL9L@Tt!aJh zTHl8EMn0%bw0XANYJYE&KWb|`XV*`0)7T#64rq+3#-A{ z0&BrI3@gF7u&^AAD{~bVgR`}T-Qeum`8lv3oV_}~1`dL=59g1-Z{Y09e1$*2*)P}w z&i=w)aQ1KEKXBeZ1Mn9(zXkt-iy`<2T%1_g2k!UGUNQ{)hI$VCmU;^O4)qN9UFv!8 z`+lhxp+6t_AS3E}pa0bd$Vebfd`c501sD*I@zWT+bV z&KM!QtAU?gMtqX;g^2-{bj#pd&DoqcKQsywGM#LoT9jrVd;n^n4dihu5tM9NuXUDV3FrqgaXYSF@f3}Pk4)Q)>-RH4*RqQep=HPkjnvZSM1 zN;I);V{QHZet)NFh}OkzIpDhkezya4<^iqw6Sed>)rjJG!I;ajNm;D&sO|1P>A3n# zLaLbr&&qZ6L|7c`=nzt3S?;<#k2Bx-6~rTsG9g^6%GjjIv|HB}MFFm8L^t|YS#NPv zUPiRSCe_1GLJf_6S0{)P!)kPuvZ29@lnW{2zy$yh%ZTvrives{t6HvNph~VH!^Ier z*tL&H(IrZdcudKXJAW{7Wjuxhz{Jc-xp6I5&It#sX;lG<+v5r-G`<4_IB*g?rwn1$ zHfmVZd4Q`{iH@e`9SAn6kr#t|p0^4;ASbl!3X9`bOH1eJq_Pj!I;$+V03R3yCmL@H zhYC5u#g$ra7vi~E-MK(3WGxP=2YniN36HBU%Q|YMg|m4s*3iCw;I9t29vfK7+eTo( z{=uv6zg8LRhfeWEEn*tZ=)~uYMzYqIw(LTSc;d#aE3iM zFruOuM;7qv-mfj)Zk$2SuB?0m$;ozBK4IsHM12%l6qmU$fEpps;Szu`Mp=>Pm71A( zQOHOIaPAyq`D6e#GNqoEOo9dh&Lwe$L0MwB#NQf(B$;cuBQcwR z9ON8V&$9owq2!PcB<1E+Kp8}sz`$knEp#AbvFO)uyjPR~I1J#8Z^Ra1s2hO6o*f z*H2_Cb%}1Im=%m^`6kt<%NDLHsM(H_5L*wC!P1-h^U8j?qO0K4dMV0-NlVaLN>A zAf%cTbpT%TETfev0f0CDDhpBZ%9ra<>3TZuRznDkA|W}&DJ67yi);sq1{U(Hz$706 z?vw-tLD;16cB0=nV`w!lA{GHCCIIdL+}ZUlS2HST1>P}L`NJKgRlkbq%F<}T;2Z#X zAKd&j$winbA(c}N5h?mQ-D`~jnyhoH2BBSXk$r8J6!PKTE==w7?zyQDNQVVkAZJoqmGc9+YaGkjOq^?6=TYyU<#|VNnC!)0#Lo=rGvl`j?T{Ta%Nrz z3Bs=m+Cyf#S)>B&%@`u8c9w)9gwoM#AeiH4Yg1?+gPY~%O@1#+DFxXjvSjH6?T=bo zE4QjKsnyd;wfX78+%!3oc{~@$P918-&ACMuqBDivQb`Zu83t;jj+vFyzD74H`p_`rT!%~&MGe@lc|5gqzX%yZt=eZuqP8D5!cxoE*$-m6pP_U>T<2pDKnAF(ILSh?YUIYx<4HTx%5pdF z_%T<5=t8Q1LR?KN?dK{NI=FQ9o>$?-hYz|Y(>%<7QOJ`zrK;dstcHo|W_D4kv&+bJ z&KxSY0$93O0`i7Wkd4Iilp9BESak9uk3r6kxo$;>I1w2?p3p~H+8p3GU#6>|h0Gn7 zs5)$J2;hc+7%4QP!3rcrTIskytqqjz)Kk4|VSNr6O4U_jk_5uz&;Tev*S|u}3L%5I z6=Noe=DNIQD870HQojTnNH7=?_Art%Dt~$(@hI!RF^Eqp1ww=IUL3g-eWptB1q_HN zgU9eC3n@B&5+=4j=Vc~L2|3IbOc>~+Gi1L$cIJo;RO5V^fRO4C=gucrU2i>EyKwdZ zJX;CvsrPIeHK1j9QdadcX&t#$(dS_ky(54u<;%3|)F{a&R7m%a3<$zo)N<+=_N6l$ z+GF?6NAiA561}MLA9DQ5WmPf%Vx7&+85Y7IQvf~M$WGQm$cSP0OCKJ+g=v(Ml!Zb4 zww==0qTV2{a48xFgmeYY9%`*S5U{|tQ0$7claq1t4O4QmvqjgZVQQc|lclqNwSTAM zrJyq|*xYAmZCK=@UH3SQ<1Q206;LO>uD5pjIeDF`4NAz}UOT zYbEOc=xhBUlH9w4EZJM`FT9Thldj#y_0taZB7Cl5x@{JZO&mbj*%qexPkeC_m4a1}y;F;8`+$W>l2?=7}&MUT$Syt@&UV z>=`bvPvZKtQF&Hb$Hv!jgG|zsbwEv>l!1=ZNIGBbksJsp~bp$x^x+o48gA z_4j#9dK{#o7}4_ja&ha(UiU3=>+etfTK%883Rd{{apY{4T}zvcU#YPkofh~@^m5=2 z4u#MsIB2!U?rGNEz)``q1PWeYI>(}A>npfSDmOX3S^?qR<5I$B9PhV;Qg#*X!lVs};aY-7dAGY}CFORd zbxLX%l5ODDS%TQqZdW=>4#w`9;;dUD@G<~9u4qEe4VMS1xq!A@2V8DrISLe_#>qg& zFp$&>jDvtO(X-)Ng{8#{qC$UHjA9z`m`Z0h*IM7SvBcOSS$)>~5|cibEyv)!V9yM5o!9eW!7hHzPSKxq3-^G3UB z7X`+_fB&M*xAoh*>dV8)K2)9xy4PY0Aq9Aq-nG;{ZzPb8;2o_iZ#^%N0xYc|X#b5Q zUIG2+V)CJYAGqg_~As0LQVJ$3o&t8=ZXi-u(OM)v^il&r6`S^i~g+;dP;y zIFG=L)g@)11$RyqH2hOJwHH-sK-`2hN@VX|fmYR~&aTgh8qXfTQ002d*Dp*smz(ED z(r46x?})q->x5O=>ts@Taan zKX?h2TxY04s>URHP+xjdFKcr)soc%gxVA?utGp{&o|>j&l#ZA$)l)nvAl_g)HTa{0 z4INw<4Fbvm>HhV^JBuA&@KGU05;0~mL5(ZgHAIMhM8&fQ>k>H z=8hqa9(_BNw4l;j~Ca;>i&uy#sLzZjBpFksag(+~d=K5})ke-g&UY2i5%Trzh z+u~`}xt2LM;0Tec4m#_qqqeUs3EyW-hWWZ%(8wWqO=lM^BFHt7ThSa{bk*FBk<*YQ zd`YoKEkHk%tU&ha0@BM@?;CNVwbCZyu zh*EE}J`3YYYFnTz1^i#YJB}3pB{_t-6E#gWZYQ5?_-Zeq4^!kJ5Rf3Raz7~T=iKaS zK@Z^P`iuoWxwff-O%{K;(Y3Sa!OmJuCH|^Eiwe^zA6hUPtJ{{mu(fpIp`3j>Y~7}( z5{k`B9~%74{bZpI?g8_`hNPb}TR6tzjv)ct(}qUHm&C_DHX-Gx0~Tm)Xfrv;PDjl{ zeHWgsg!?E+>%&$?zK2W}4)HK@2JLRRWA~V8{KD_zFJ4e^W?+UN>oQ2@2A>p(4xl~EwWtp6UTJ&Xj7v0N=+{z-IA8r&jZ zY&jV)c=k~JZ0G68O?PZuvWFW?B#J<4>*6xyvHE`{BH0s2H5z;`0p$_TDbVxwS4kgW z9DN5%D4-te1yOIlPa*LJyd453r=J4__P_Td#ChUfi?h@q9q+@=-HIOp5yuu#|DwOE zuhZA!;-93vYj4G%SeHoEcnk(E20jG&j_p zAMhoCeIg6HE*zKXf`HsK-G`wR#!l_Z6&QhQ_@ay~_~uOcvr-&S-R|A(cIJ(T-a7R` z0C8VgpQC$z(PwAUKUa6PKF-+jFx#ge2aw39Resyf)#%@QxB-c2S(5HjzVy`l=FE4oq)9E&BL{gmrG^Z6rhqZV5M z0zUe&(9I{V|8S0OuaInEM|Z9D;f;_o!n-C;EFxAz+82;40AUiW8P^tq9qdi_{@2Qo zSiw64-_^g(ktYDBN5K=BQJ}e+;&wBRCuETI$L%(LLSWp=DoBdf-dfaA&G73>GbO}B z>NApXw=A51K!7HMrKKI1D7JS+?W?s?UQ<{{;6B7SvAwWQpW>E3?O2qPt}Gj zO%nQ6yZ+eXI@yA%JTienR#A9QuTa4ox%CEEg-Wl@oT&(QjLqpxm>Q(-RN!tdZzhm5 zLBT7n$*3o%a3Q?yfv)#`w3*bz;8G%f^8`rXj8?G05kt{K(!Hb>atJi(e~G>O=td^| z=!Zceozn!UWa+OxJbJy69wluBROW=4QySSb&B+gMe_nOa}pRqj_DAuIHS*7Kn%t!rs3QMdI)CP?R2%971 zO}T{T1z2YcvDw zNhQ|iX8*y3h2g94@>w{*t_Z=Sh9hXHheMo(lrPxp$asEqS&xju_gNsPMoL#7YqHU09VkN8O1AF|Fd)eiKnyahonv zH=^o9byq@f35+QPhNe=ZeyUxFQs0_^=9~mmNUmb9cf%H`8e}auwb^`Dc2wxSIn-fZ zz%}ZEe3> z23g3%Y{8Tp@-JP*Amd~2loVCh!+jhM2CZJAHXz~VOU_j$u{m~8ZY8+W^Ic3YyyKE6 zIYxKsL-pf(Sg}$oN8P;!&ORo(iymTZx8ki&?4)o6+)w=k!4ur9(z7eGxsz2K10vx$ zfdooRWv$#i-R-Faeh2>@04ANEh6lx*lbyo5>5{4B%%pJ)OQnO`2D>wi6-6g`rSbJUNq42H^m(^;dhCBp*D1G231ybQaW`S0X zO;~udojqZZK}&-~YQ)X4-M$Kig+ACba&f3br!xXM_L-tKb7SuvN5O6gJ=Od^;FJ-b z!i5<5ED-Cogs{m*e9xTRCdBv*vygJP`4XfJmyOFvo_;RKwStuHU>l2J(~G}0HQv&P zH8EWgB#oW6K;z?+Hc!jj9K#Py9&H`U*T@+H!4AuAi&iECqB!?UO`D;E<3==t%_M0*^ zU2((4t3x|mQIRiYCB2O8%1x~&Y%FA-GK-!}AXSg2>x;SB%WRMwXEl<366}+As)Yri zv}MLo$()5u)DV35@5&kyjVUy?^X~FRBT4*3Mf1*EHvg9TKGz%P{l*%I3UQs_sTCo+ zySC7{;4M>cMX~4>yKum*%uels=P7wlwh1zjXUk~!EPXy;-PXlnW{bNMr*$`ogc_K{ zR7fU&_X|B69s1TdjK|{wjSQ#g(twWguQ-{&q(45i2qVSyj!Q5R=5h0D6*Uz7US~~A z5v(jUAtrYy`)|LL<8c@dbWopFb4$~oCMHwcD^g+Kz>Jz}6(-GFMw_{%NghbirSEO| zYpAttxB>~+HU;**&2H`PzZy^MjS)f(O?N#lEVto}bfxH%+F zGJ%djXfsXId(CHlryHR3b&nxcD=+0e4%o0GQ4`H29AkkRNTv4fWZe!5*iLcMi(oB|= zv1N!EV;!*6{`uJjn{f5Oe{=Gv+kO*ce9*ncm6(kyA2%ts8I5;sqzs9%jcJLk>k+xO zM#c6PR}QW`wgJYL*|;w!_1olI%go|E95Z#^i-6F+la7@eL&|u=Ne`q>ctkN{-C%1N-)K#uI;Fp?g!f&0=&oGnard}g-BvvG_(T>1lD_fL z9Zh|4o&TXNqs4zCY^jd$rw~fOHcpggf8OE3>}*yUYdd#^m*0xSkIvRi)N?SAbT9Qk z3*@&wxpc`6%AxNfuJX>_o`JlU@@r&V8EeZ;_x+xvzCUrZ8-Jn+c?o4%wzfQ-VoWV+ zKVjAWM2Dyz&@&fwf|H6WR#zW79~E7k1L@j7&r>W$DXS|gM0jr2u zM``hGx}s?XS7iN+XRvr+2SeLFoPadk8rDkJjm!}CljJuk>(Yujet)e#4mi!feOs#j z2HY|+Gj1SBER3N#ThZU_4p54>*xZv~iNOWy>6 zXLW;3^$>UCJJ18{ZEV)=$#g)ItEA$ZFnXIoKCg{5WKBO$&X=T)Ovlv-yGX>|UiK_a zlmS;rIHKCo9M1d3uigf+mW<~%Qja!gu$FQn(0P+ZU@WPmHt2=?7x)6 za`502D3p58j{Bb;5+U`8m|9c-TmvTE=BJR#^XJp%8ZqCQnM-`^kth${$g;vStaF-F3EgBp%?+118?`#OUICFk(L{v7)B?%&v#@!)qn- zfj*W41D(BJ+v5>sR(esi1um%S^dhJ#|Es%M6LL|&W6EVb?q&PQE1wnbo?j`ANm5pN zlar!wG$g?9ib{r~s^$4lj65Lqj{D!6qrUP7TUmW_NDfWLPJ(9r8Vov%fLq2(FlVmC zxK1urW4?K_q8%Hi!a2f`SK9E#fs;(n4)w9QZRBRwO44^Ya8pqMWWgqAmX0{%to!Uf zWY#>}oE?ud8Y27OfrX~8V8kL`<1*rKLqpEF$Xf}S@4ypfYifbl zR`eC_DO*@o56La_Z=no1;gtz~oo+-s1?^#=w7#Lp88?mWJBJ2Qz$x`Nmu%ZUS(Wl0 z9ycG26#B`wj+jQmKdGdI<2B3}*00?vy?nLb^yn%-N?`HoM9LLXB&(3qZ=NufuD&;6 zl3xl{c^tb$qqdId2 zS@mJ%dkw_ZXICkhq6*%UK`LTazZtg=w(_y|pAE?q%Tn-w=ge6-2IX6$s!aYC-keIN z#EQQ*{Viv{Vq=&z^*^Ml?duYJmo^}cqg`Z5u=kve2T3QDC?#@upA{=^p83FZE@zUl zkgUE6Co2ViG?+}fUGD04LkAnu_E;W_QrKAscKO^W(4b)I)64fnPg!4YbY0nBmUh`; zmPa8DCJ%W-jUFPF!C2g8RDnaHYI7sU(r(%9Mgw7nHC=rRn{vv)K1$s_h_OgE1MC`s zeZLqGkl-m5KbvICvQbrtDE@~42G>-Y>n>gt7=AgIX1T~gEVM}qIZ9A|h~dFr2#iwh zeM`d;ac16}>~LQn#OSSa4E zeat7sTjrlTAO*F#sUsSlFWCfhTsp9d1>zVC@~G!o_^d`RUU`vNMy%9sgM;`=IOF1L zw|5zG^uv3#AS$n&74}@;8;M!x=-azpT3}miDSJqrW>y9VA=?OA$dOxvvJE|>=k3HC z*uXW2d2NW#S`&?za*Z4D#x3{ayhp1s_+D=92My z$2;$3ro9C*Qx0ff8n3a5k_2IDh*uGHFmV%@_zk>z^O+{ZH;o(-wgLs^!MJw$il#c< zXwi}?UGSl;(Z7)Ei5sijYJ5o!6$UQlhrFZnl0M*OuXD&3thm>CH16Yk6Md<zp>Ng|4=6;8!Ps`qBsf|LsdD( z!B|AZ#W#JpRZONjQ)A!itk5px0v$`!hp(JTRGZa5-AY`?ZZ{u9iQY(2Doo-nF?AQE zH;Xu57?tz_-yIMt^k?hth)j2L@KCz%3At7byHCW153&u`lVbhgr2sQ%L((^Egv^B` zr=!YVSVvN@$o~tI{J!FmTpq_er~u|_qg*-W+SOzGhdE1$!sifBDXeKuRvf6Vcuk32 zWv%niU9r9@+jGZa%9U;>_3U>>!|hunug|>$y&4Y#rC_fT;ToXWmU+ZHBG3l_WUtb} zKvaSKfa9sZFv_%-{u#Hy(c?_L^&olN<3w2r{s_g!r0FR}*ekpNjB6tkc zCML0aimaItn7cWLe52Z4(v?diYWUW%=C`eE3F&-Dn;PdkIq<67_Yp3*m0PW#{?E&b zlLW}Nyyu)=xlRswqDG(nk*&>8Z;3qN!qhy#?y+ znX1fhXyFo1e8~!@2W+cy0|`weW!`j4e_w=}kQ^paF|m0E5mH!9gDC5CplC68bymRE zmnJuLowX}_(#@+*oEn79ZpSvlfdm&urZ5+E&aD8@#-LyY0*-opwDz&~(k9lqpTqE9 z@VD)x`-)`^%$pdS-&i}_3EnHWcDLwDZ#Gtgyvc33pR4_@%AXFKQz(InPq9(nrnMS$ zS#D*%O;gc&?1-SK&Gm+gzdBd<-M^DnuXV+KY>Fb#HX*ltT;SM8(+rLJa*En+E9yy9 zXUBH4y!L2@Nrw#a&@n`czj-Fb;iByD9(z9r*b~>G*0F-6PK(naaWE-MZS$n{uNjA~ z@9HCEYmy7CQzmtjr)JFA~>G{)x^ah5XRBlgSce7 zPkh{=VOYLCs;b)7v^h2Ffny*A;Bqnrp0w zh!%uUD%usFq1`%#wqq+#y-(91MNhGmlKF}uYsOn(s&mCnY>%E7=lpEdiNW!F7|!$S zX{A!RRA@m+cT4yu6AhMJf}Xj&I7XK=J%CEVR~2+%3Suv7>|pHR6%}0D8 zJAVmwsO`W-X0kiC9959)Zq~*QmH`?hV4r@U1#-Yk(?LpP`Zu$BwqxzDp(-<={auUO zb`4;WRGA1~`JenD^#X9b=r2EZKD9^PaB zns+2-@D5J!LUo$2$9;bF*Qb6FIz`UZU*a69scJ%5KiyY9(k9j;?zqCwW$wXws4X4$ z+;!lfr$ui%qvN2OJ$0J85Fw*)p5m!?w#qo*&3#N}29NO&JcbAG1}a#OA}k@ce0D_DzE*GjUBXSeX8!yHV5Vr~(M%q=n+W-Jw3|Oqvcg?XdmkB=)i`9A*L63;IN{FLQfC zQA;~@6b8liamh5>Gwlk?UV5VZL$kz6E+u^rWilNCxz@+J~+x131d z4+a18D4x)rZ%gyPZ?+zmTgQGFNsN5DB+6_XvOiI_DR$1_zVYZmg#K*3r2d>_#|A>1 zLRE90Tkb=Pznr}~Q32=!Y5TTRNvc`<_2g)0UGwKBEP|0->amd-Z(A2(LtyxL>)AR- zJq@+O`^vH^XrM#wEz8BxlvNDUP@CM|GUiy@1U4Mfc9c5oaDzmhEeVA>JM*S#SC?5Y zT}ZHLH=Uf5br1q&fHW9i#5m?DC-1=0!akVKd^q0T>g)%+%_0nNDt|N$lhWRdo^Im- zbF7*KORG;wC8<0lba;}jrq=ARjVzCtilo5 z3QOIf^=p#}^-akuT?Gjp*R76E)b>w)~9UB0h0g}e9S z;hH(KiHxgPVs4bmF5{cPww6ti>fXlFXKNF&gTK==zJi{_Cg|y|LYOSA-nYkQn2hQ4 zEph%e_4Ag6=5Br*FOBkQY88JErPM2w%(v+h@l}I;OKX0vm8hFm>c`hwHuUz2SR#Go zlf?r03u|u*hJ?^Z%I8Wre`j8K*;S?ayGH=`2C)V&kKz?Fbq@0 zusK+P1}NPqb=Mgwl-I0V7ZR**)Hy-+_H4j$v*QTy~d5)dd{Ev)=K+k}>3Y1|bNtDrL;&vB{A7SS zH8{NCUZZ?+ZrBx8CGYUd!!+u*y?C(VcMw6lE6fi|EtVkqdrtGwt72jQsVJfP3AmYs zS$|88B`$hhujE*C@A&l(QQ!Y1pVAkgQ{9R11hd`0!Mo>clS_ZLnx0497X}FQ>O7i- zH4oxwV7EZ~6)Ll_SpBg%n9GCeG`+AzwDFfZO&s9>>SWZ1fYjjXuLm-$9@UTR2rGB$ zjeB`s7u29OF0~#uRy~ zWjzxNSp*JvT}Oi^cwKwvtjxnJmNy_47ztf>BvF(jcJs#SYTZhs(%$&;HA0uMWWl96 z5_lrlgH+_9>A8I)a|f2Rc!4oUYHgN3( z{;4Kz(ho6Uu<>F{x#&kRx@?aTz|B9(WKG%Lvi8I%$*jERe~U4}!v>IGUJAkVhs|m- zv37rn>u4YlKRQ-YXUtabM=UfFI`l^Mu*0}gIe&O%S55^h8I<6I7-59)A{zL`!rTTL zxYi9*VLucDhMJ|C0Ss9N`l%o0G>y^^63k1mcYG800=o{20fP;d)`f=FrL2ccjBk@D z&M_Dr`5U%#6L?L&Kgqmi{1+H^tg4Y03CchoEkL0i#`0@dR8puVz>nZc z{O0GcG?}n)2yDl$=2oxf0w3|DYmct^KGg~^j`T~z%z~)nzbjveY)K})eG@{k51(#@ z;5|PGz7t$*tpx`=e>uqhza^Fanua{5v*TfKu^avkcH!GvcBKE)K!AUTUAP=;8N8|N zw-Zzmb6bSTB)s13kS)7?BVuiCvoMj8EiVDQ@{$qJ&U6DTa#am#)NzfX_$0{@ATF*R zRBN1F978=2wCRQ9Ln_6Z38b8!NH~UqF_Ra^QfgJ%tg2701i@0~_NIz~@J;#L`y#KV zwM=Qlb;X@G*0t+bQMI1keI5rCqVf2Rq}tV|=PP1>Y%BpxKw^lw%^%d8)YcvpOzB#e ziE5+S81dHD_lb*5k?=2SviSKhv}S~2=1Fzn6^^~PvuD`sU=s^D+eqjc#<8?fqc&yZ z+ctk=SGQlQwHJkExUK#leNz;0e0?7=v`X zS@hV9iY#7^+eiA81t{H3q2s-;dgp!dGZ z?RTsG`&I_5G2y*)Y`!VVvKWmC!&B`wMF{?gJ-}aftT@sJ&HFbx`~)~KFh@9WfW`RmS8|J z*MC;TW~uO&xA&oC zkz&p!h`QOW0wuND{#-s;o`nzrtlYPqq8JozZ7SY!PaenMdWVtk^9fAx#_q4jC6&7omaQ2h z-wS~S?IZF)Pf_aN>6|ewpK~{}2TEBmzW-e)aXuwevnCp_>IFI02{_T$VOP)(H7F{y z&rI0Gb;tZ8aB~{XuAxvx@(NnrASRywj za-9s|DOD4APk1`YzntQZsE{RZjBIBEZ>fsF16-b-fCL_f7!FUcV7M#tpN7uZcwz5e ze?z|E_bUUg{H@;{dRas)6j!d9d?Ipid}VpV=zzc5(QQ=kx#gwdT&`HS$zbG#Fm|I6}QKPjJwuF zFL7W#iPhCD6%kN?Gdc2Gf2i1?cyw;P(lQ!}TE_d;+T41zbvzoej`k~$$kn}tqGP~f zu@4jr_Ff?Mx^J~MZc`m&kCZvRVP#wgw*~`>@eYHIqp1JW$^|w|36aNGO+HjXeb_@+ zThl#RM;}OeUv0_gn@gX<6xC5#Px=-M77sB%s zn`+f@QNss`Jgopk4DT-%P(9aMl-!!GC4C;{-rZx@Cx32a15c@)$OE6D6V>I`k``5Y zaqVVOP6LB}*RJXo_Cq|Vd0)> z=T#`^2k7&PS-Hml0R2O}^SqK1TT`c&_8D1$Mn|!x5|^&Jnq+!6@4)3C?^nJDJ0WVi zGj}~m;436{@TnbB z_OnQM`t`r6yD&Hh38R1Q$DiIfm6dF0N~ds1f0-w|F@SyyM|aIiRg3CfVmy;XeZTSk za*0~hpu|gsE-8 z8=rbsK)sZld?~8z<__6p?6P^fxXLRSKu=wDfx01%w2($!M~d4pg}+B6TFd9}Nlsb| zq(d*`CjB&9U9`-x?KsdD(x&}c|3B=3u%Jr*YW+Dk8h!{nAZdmPf)m0&4O~1_gqndT z)?Vf%9KH!!Jl$*fOB2%BFC*|GSG+dr^Qv{fqY=BNu->a(*RqtMUg;ax8(M^F)a#|c zVpH6C9|`J(`Q@bvGoje|dhEVaKvz>P$D)!gIHgSgi*#+x32soh*c748SzT0 z^4H&2z?G4iQ7z(;`t|-R_o-6G*=;ru4NXr^)+`>zW<-rB!A509DHno^e|y6#YH6n? zr)>k>GobZanYL0gD9h6jD7tYG_isrI3WY(U{_h9>U!POSpBfj^RYUIE##l~?AXU>@ zCCf6a&3cL+Qe~W{CD0Q}i(x`%B{7)7_qcqf?zYN5@T55Xc|I%j>*3PP(N4wE=E(95 z=cs%ElXa&`(@vF9;VOvD%1*h=9(rDYM{5}}Ip9;LvLP^2<6m}?K@vlUl!bijP3vVl zxsuKA7ewK(Rbusp?h0c|5HC?xxEQVUyn1S4`pynU+MGo$9vOt&=Uw**D}_;ebQfJ5 zVYGSjEu9qZ+-(nKb>DmrjdhTsmOffhy+CBvDG zCW{b3Saye*maGd8i5A+BYx7HvmOCaeZxyZOiuj*q8@o-*ky*9;fk4UoXww7C4Mmod zDpx(H$9MAwN~>y~)O%m3NhTJ5C7(5q>}%6|FI+BuZD^koI5bvfVS;i>iBjnN&$uqZ?L#Bq%H3mSG3bQxN;R{csd-VG5RNFOL(ylUNnnqRK~>Xqa80-1Jxwl!!5UVTRX|1RBZE>RbdUtoFifffi=$ z4GW-f9_rvE1Vxf(%#(9u1<$s*0V-@C;ycEwcm48+|3V2oc9sRBO^jc|p$kOEVVLtt2Mj_8G7Ym4vL zpxY8#ushaMmO9HegT>*TW?F`*EUW>G4ET91F;_N?2muNCK~YJ|vre-A-Sn7Edt=En zC6&bI!v#l~rxVrbdxR12-?|~Vps1F;wrr(@k!3L(8~13Ftu=k;Z_xCwiP7u6AgDeO z#`Tq7s#p2NZq~JjjwDHjvsL0UFr8N5md*x5uBNAC-?{RCnG5~NB1Rprm0Rof#e}cj z`Bl*~`=y^|W2ZbTvn2KO+*1(=?i!7w`?!2XRJdB{jvF#MxWVaFj(3}N+!w!0J1RaZ zccE~2BI3v|x(&b|Wt>V>w@3O%MjU0m5I{V|{bz^JftmHaM&m@EG3L)QLM~oLNjmEh ze|5U^9(3SlCT|-2@S&wm#>qqt+Q9>;kCl@VIU&xFZvypgeg zE4PBT0*~%I`;t?l%NrO_E6~@x z`&rcc$6Z{iEd?J8!%Me{qLkO};^;+6*T7bZ(%oC|hdv^3zyzK39lVrVugXL)S;sC3 zO%oAPEWf8p#7HJQQF4BK5Y2i8VC#HjFh)w?u+TmW?Pv z^h#@d%0IEv7Y@a%U`NYj021 zUI15eNh)r#Y4k~MzQHdVhk7N1uYGvj3|oBLhwC3{p` zz4xVHi%_(ga74mu>Df~8O~lan+02*M)_3%ejSW%su&zuWOW1fW_OZ@&<#Ky*?aBnL zZlqxNk-Z8 zbDVYDgyx1Z8)E?$jPy?dHa=I_(Qs2Q`JVg7iKPhTb~DD?E?d;*e~&gJFKospt!e&G z)KCbi6#WLz%zPgM6j6;fljg~(5Ac7H|hc!tMO*m#mebNz!SBWKsf&o1OThF-7# zD)>5ABM{>G0u{F&G5>pE`p7K&KuNX4ZS0oF9kAV3^fD26V4wxL)=r<(8*r)Rny{by zoA1bvjbE*Rsz|Z`UigL3Oo%l|M}XA$zAQe^RSDixprt(xDLzZFC#h4RdEM?xxq_{s zJp(B}OL6qoO^4_7yUJx9^ekgxs_T(&su0Cp&603)=Nr{wx<>jSXA(GOMx|M|Yy8Gd z7osS)=yy*@VD&ed=^J(xebL-Rv(Xnr= zfe(cAMe^l%M*Z$8xst5~_KmwLJg?hTQFNE3^x>~c|I#(qZsW)*CT3=8&oT+^$*xDf zE)Dm96J}PK^ou5>^e8JVx)CR7Ib3ty$Jq8{UnFT_$i&nlkTeF0czspLnS zR-7pSrqaHuzwXMGulD+XGXxz=xrH*93L1JdR<&nO9B$ntm&Tr?!&S*HpOE2*_xU}t zl^0eAAffgBR)PUY(W>^IN9@HJJbs(S=Ltw)p~~wttA5Bs+21%jX%dyZ#_?rMp(E9o za4PNbf19xSxKWu^oQHu3yN{e9Je<3`p=jVd^yaz}kkBh>bIuCLvzJ}}Tx^!u2ZYm_7}{W?tTTB`?u59r-F;zL8s9!Z#w^3RM-ScVQSZ4t@qrH z_5ni0X3orIxQ74QRel-It1;zNWaOa#U_@d`SG+awIgnCkveT057QI3!U}VSf8Ul2jZ>wNIbfm86lMST5mAB3 zWLKGPhNC0KSw~e;O@PK(Izu!#4O1rxD^env$6(AT>z0|SN4ED>cy3-Z0FIemqc#j= zvxeEcR-IF&)sM?&^)o%{ZLGeQLb|=Bu$JCD!l`Xg|mzY9*jdjaXLP;YPFR7&D;bA#~*L&1<)eXq;`PAD`n zb@rCc)syu5s^m&Vg+;$_QZ-|9p54owuQ|k4mapkt)4U{S)^2CXE2h$&6>I3Wa|PeA`&H|*Ns4@5(T zT;rkQgGHOB(61``*=8&3aP~w#NF%9BSH?>$+ zVl*qdyA`=_99QGMea&Q$uVc(iungnYNtg-pKK=D?JY5K_kM$}1_S@w( zwECxiP_v+;bK0WF1t*3&(<;8-JB4)k7=e6b^Y7+nmsVs2Y+Dj+_3oD+0;%BSXuE$( z*~N~rPLI?>8WyCfADIr3*e5~Vdof>pY-N#Uk<`X-J0I=(c?FyH5CnYT^I{T8;9p=X z_3EAsyTrJqdzS77sh2F`Zj}x`Abl^*(+Ay4lPZT`P#V6kP4RrTmqgtIg_T7GwK*KB2- zJGt(<%5l@$-N_R=NVIO?w5BfUzA)d!ou+v1sZWiOCi~8i$|8QMX}cG zy0g7tF|R(?ct0s6iP&35DxITS*Vr`&n6`*?^gZ-N1vt{jooYLQ%zwtBC?Xt=N~lgu z&61JtdkPiVgPGO}>jw1j$ZNo>ogx_)nVp?TNiSwSt4W*_iqlk*e?~pI(MBfwx+Gmz zZDLKbH@O*CXvY{Ni5%!H@dJ2st8615Lxa62S0bS)@au@Q%ucfa{h1jG4{dC zawK~%HREeYbV7bT-`ft{xjP1dAw)@LWiheZ^D(ugb7#)sDwuOMB@(zZpT)vX0*)N!RhX6}pBmvJ-^gg+#=!8E#mW+0$3j;m2w8 zS$3c}fBjcif0ZD5@zVnwOQ(cush^5lBVQ^-99W@usJRWcSOn(tpFEaiC)7U3<|rhB z`gq2WZP&jnm`z`|W)d%YH{c^)88}tDhY{NAKEEV($P=K(fD0h!PG~FA9$4 z@!kvlZjYGaDv$RJ@_XEW;qrY->>a$(<)(PwkHx=_HMm{nzN^6GT!N1TnM(PynA0jf zqy&($U0RxYI7!cD_qCLr$&NM?iImTzcBw+Xj@!6#&K2=_4QR>BRZ13uS_msOO??q7 z`d7$5n#vGT9qyI_qx92kjlxDGZ>-P=#o0=QkUNc1N>1{2DM;YT_P}=W@sEJ!sIXOmL3qKn+zLkS$mk=Y#Oy^?T!`zy&$>7W)C7U z;aYz)b%g93_E&rT`@c`h(wWpbR*&B5?FjcQEP-T zxu@K(m2F$!hK+N&74AhIxLVe;HRx}602@HsX2%0K`WoVbu9L)Tal|i)xHfUufLh8n ziR$`Ezfg(eYjNH~c@%V}+zUcCXF!}ptU5~EmxPCreM)lpp!CaF2j1J_A~w-SruB=i zpYRv+5Ob8_-kspu%)+jjEw~kgYLZ!JwOW;c{p&1c_!fSAoTbHjFKfE1{%-bWL2pF3 zKkH!PQf9UQ(t`6DIc{v&qv^>@ObK$rkH|_s!SV{H$eYS$qbYMs7H{MuD_&LL1m3VS zg%wg|sl}@6EK1Gyu%^2vl%$s`%%OBF9!iA{RgG%l5)8XW{ay6yS)G|vYcK{bswGl} zUnXfw3U`lIhV5jHRII+lI_e{U0u_bH3#{|3 z(OXHvVE&tycuj}StUG;ZxH>Eargrs)yMM8EvaZ)`stDZ7IwmULN!DiXyjTyvbewB8 zCTyE_%4tdZUe;xBZx3to_Ov-^6X^R~qpTw=cl{1I2tV`DYEBGmEh`vw{=x42i2t9; zwmyAJ;OUyQ5{ahD15twNEiCed5on+l<_MraBuNZ!(S|n zx&k=6@nx$-=^xo93X*l)=uk@LQo7d!a5uxc2Tak)|&!=o5`#rtj$4c zuGU50!|K~KfxByx&$T?n>h+U5$QM`#SvN~jCMZbX#p)lPYZ;XH%NbpH(*@hbPO*x8 z^WlZcT_asmx4i54bk<2$acrx1&M&ORtP3q$8h2%L8=YVkM{aR#7O)nvMjF2vl!ddt zV9Cn{N4^I<+vzW>ci`=ORJZc+7ZIm%c0p-30B@jw&>mdL666Y*->QiyePy|L4;K@= z^mqcpyvi-sWUI|Pm_*E%KW_f%Y8mZsLd@!&wZ9y`V44E%osH|r-dSX;ZvIZCJ^h$O zf7|V77;1d>WFVZLZVD!%;kZ`S=mD*{8fP-7Xf=2^{1lbU(b+%_`hCxk;0Y$mq`e@^x-HGww*rj-X z`>ML%@$7~1iMn_q1PhEVo*+l>(L;FhZOW*abA*H@%SC{^ogsXj!7sLM-Hs2TQ56=F zhC=N6WyRFKk7O0m$4`B|jR8y}v@38{dtLbvuC6AgjPJy*KYa?1vo$%4>v$rcGZC-| zo8$Jzz&Ko-Vuztfs#0qUpBrd6D{T287M(HeNSGlY4(gY!FQ%9x)pWoWIZ=lCr~S@-^a9k=OL z+O;6@IkLFkdG1XEgY%fIuNJP~%_^NfdyBD-@pfg^c520Eod^_iTRAyaf2i~1ktEwZ z{w~8;zkVf4ToV*q`~*8u`B5jIU)71+L>;HqAXg;oJsci9-5|X}Au_nxyOLh3lp?br zvr+XLtehn|+RiQJFC;rX+r!9d?6bh#JE;L;a;9)~8u9a?R+R3U$pAGj=jTjvcjSy> zn8bRR^-O9E#k6e0mptYZq5Zx+Fk>%c`MO3ipl@f~EY<&|CDMi|YapIh5c?{s+;Y># z-W3B3FgurlU5(IlZ{Ee$3My|VJu_e9z%qq~ey<6kX6;;3izyP~Hk&lfJ6VY;4Vb-} z^*4MmgFlM%Nf7=dKY1^Ok3+ei4?p)WFqSI{FWRt-UIzu9Ouj}S-?ATG79YeFlg~1$ z%T~cM(8t`uge*^L&T5z&n7w1QO#`*y0&@-Xx_p;<*F)wQ^P*;F!H!4FNwn34+Irt7 z?jPctsUYeUZInqYaOtC~Xfw1;&c^h*%Zv?-USrCbyvUf!_{TM#m;-=Q%w5baj?3yR zb<7p!^dP<7XtfKd-fGhvEPC00>kKcR90vj1cIt_bL!^$Ns!-Hp|IfBDvdWqO$4!jZ z5WN(E#-O4WTuZSsy(`Sy7F0KkIK_%vfQkOki?|$>&LqmQ;VLf-g2H*ca32_!mTqup zbyAX#CY|>GQ3bMFr~XeQ`af0ruSoRokUkxeK^P#!s7Tml&8}zi^Uu3d_hVvQB+NjC zqa>Zh%s{9s))HfiNtk6)*PD5H-@4fQu`wz#vA;rr$N(=p>w<{-koXf8;n|Es1nHZ< z_jim>rG3&?OGIkT@G`bie^49RfyL8i96Z;#_D_3(;maC5J^47Mz8513b$j3%SQos_y%^js}n_Bg%p-yve7q86qveXm1#2FGtRH71_nG!km2~OO-o_qV`k>dY#0-VD7P}+fTOhkde z$JMYP|2P>NaVCrMvvAcv7s>kI*Ko0s!3kta zsK$n~pZsCVdjh_a-rH%nqR@3LeBjs5F&&OGa#sKDyocR|yKi4-Z>m|fGAp$4r)q#SRqR2!O|y~Byn(DEnH~Cp{r5RSTu8s zS*Hil;p~S5RAzW;;(Z}>KRqdAH+!)lIVm(>s+KZYnPR?7#1ZjKu!CDRUPS1qn%GKc zp^Pj;aigFcJ|i{PIW$&Lxn;|>>$%vZeDxCuU< z_h-^as0cJV9Q6qPDW)uwx0U_4^iBV)cRPJ;ryDTcNAG+me5WdI!b};c7q1a142?do zyb)M%wUCf2ti^`Kka}7bpiy{?y|pu?;b0QZmN7qDq3yDFC1=ne&yz~R(UC-Mid2>z z)pxgC8H|$-3Ip%IvCUKHG5_6e0>al-@bQ}?!&V%P4&maLC#V5;X0IF7*rwf8Q>xkB zSmHtZ`R!ihjFS0!!T^vl_UxZC;$Ljzp5uvt7gvFX{(xLK;1luV6x5a=IALW^+@kQg zWy|NJz;Pqqc2ZrziVE3`i=_EC}`@@*VO>#M;V7Hn)HZOyR1ru}vg~6fLDGWFtGe|&BFi8q06D$PY%ply? zjKNg)^G4iwO+*XJVqS6@uT3Zo#-223fHyr(!P2{UQvg}%$=9De0b8aYzs&jaZo%Dd zRX>8i$L*Zk0GD{+z`+9}k`>?}ILP9W9|Vyt8m1eYx*8h0ni{(sHd)lyR;5^jcB*8` zkJn4imYll|<|?UpPO->IyK-+|ek+-K?Rw?zi{m;FJ?)koBb|Gd4dKAWQP9;dbHBV) zaLET=1N?$*5bTj3Glb4jfRaq3f}J>Y7UxW8AYUdgis-9m|*4=ePm z24sA@PYlbu-J^GwWJyFCKF0*hVVmFm2GqqfXOvQrn$I;|cULWocBN=>^ zSm5`3r>SgDxeOzH2BYD@u&6LQuiMRQrLK6s;f=OZm{qAYbfwlf?;lqO3N;SW2GYj1 za`k+-QO(8H$sVNQ{(r+C#~QS@kv2s)2|IhpXm-~#xF35KU!dY%{+?ly08R}N{r+bXHJImu_ zu@%1d_~6usje*z?eBqkuQ%_kXV1I`r;o(Q{%WrK?HOj{1N4Una17#6EZEw#o+gIs# zrAIGvxjjp1l^NxX)2W=vW|q?Q-gWj(9#a?=cBD)P;vMd@0o>1Tm)E9|QmM;dy$w)6 zVFhE&>R+S6y-f0&k86nQD~o!Az)JTyT@_N@WFoNNDIW5x58M*RjEPmv!Nxcq7+#F=+? z<^b#!xVSp*m{`g?ivQO+c;UVstwEMImjW|MRj-SK#rN!c9QJ*-%FVgTHVPa(o?0M) zRW~9q85L0Hz9q{iQ+Soi3{P6F>MU}FNAzy5DX7Y&a=hhbYiffXh2vmUP7*^-4uxrt zB&7E-&3NERI*GuZ1P8VExq1u+8fJc9& zJ}q;3W1>~3_N@oGeharnVcbO1sqPqayZ%+>e{98w?6dPcZ{$3fj2AjtttPD@_Tl%% z%#IPtvqO+g#3U#$p4DwR%;ZrN=KtkeZ*-cvKugQ{q)R~j*YQ^-V8tiUl|C$xosVH0 z?!}OIk?4#nZT6ljAUj!nZx+NrlF9!tp{xVolTqUP?1B410UdeXjdx9;S+wZc#^=h* zC}I@}yYQW^L_+&o@2;raHo&SIr9(7f*4+9u7Y+ z2(fkM@R1u-wi(yz;MEcC<6aTTpD)=qGfB;_3w`-3mPop?5$u1WpB&s5HT_xyX1iE; zmj$CY?mU{7D((6DP6khTMJ#^DRRUIxe?LQ#+0{|fiVpJPkZ7%#N?}o`)+9L!CkTi` z1=_yd`uzd5)W1U}U!=!hCzCH77RuyOiA=$T|42>w3ry|{NmT3?83AZhD(9~F4ue%v z&;xIjc2AK0pNU~+9(wTNy*(kGkk32oFP^F5Zi6@ohzQtb?bV$i`ehsHj=O3w*MnM& z_p;{WNhV`n7U|KV%Dpw1gv)Vc2%xo+FG!MW-xHGX2ZN;}+a!%bp4Z!z1E6r;#eRI1 z^Fowj2J*3yD{yU>N$7Y-*@c;C7;raUhArAC7b17J0UyPFc$G}~#~P*MCD{=g(NRu( z{F6gh=>LXN?nWW!qcHaMSyMY{F%C+DmU^mk^|&6Dr9a~S=`l98n!s!kb$J(lds8Ak z=4fdY7rA~&;Z3aTuPumTDDR7$|FNkZ(Hc3&Z=o+*O!F$X35W>n+mH}U6auR#oYKkZ z1S=V+T}kzvnG%h)Q?Of1(TN4OY#gM%c4jULi^HJ*+5$QcOAbfj?F4F2l3Yd39bh$P zB>ef_Q4C)`_#2I3VWKCz@h*i0J9!uS@YRmmOgeC#)k(cL_w&<7r^{%-boU6;CREJ6zxNRyUAuP8;VHyk%VLLh+^d z=I_roC%3L#ce?;}$=xsN>7?STiP2jq`T$r+G4DVVvx12#Cdq5582gz|2j&pSITCH{ zBJrj7vM}YAXlyl}Rwn8>eJXr!-3m+7Q_tQc<^8+O)?Y`>uyz717sP%_u^G3Paf5>k z3COuWqx$H1&+_#(y>Roe`^N{-Mml2FKK8?zT4#o zq##~!&^SpZT|Zjli~gb?`V%kN>d*fQ=KtPT9W6g(i?yClj+Py9rQgcfyUSD?Nza#p z``uJl!^>;*%xec2!=tzAnjNf#OFMS{UKRqdIswG$XjIN9Xf4*qid*# z7v@M|8#fEQvK3>j2&Mvf4YftPve_~#?|!t9Dy+a^sOZ3=USDT6Dw-B7BP1-g0tZv! zN(jrqHGw#ba?ofs_b%mD5@=}f=kAB-rJ@+BN(pxn%OMTBT5$?aOT6JSdNDd`_ZtTz zuo0$88ID1{d}!ZT4CLODFuql}@%poHLuU?3uF z&!$%#3fq2c~GDW3Yul$@lOyyT81>@nu77r2Lf>PMquxFm*Tj6qZNhi6E0UiORQQm*Wp0Pt-giq1X#|{Y;_? zgSyJD599OHe()ImYa1Q_ov)!qYj57x&`RHv>+^E>*?ynu?A#XC4cRsiU3gyLY*-)C zdo8qqwQ*9+C^MEe1@vAEZDMV>3UE4cq8wNuJqgOe@M`Qtl${}&PmXr&?mxI11h6v+ z`;e!SHKh)H{V%3UM6?*-tYhi&^QUe+T7+;_)~~R4ZoF16^r}*UJ233~Kg3dHtZPGs|!cW5@$Zxz&?C}U%h1*?r!${iOF1afrR{;^|A zfaM+{726ssKZ|gn`$~PKRX5TF+vXz^i^~s2)IbwaHY5W_MY%SN99+#1e#o4o%-|(F z%Ah=V;KALnIIrpi-Syu`WYkYRyBfrdbXm2(CdAylf+LpEwqxJ}b1RjamT;S8VnDCWtx&6*gJH{5pZ=Akp?H^kI5eRXbk)b@BjFJM z9_$_tmt)h*OSDUf2o(OC1XTC#e%f`6baY{1`05hVNnk`SL+L^)VLXi-yK$MDh&Gk2 zK$tKAl57oHsuEyv&%@>x@19Jc{Y1Pb^4xF>g(iJ1Rq8 z(X%{`K%hc?`p<&EoDsLO%ml8Yi#wAA2`0#B_<|P2?s)9mO9?E>1!6v`N$M84%*Jq6 z3Lh2I`L8ut+C38a{JRkiJ4(Rv2t@Tjtp&IF z`mlK$Tauf74)>Ta04~l_&zp~!zp+h3^Vr-&t$sw*>R>Ej?Q6bJQn-zBix@wau9psZ?yqJP4*k@YB%#*_$M{EIWaQD$2_-9$5#HX4|xnJ?yi~D`buM> zm&93?HDC2IX~5bDE$gRE@3V~=HI25?okm?}lh!h*6B?gIdr>X*OJ*|kq7a7hW@?Fp&}J9V+IWB*@uuP4@X z;ES`df2qo}J~#U|t=K}2l)sw7rpsRC1DhEaSL47HJ&8omn?p$!&@HfLvQNn-d%98%GbOzr8N#(^A-uV zCY{W{Rkf$Pwr#2EpGf2vBodlT!uSQ>()M$+39+@L2zrNes`|fS@Gp3AJzj#13^_uw`uVX{U{3b>UVO;gX5bbEUNnXdUlZDB4wTpS@7p@i8?W6 z7LZ_BhkB)rJAR*bJO}wom1{ee{%zz4b(vKhJf;Ydt(T=*H!x>M4z+#(1~W1~eQ-F1 zYXmTP4DT2I`IxHVNwz%5_;iUOwYvB%xDK1SCL~Ys>is8yKfB&mkXT^^;+X{--bihY z{@%T8Hbt{g9}T7b`%I0Hv2;dxpjs1{ZI8)) zX=izYU`d;_1>OlMxS^8Wha=`ao3C#ABB>1W_@It6o319i7moZH%ceScw`9}W)R_G> z3Evmz>5AV@An%V!T|2!Z1)R%B-}_)REztlHETqF%!jwi#-p9JjCO;gOytQC0kwD~Q zrkvMEA|*PZ9@{10ArM!JWy(^=rv^OoJ&*^`^@=^9>D#jS$0BMPR^Der-XoYlCr zz*2sGoyDkZN-R9$%;ALKSVIUD+^8WPFE3&w(JF!LK22)Nb1WMeptod0POxw1+#WyBVzH3=rYldVodo9Ve_XZ_tDyKC-fLg76=37{JQNf{o$4t?~%FA zI(eZQEOAd!!va_3phit>6vU@uk`xszEH0+j5F3TDNA{a>$zzMz> zIhsgon4L#0x-{0^_xp&k$;l&)kKTWB$}${8UZEw?Vg@zb_oc9LwLilpnT!-R+9_4j zTbPAt3y-fzs62rfyspJWxwxReei#N9uEj-yZrx5BVQGAQw548y8Q>7t4GEQmnm`)o zkyBb#zm{AgX!CfT1@cmgohMpP*bQ+i3UuqR;?naTpc219i|AmWjNMPZ$=AOu> zvWFOy3i33WxDj0-l{kEz&}5{pQik6&XqRz z#Dpozl;C-c#W~>@=?pU`#Rgy&q@O)puE9pbl3~}ITLL4`fk689p) z5wg+y>*494K6D;e&Y*2H6!)3M6&7#2x?Yksbj3V#xf-Q34q zC^1#ra&E7(L#ht8g^l9{?Y!EXFBCmpTD7q&-|NqV{%?4owA3(@5Z|rT9R7)?#v_53 zq2d4^N{#1*nZp*Ch27zZ%9jagB~g|W+7k{1B|N)UD@sIyq(i(u^bRp|a3KN{s+o5lAVDcp#RX-hJ-EfwD|n+kEg>uslyab}^o)qKwAF61 zlCU$fbd&Dk=uSZSPJk7TTx18&FB6D_z*C|p@_yy%2?*fj3I)s7vxCo#z*y3EJUuSn zL0Z5&QFP0?RlnAbW#1tYasq#>vX^TPaD*`Mq4b1UTLcja{46ea3?|99{}IqsknXUt z_7nOs>+Qm;@Y)!B8mN60tBHEfCdh$h~k;rSFx?wrVK)eHd44w^~Fx(H( z!OI?}#J;29?e)IP@$qks^=%aDyI9I889iYEuVjqisTG1OAt*tq2uNNW zk7QYB81OS?+>Gj|*zS9rVypGDwyRG&|G&i^9PbwrRyYP6c*-75ccF^B?poU9o9@OA zBaAT$b3wMS`zHZAuH$#T#u7)>o`?dmst$;FYGSv8#M?9GANB8vu+oP-BVldHPq)sN z?w%9*^D$+K69n&%L_I%%)c7z^F8W3c$_jE{A%KhV4HvjdfXJuIv`o_bREur`)d~S6 zJ7V3d5$i^D6ZD?O{j zM*5v7in4VXo|AwDYWjVNvM7hE;2Nt^xxabVpSa0P6Kd!{q-}C@fAJH(`RtR^WRx(* zFP%b=MGl}c>Fex)#+a>9@Z&KWWhg5ZpF9aaRzr_GJ>8GL@d7TORS5^uoF&D%ofr2l zj8O(7C5=yoWt<5ORk3{^L6Y6EHELw|B}Fn$1a+InNgh=~IHg@86gz*>@P7P~c#QsBep{dPl`^iB05A;vokXp zqHRu_)MQoFYMAp?8|;_Zl73K-Ju;Hkhuiz$_hk4PPYFeAS;6b zRMij2j28$*Ra?L+b};_wB$dlJ5nb5u4-6eW$L>nS_4@L7_vcr&`{QQnFDrYmG^@f) zeHt7$TYo0)(bUXx6TAAz7tNdxfDX;eY*KkDti?0_va9Ysl|=Cn_yHdRtj!;>feiut zS6=^>=8k?**kQpCghE^|4jwu<9+Zp3dx2de!LIrCr!v9-`}$!#d9|o)-NP0DNBUOS zToJK)@5Y*SWJQ=|f&@H8gzE?qWBm$bVOTNIK>qDFzqAx4lvZ4`GF*68-Nsew z<0ExIPmjF?wvh+!48p&|9%#U9e?E(&S3JIME}w8bA-3?H;)$kYAmT_uu0vB9lf~%m zm-sC&vLe-rixdWYEn|W)%Bh~B6SzhFVIMbR?H-tMn%?aisIBr`m{$8l`? zK0mcrL2aRawau!_t&(f1Y|+j&3E{*OC{E1Shs7T1e5Ic-?tm@w_}UgXUWaSlQWuS# zc}1dX4!IafjZHn=Ykcx&m##;xwsiDpt4;grwXLdJpg6X?m>4kmnI-P*^P|$s1#qqc zHJNUCl6TW3QDiZ89n@1DIj^R*Jg0u`H#0r?W^%U5+Z7gs z9gf@_1)M`G+B9{H45h-}c(yt!#wo3Le-m4)LiYXPuM(Ks^r!^6GsJx9bSU;TJxA9h z$utJsb-)BMj>a8voC+Ri@8@-zL|#Yr36T)6x!P0SzKXV@6zZzKIS}mqtL}DSmH8zc zZEy(HNi&B-abC41BNmH=3S+?HbLw>>Vce9=&_%zOR=_!KfyAt^Skq3DmZ;0w)vAhq z&GSt66QR||cwugdN4S$?J%)!`?Kwb}$yeX$3mG&^kSzwziOy99D9^H6cPE|NJC64U z&r2A#57^p>JRp2|ZT?ra(cJmeccF6}j~8qH3!W-VvZM0UU!y!8sDa83ep04#;!Cs1 zw}#E%+@LnBUi4&sKv$dXQj8FrByGM(QoE|C$aW`MdV3oL5-SW5lCgo>wTX82F z$4F}gvy39Fu2?|u;@4V#o8F$RC9W9=YgNx|V1 zvDXCm!!`1KJftMkERL87mRRBft$3G8S?N%%gbpuBvAMzeSxRy}7->@M zb3T-0+K$xA5k0`vNl1*uqE4{JpqUk!s?l0&wnw7O4NewHv?C2dVFzGM`TJDlSL1*Y zUcdjVz`Ba|N5t{F*;S|MvAenP7?UBoI)#CE#oc{hQkgsV(XQ0ptm-p#{GK!v#$tH1 zHasOMW$~dTSYcW&7PiXFCZ6KnL_J$*P?(|j3Wz&EoWc`1)bje5fc#(PnX=F2`mQL9 z$Z{c0Q~E2Er;UWe{+P1%i)ftyQSqdKrS{0-Qk^c#;*rEb6!`)kUpS{mljm;4H@_!{v?{>VS__6hVmU}=ko|hHCv!G7%P&(kgx8F&Nz`a& z$9VK5{gdtGsXbMLiY2WpUtOeQ(de3K&xB?+=eyh`iM)xF-My~MIOXBbQRoh`_5dQx%bDkaCSkT(RU#%6LgMMVW4=$L7=;JJW@1jZJ|{<3)d zEB^qQFrlDMOwOnw2QbjT&{horJ`PZa6mJsYzd|!6wn0YD1_5A?5k`cSELTFIti;^^ zYDxBA2nkuR0L04IxUpWvXA{QQ%(xQIGbH?Y(Z+#!Fd+k!AeMn15cgl`bI6CEabTN3 zfe}6wDlkLeSbYpvi480KkUNJB8@h1wzW*cTXO68ANz&^nA7BKilS{;)26>X&uEVSY zQlS}l#uo)`J$Y#Fp#xb%73TU)sQ&z_MV-3ul=RG*u*FxEkPxgag}#S$xVOb6_Y9Qa zh1Z38=*Y|wXin;Y3s~dRDp~@r)*-i}bui$`vS4X9-v7nu_jI$!$+!4wN=zWcJ6ZNi z$)Z~V6(uSZ;9YFTC2;j02?EqBnE866L#@`Bxc`)CwXZtbJKQ3%d05;b-~$V$L9^Ah zAbS(;xRYrS@h!ANwQ9raSZ+R4BBr%{u7_ZmRv8_ewxM|!Ncu-x0%{co?KQDn2}&$K_>#T5t;^1b5M?CAGgH`wXNq%?wqq zmhc692}V(q2-+nwO*=;`($(IBY|&G^uL&e_A)ibCI?37S4eO*C+D^EPPZ= z2+SivT>}7j4MynBg*YEZr2iJ=JIDw{m#T)kgxborgHuUaUx@|{{SSE{0Al1CO>^-7 z0<9-L@+Xi+esN8XhW->;%I2<`)~!X0K8H-uq04}dPp)b)&|i=R8|Yo^jboudAvZLs zx4-DeK^Gx4wD5A`lErfugJ@qjZt2^j&n=PA|Bw#TaHeU!8Ug&cLo!tE#B^vq&S+Siz_CD96pv#aMx1d2-T4Cf}w_ND4Jf6KPP^1_m zGbfsAi{5T*qvK5VaZQZqAx%lL*%&>Li+t+URKHBm&5jK|7ZPZl$C(8elTgM5O&{WB zc~D(C_Owuo!Dqc4IA8LQM{(67o=}q|o&3!)*3Kz*}o|hB{9%Wlc!eLkt zVJ=)~dm-4YxLB#gPhAuj(>ojBIYRPG>Zw;55<(M5DP9Og`AmluItTQw`;U+uWWf@G z(#Wdm(ba8^mRfgHlJo3>@M}yNo6c6dL|VpAvan_;sRWHr6miI7tN)x+O(hbi+0pFk z>aZ=`9XEo>jTy-i(s(-f#Ca9~FZl3W+g9;py|x2dQowj3^a#tmmkeOFsz5 z?a<{gDHW6v=8OJqbJMfaYu1&(q0->YI7csr7)exc13eee=z^N2^dS(wZY}MnRzhr%7i@tIzC{}ieud)Fx)^Lqrt04=A4LOF<$_q>$)70tPN#PQ? zMan0GslC$VTpC+^403w(v z?+kgZ38M8VjY^Op%DlqIbb`5x&ffwjSTFm@2uLi)$*kk`Kb~Q2WVehI!{Rc%M1xnG zo^rGl?gV)v^RyJie4YC6PY`sz=R zuxLG;l@+IG8GKvxc`K2Ld5geCn*x4I+~%q(`7_Zd=cG4O{DmOu;x^Xs4`V3ftlHXl zz2f3!V=?tUqIE@XU)R;;rrn0Em3y^{$W}EW_>836F_Qwn1#q3e@GQ6l;d3^P1X+>I z_CKf#jwrzPVitZHYkq!Ow-THm(%R`$5>#EVO6gj9DdM&ZFCUmf2`-go`Rx z^v-dg<*|Q%jpGAfiG;Hj5mF)pvx8Gfc^?w>K&-6y>~-ZH*8}$BnI|<3TG6 zdPk2chY#c~w5RGXc#Kcq;>~`WKsfOq&FzvrZ$R!HjGqB6CR!* z)!F>Y&i8e4k9*o~Ur42`8``yYi6c;$ZK1J95MN~zfXx4(&B)zq^29s(=VG!XP>nR> z2ysrf{jx+N z<0{QHX6~n+(Vmh$g1D7Hq^}Nl^v{CBZ~qbg2|1y;D+`BhXb7YtSIV#!8^IzgU3q@Q zNhg-?0F0GKv|> ~(+qoiyv%tmM@aVNiWV00TLe8Av@@ZOd(2SZtHFF|DrRatNrC zH|BuG5)(!UsM#lO9sHfWAbeCu4!phMi^hqbkWH^&A=Mtgw2QsR|K zvRxzcAdZXz_l0ImoQ2~>xdnm}oc4g^)K6WISTLNN1=}kfDppwLfxYl4r1}RepiaRF z<7bi77J;*2iY~5+&XVp5uMV^DYedbPd2LQP)9Y5MGWANPSMJn}eU7LB!8g;tUjVbdZBr&H zDz0lrsj|!+?i22Ye^}gQOy&_r+#S~__s`zmN9vBdAY+!3BU1547BwYgWwXj|G$|Zf zF`O&2wS4%Kd&PsG*jbab;-CYN0{=^^|Ki7hYGJ(5V<;4<8Tq4{lxl5C^7!)Fv^t85lxp+PQ4(WM0-a?KCv zp~ad&+K@i8+2dP%*E2AKn%OWRdnb-G;lSu#IjXQotpC;&1@uk1Z|2+R6#sM&SrTer+5&Sn_rTHMAzL=6+RTyD=wsidQ=r& z0IoFykf|c`9I=gZsErlJu}OaeX)2p2cp68lk?Ms4$8>LtD8^aMRSyg}cvUej5hFMq zVR7>{QYn`jOGzLCA|?2f$gx!JHXA)06His(#^Qsr1}JNS zaQ}jLB0}7Gp%*KfcuAT&yBvBbYt0}liu(tFaB8r2MUs+g7U)jUaalFA02cZSGGjwQ zorJ_VnMb3^1=e%5?RO9ee{iE%gKM&mvW%f>@oo&zH&9tPJXmvvzpOX!05he_ZjerH!%4+89=;YPO z@(&hwt|AZ++~}&4^La;wmO1q99}=e$<|-yp!HLXMB>c9^2bDco!pg8VFAh35U*F70 z2X)Amq<5*jal+`l|Hw*4GfDN|Ek0-= zy5J>A)yyC(|NCkWO1Th{bmCY!vNw5+ESZlQ0aAF|*eg1@$171V~#IUrkM1_H#^v-QySPW`P zE|e=BHdF@*1%VFrk_I27f??}}BiUgtkF7ZI==vv5+K>=^8WhWuH zw%x~-7=}dYZ|V?J?6CMb{BP~r;FzP z#T!H5E$P>jJcsr1(XL!c|E(yK;Dw}U^VzVjY*(W8Vk53ykL>B+wK zEpfTUY>B_bdLY^?d_jaV9SU{uvr&#w@5R(NH4m`j=^b2w@%=6;1Nl+FC;aRzreh@I zqW0Ib%dPEPRsBz5fYy{&PZ$Y1w<`n)H?2SODV zgXJVft9a+*x+ZX@Kyxormy2q|@caF?a$q}fdm3?orRqlhq< zst}-e>(Tqc$JnL(n+Ad?kv*)?r76@2;@a1D2%vDt1EHzdd#D+zSoZ-+DwSV#GESZ4 z%QSqJSU)7`_*!UY{M%1gos`mD(W@?9_7FB64Rkt zsqwSJA`mLfh1z;kDYRbeGHC2z`JoXWs*BFp%M44Rf(mRBbuejl1E0qUY11}z-c7Lq zb>Ffa0ZeF}xaxXzJhF5(gS3Zk3M0KpB@ptYny~bc7IvEP!(yJ>PQ+xdb6uNOpU)DZ zoYd`s#B)X$1L&DICQq3ed0K2LYOr+&w=!dF#REt@NnQZ2Kw{`ZC5M%1}_C zQ~_CuiF{3yNUpeE6Dj;7KL`fPOe|2=y?qg_aQz8Yy+NHn=r|8uGc|5S9H1^=q!-8U zK^WbXC)VDay5+!Rsb1tURlUo-oObyag_BSGEXCiY{&BbPQE8k2f! zcKD_598)rP(eTHotNLUvrrUmZ$E@ju^ti{q+4Q|u7*0kT7q zzuHvJg4g3Hj;sEX*l|!oVQ&mzX}ZtOLvzz*T0K2D_ne)3yKl7rQHKKV=+3>an{!+{ zEl(KrAUpV(A$FXLk%f)X-kTm(Tdzy< zItb(B2745Ux#?w2{T5DKZp=8@qd>~Dp1th({^DTsg3@;N)v&JyjoqMe9q|l9A4By) zj3%?y;t)H$9DDC!Z;20lWQ&y_JuT-oZ}he{=Qzg@jB$e{V=XpQM3~u)MS+!}W~0ew zwKzJoD{oC2TCnOh@y5F?xdP{4I3*;(siW9TmSDJfQfa+2o1t22G_}&jdobSl<~l@e zV{a@>UP>@?CzN!WU{c^Gki639OFjtvS#d^LI~3mi6{hTiFk}5|>i!iN7J+NSV9v?v zQRI=%|NnY$J8MrWMe4*+j>I(_OlGUaBeG$z;}~|L6C-gAyYu5|KV*NoY;C}9P#zj3 zd5qQHp;n{GYVnXq9BiKP zlIOgz>*g1oM0tCM3~rzXoGUIq2-1x8yU7|r`Z2P^YI1zyZe=xu>r5~i8LNjj$8eA@ z0VlhRp&b0)vO~8l|0g^AmmRrhN1t^plpX)bE`McL9=NJ&sa(%)JY_e3%UgF<9%|~8 zFC6wGp`+r*!j4~D)>2@^2I;iTSzaid_#EkSV9~LFGLkn@-mud5^}x0|qmQ=C$#kbi zmppdU&-3cxl-}vr`T(`EE^6tDXzNaG#?*Ax#ZP|m+Y771ABwyzkF-2jASg78>HBYJ z%4AUZDQ^ti<+HqUojOT%2S_Ev$xR6RaZtI(I+%PQyHf3B$rJZo+CQBy1 zu1mP9^}$D|$oao3Yh4HcOZDHm8+3THF6e2O_>T?VvZR4oFYA_O&@)}-ei;6IHJ2KLB+4_B=>^rWuYfS5P=Y9ps#(rwO&(geq>}-d_}}+n69drp?df^k7woo8 zjoCIc4AbNGb-0KVda6(r7Xq7rTVk{lGsni6rSPCe z9KB(F7W6*hMl;nFk)28!*k87ZPi(;YgZm_c^ixdI>O%&~8ygfcp*q>35>)DfhfW;$qZ_TNB~3fAqjLR|MK z&s9iVQ~QZB40#67@SGRCi1#F|IdFZWAV4_dfmcc9Nods?}r$SWe_Eb z{vLqye;jCv?tcI>A?0s(VfAbQ+KbUwxegaOhc~TSSPO4~T62Y18UY zVWK|({~3w+P#lE;==PLvZQNeH_|xAcwCKwP-O(YoA0nn+IF3mUd`$M)_mIEg1pou^ ztRq8~vv$VTv(m4y=zBt?aD~z~=!oT2O@unwgxE(NY9#bm3&RkW&^$}07i-r$REH;A z{XW|24tGcDxuGP79x46V`B4I;ZCVZJrh~3BQGI`C7*#b~H7Cf`Vw}T?cL@p-Je1L}n2t1$o)uv$7FpJRN9r*2w)6U&hUL|5tqi|>Kq{P(P$i3N1@40H4j9>8*Uu3lweex{S>() zBCzA2o<%=IkA_7Q!|+fJpCA(OZx%kS}+ejS-OlA z*mpBRwX}B>5@)YVGE&R>p&FEol8x46B5JbLtS17TrZQpO3uy^ELMMGsSatS^oB#CDes zi8*+5^uzc2Nz^GQkFA#u*Iq0+1opl@R4VSwsOOOZx(0;kr^X5O)_4)1^NFav<%VCw zITwzcpbB+|;?Au^w{jTNW5Bgsn1d|(P=HNTV-&4HL<7EYax1+kd#hC#EV>nCK*U8WqvcLkf4 zr-|viME}Sz-N%=9TSmnOuURD*1-rwRwL(lx|Og;6oAuU1ETn z@1J*@N;D_=8d~^@;_0Yop_Er{Miv4Kb|j(I+(oI^aNo*KNgz`Uyhk&h5pE8Ea(osY z_x~^4@{THO%X>41=UxH4rweD8(|UI;roYYXZcPh2iZ{1ZEolz^@%DJ|GP{lkqc(>n% zhP~1H;%bNy_sO#^R{;%7xYhUTUYU5ji2d4){50Bzq;kVn<3{^C zWb+{8Yn)#zqSY856bj(#-iyYKJ3N0~{l4I{7=UgCCLhdErBjchAidp-1j=ZEckNL3 z7Z9F}$~Y8h_hBEls3O^^bf9n7_M}5Mbn9*$Zg|oP5@zYg;uk5OKT#fdo%47`Jp2CN z*J;rEeOlTa^uHWR!3s=57Td0@5>y&nU(2O{Lcju#ldhikd9Ir23T^7-qBZJ}=0SqT zc+5+w^Et0< z+CL?kX{|k=3U>IT@rf$)Jn6sJI|jx4E`kqR9UIz(wD+byW_97 z6&$c)sg*2S1u&dh?~0@UxLBjaOzrSM7=Ck00XIH>T&9=vjduM$p0ZEi6wAPjl4&en zU)QmNO?2{6aBAB7tKP|IUqbUN@m{R0Kk#-Y-s(>=TmrDAbWgpA<=600oeoVb{n`1I zg|Z!{nw0x=(4A)O>oK8Wc(~z;DK(v-@b+Kcn&fkJ#Zdz?>ccg*&R8c~5* zM=lvRfW*sbIG6U8uUm#n%E8df(J{1NT4}XLv(^l(^k0b;tmXzILIr# zl-foKbrz*O*>8z$X(0iW@nDrU=J#qB(~I(ra5|VO?D1G)8K^?c?ORB-PQN^zVb;p+ zNqvA5&6%XR=HFe5?p0z%*2+HK`60DzHH&%1W5v7{^jB@#bp)WK|992#;u*L8e_1Zc zUI5tg56rv!B*aU9o~KK9-nL)%O#lD_U$drV{p!#-=lKwP{D8)RV8WgPyhy3KfT&24 z*|(B=!Yqm<9!Q;`CyjXfDJATF%TjP!qHj~&QafJ%Pykfrf!GYFN+iwKV$^()x&*ar zLeJD^f;xw5X{qFOkeNw8Blvd+VTIu`R?}h*JGg zJpwKPwt%Am(0K{1%$fumutx#yQdwDdu-%I1j89=tVs0`t5CptiBdUe>&>rY7_Sjya z@3Y9}up?#U0|!9Ws7ccZD&R=YBx1}JVBD>FKi7mXGQksJXD?S=ld4>sqR>+PfDl9c-7nYM<^XiNz= z>ETYd1s^1NNUyU#ARGHT2^#W-ssjy6*cHuu+52_Q@p6zxC zy@p%Gf(8$j7z5GpK(*!2fcm2BFCe%5Sn6!Izh`P%MZmWiVU>dY+G_psQpp+w&JjkCP1s#E}9u0k+_g+h-4^C$^jN6NHy6~!5pWp z!g2u^H(JGBO83A-|DwTg;xz8fQh)E<>LI$(9^Vcl&!Jn1;j{?>68PH{vc(+Zq%D7% zICNic+C4PaB}l~a^^T8GR|)2$Nb2Db)D^19n{e_B?i~19e?}Iv_86vU^dSa&kbH_D z8kt4n`0Di-0mnkPe330zqmObG!Za!K<&>1LU4-1FTu*>40SXl%Q{I^xHJI`2S>sD1 zy|Spy%Ou`kk0OL91IW4K5an=vD-op83QZ;xM%d5e)_2=UULUvtkaWqfRmpRtSb^dk z{UDRFBl+f%8R`&Ffh%fJ*CzE%M>kWjcGU#96>`mvSJmg##pe$fWwoiuj0sgvVopLs zc`894SSIfLxC)J*kkt;NIJFD+nkAa^9k+2sBD0gwUQUkbA3$uR@ff(rQl_G}L+~1}ghV z(Cf3DWb1M1h~vbV>6VdJWP zr@gK+HN&?Ju$6qj5!$IWS!o%CdBx2NC>@_Los@}mMPe+d>@LDz1kUd*;ZDebMB^a^ zQvGnjhE!c7mRBWpxr4;bYQIk|I1ERM6Mr!!R8DvUJFtBtlo_|ZyNcnXCiBhv&fkV1 zRDzZmw2a#9o=Rlpwhu}-T>+%3P&iR_BFZX1!J;j`osHWgNsf(yQ_Ika$iMQHUsu(< zKx+D;a!A4+6XgRQi6}VmDy7r7sWv^=8UR(VLQ}k!#);DG-}6UUmKIeqJE>MDSo>TlE2mnsx5>ZXNT`CTs&nSLmI?sy@N%B0IvDhqU9|p8H?AL@uBn$= zpSuJ+l{Qz$DW%eO*T{WENp(18G?q`-=4`bbtRhTY8w=;3+TNQMs7T(>H9B3c`xV3r zOA^U~za6nA%d$~)go@2PG1^%4NcEFimdws-$sy8y>DzhsBtVO9E zIe7wW%0`@kUNyYEUbtJ3YNV`ez8oHXQ}=P4_VmT7CQhL9)gdg8G6 z9Dl_wNl99!BGSB&b{{cg(W1hAtp|mC_+WQWC`qCFf^$J=QP;nVcE##N9!ZAp)dUQ5n|U& zz6SCTsI$IaYP2b*y87>Z-$a>lod6clKni2 z@eV!6WZcv_86Fb3QH|JSBqF+AzcOylx*5yL`)yE97}sJfGu zw=;!qI!oIKwfj@gGi>P!TCYVmSMO*rE``VEtZc-b%J7CEqui#3szAj(HXbIAiFI8h zH(znI>3XKwf{v`KQyoxzFA;uE9&mNlHS~1%(So+Ng2=flJloL5mNXV9%cXuz9#B=a zG&-Em{oVdT_Fj~98q5;5$~i64i{~W8jvG|x8#90U)ZA@=2@;Rfdd9O)R57rQ^$a`< zaYuxf)qU&>nl;i5@C(eS@!2#FmZuw;IF;-2xUIM|;Cj5HqH<6;nSN@|)ZU%r@nH;R_dbMGaVE~WEI+_e zHAZ6pd-wCT{LN1s72GGxS>QlkbSqsVQnI!V&W2#&9);KY3(vPbn5C=4`ubp$tx)8)u-P;cieJYe1nD(Qnc?e z<0ed+0v`BJ(`(I`HD}&}Me8i}QGN;F5+zBNBDFWAN$YD%EP#daKSmSoIn-_7|h1`?J<7 z)4>il1f8wDyN(I)Hm#SG-cY)~h8xzu8)1}@h;dslS+-&o36y3{o}GbF=Ypw_)@uqu zZCZaoMsh$lKQfIm+F0W{a_q#ZGv^qovjht>3%J~R9CQp`?o9`FHBfx<+mk?o_Is63 z;Di%NG_l0fYlPtz+s!Q#$4oGm9(gg3yZC3OGSw0rb1{#&7$2i!B)uJFjU~>wu$h+C zbI6@lyPGKn?6cn zCSh=_$OkT4c~X!ze(fLM3|`-$IRc47W3V_UcneD_Ya0TQM5X{u%*!w>+c^!e0VIQN z?)gC&r4H%;-&tOOvVz}y``!0H{P~B$}2dl5PPIZBDvm~UQdp@vaa?AKp}tavGUHn+lua%w#T!MmNERyc$fcrW zAR#LK2926xV<0IRTCgm}#KgqJ#KgqJu&XB~!Bb6T)IR%v)W*jzASfiPO+-{oTtd=+ z&Ye&pP$LJ+%BIoS2p0|Iv2)poaN=gAy&KVQcu>Kw*0i2-%3B+?xrc>8#5v z--OT*sgw%Utw{w}?QV!6w5;uZqERdM4Qf=mjJb{AW$+OY1HPPmR!7`={&2iBBkqu! zTk_7WtZdbq@ibMD|4|G#Q+klpPTq^Qhjj#I%X^jAeq}13lljagrn5Tfid!P~0ih8k zFZvtLLL)Oq6424G9eu;uhuba0*9X#+Fjq?ZfDQZ3cW zm1^O8@eLL)g-f_rTa>i@ z`>|iwe*IQ;(wbk?Y-(b25;Pm%{@UQ=8`SM`$a4AqW44#q8=md|LE=SrbkJKRGR@5TGHd%Q3E>8zBFd--rV9+vrfkDt<=G#wGoQhdr5%4Oe~&erJ< z(2ew2_zrsgO61)6y!$VFdAi1iU%q>U_Q6TZ?|J*JdHQ8Xru(h&G0K>o|017YwHVQC600{k$SKQq-p9b?Ax<1L$U+-8E8T`8% zz8%uHB4>h^@7cA|Qw98?mJdPWMOoX-5#d71$*fKsuSk9WdY0>wTt&PZ3sq=-qO6#F zf%nKBb0O~^&k4W($ehZ_?z4d@qfChx2O9MV(fCV(0bg`^ksosd0F41CzyMGH5P%xM z0000Ty#~P=j-!p#H^BqT*-^0-m8C$YJ1oM{`IqKYA^zL^WL5JW@~#X=B4 zL=+vOOLU5EK?M;-lwAI0B>-p)Kmi7T0)PP200sa6s6#ys9;t^1I-D6`DnJ$TbCLj{ zF#rV^015yCPy-kM0H6-_G}L>0gx=`{!b&opM4xh`4fky|$-G zQu}=6_W#gJ%Qx3sp~KQ8Qmps=88P#CfSea~*8C*V$i$|(g)Q6lkRA8r>jz+wo%>)T zJDWK%C1+Y$Tx zyHwf3cH!fFr8W0$uP4QCCcNCA`p*vhWt`8 z5aR?n%pJB>4HK$^(IyXK=e4+yp*~}Pm;GH_X(n_R5fup+nG}TR3wOjW3c z^p;s%lf+_p!G|zA_lMtCPr9akYf8M@pF$T<=@MGBW)#rKCQ#^t;D$}0OnIgO4Vyrr z3xXRqfwGLmQy{ahlY=b2{s{~?O&oz;ls5B0uGf1Atz+@HI9NuEoZ^4JICSA82EOz1 zouBVAiBPHYt>1P+?n7ZfB*%&GW7#3fW@|5m+H*G&^C>B6v<7a>;uexDV+To_V44)U z?fKE%%^~}Kq*U{xrH$2)PL8Q7IMqyNRZ}ok8hcdKPcK*aU5)SPn{o6wFs=71^PAgE zx!GNsc>|_=^I{71Ptgll&!kusu}PX_N>rDM(`V<@yL#XCtXsrBDXZ0N<6Oo))VR<3 z;k3a!t)=3O-LXnGCbt{rbHPuuVpii#*jwMfUPeiFAi~6? z%;G{STgQ_FK!k}&nZ<=vwvJ~=X{trZ9T2r*4Y^5 zfCv+lGK&kTY#mQ701+l8Wfm7w**czl0z{aYlv!LzW$V#p3XdX9K+L#=SUM0hi5zG$ zjv03ve$zG87;~bF)`1WAzl6C2t`DETx52B`?t))Ga*+H?L{?PIr5mQ3GldjYbLoaf z|8<9RmotO+N2R;}TaUEkJ>^!1rAfRbM*iSDd;Q|27@eO=aa-SRa|_M?`^&( z((DY1@c7wrP=%M}lBibSVHLF#rw2kWiFe8cz6U*jPEP7|wPYB24S4Td7dSin%zNfA(am6W2B+d8XEB5Y>dKSF5IrDa97HK1Q*S z^~v6=1z(cmht7bxYTXNEzg_I%m&d7O9L&4?-})L@^dlkQAHl zEDivKV1$WcESw-IHr-hq00_Yd6UA6KK~iiQWjx19)S@go5F$!ejHLiVBw{Zm5F#35 zsUYIg1|me`T52LnR_xtF4O_S$i_xlcw+_DPpX33FT@EY6=RBABob+L8M&pv1xk`{B G0RRB5qhxRZ literal 0 HcmV?d00001 diff --git a/pr/509/docs/static.files/SourceSerif4-Semibold-457a13ac.ttf.woff2 b/pr/509/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/509/docs/static.files/favicon-044be391.svg b/pr/509/docs/static.files/favicon-044be391.svg new file mode 100644 index 000000000..8b34b5119 --- /dev/null +++ b/pr/509/docs/static.files/favicon-044be391.svg @@ -0,0 +1,24 @@ + + + + + diff --git a/pr/509/docs/static.files/favicon-32x32-eab170b8.png b/pr/509/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/509/docs/static.files/normalize-9960930a.css b/pr/509/docs/static.files/normalize-9960930a.css new file mode 100644 index 000000000..469959f13 --- /dev/null +++ b/pr/509/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/509/docs/static.files/noscript-263c88ec.css b/pr/509/docs/static.files/noscript-263c88ec.css new file mode 100644 index 000000000..e41db77dc --- /dev/null +++ b/pr/509/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/509/docs/static.files/rust-logo-9a9549ea.svg b/pr/509/docs/static.files/rust-logo-9a9549ea.svg new file mode 100644 index 000000000..62424d8ff --- /dev/null +++ b/pr/509/docs/static.files/rust-logo-9a9549ea.svg @@ -0,0 +1,61 @@ + + + diff --git a/pr/509/docs/static.files/rustdoc-ca0dd0c4.css b/pr/509/docs/static.files/rustdoc-ca0dd0c4.css new file mode 100644 index 000000000..7e6df69c2 --- /dev/null +++ b/pr/509/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/509/docs/static.files/scrape-examples-2bbcccac.js b/pr/509/docs/static.files/scrape-examples-2bbcccac.js new file mode 100644 index 000000000..c90f297f9 --- /dev/null +++ b/pr/509/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/509/docs/static.files/search-8d3311b9.js b/pr/509/docs/static.files/search-8d3311b9.js new file mode 100644 index 000000000..5da8debd8 --- /dev/null +++ b/pr/509/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/509/docs/static.files/settings-c38705f0.js b/pr/509/docs/static.files/settings-c38705f0.js new file mode 100644 index 000000000..7e4939e5c --- /dev/null +++ b/pr/509/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/509/docs/static.files/src-script-813739b1.js b/pr/509/docs/static.files/src-script-813739b1.js new file mode 100644 index 000000000..bf546257c --- /dev/null +++ b/pr/509/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/509/docs/static.files/storage-e2aeef58.js b/pr/509/docs/static.files/storage-e2aeef58.js new file mode 100644 index 000000000..fb76ad5e9 --- /dev/null +++ b/pr/509/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/509/docs/static.files/stringdex-828709d0.js b/pr/509/docs/static.files/stringdex-828709d0.js new file mode 100644 index 000000000..b8268d530 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/clap_builder/derive/trait.Args.js b/pr/509/docs/trait.impl/clap_builder/derive/trait.Args.js new file mode 100644 index 000000000..7e749e34d --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js b/pr/509/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js new file mode 100644 index 000000000..f69d622e6 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js b/pr/509/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js new file mode 100644 index 000000000..17dbf22a8 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/clap_builder/derive/trait.Parser.js b/pr/509/docs/trait.impl/clap_builder/derive/trait.Parser.js new file mode 100644 index 000000000..f44edeb2c --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/clap_builder/derive/trait.Subcommand.js b/pr/509/docs/trait.impl/clap_builder/derive/trait.Subcommand.js new file mode 100644 index 000000000..c0c6a8437 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/clap_builder/derive/trait.ValueEnum.js b/pr/509/docs/trait.impl/clap_builder/derive/trait.ValueEnum.js new file mode 100644 index 000000000..c39cd5dd7 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/core/clone/trait.Clone.js b/pr/509/docs/trait.impl/core/clone/trait.Clone.js new file mode 100644 index 000000000..3d0308808 --- /dev/null +++ b/pr/509/docs/trait.impl/core/clone/trait.Clone.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Clone for CipherSuite"],["impl Clone for Opt"]]],["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 Connection"],["impl Clone for Endpoint"],["impl Clone for EndpointStats"],["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 Bbr"],["impl Clone for BbrConfig"],["impl Clone for Cubic"],["impl Clone for CubicConfig"],["impl Clone for NewReno"],["impl Clone for NewRenoConfig"],["impl Clone for NoInitialCipherSuite"],["impl Clone for 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 Written"],["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":[499,3319,19185,802,1214]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/cmp/trait.Eq.js b/pr/509/docs/trait.impl/core/cmp/trait.Eq.js new file mode 100644 index 000000000..e0399f5e1 --- /dev/null +++ b/pr/509/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 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 IdleTimeout"],["impl Eq for InvalidFrameId"],["impl Eq for PathId"],["impl Eq for PathStats"],["impl Eq for ShouldTransmit"],["impl Eq for StreamId"],["impl Eq for StreamInfo"],["impl Eq for Error"],["impl Eq for Code"],["impl Eq for UdpStats"],["impl Eq for VarInt"],["impl Eq for VarIntBoundsExceeded"],["impl Eq for Written"],["impl Eq for TransportParameters"]]],["noq_udp",[["impl Eq for EcnCodepoint"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[251,1940,12268,261]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/cmp/trait.Ord.js b/pr/509/docs/trait.impl/core/cmp/trait.Ord.js new file mode 100644 index 000000000..bc7d5f330 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/core/cmp/trait.PartialEq.js b/pr/509/docs/trait.impl/core/cmp/trait.PartialEq.js new file mode 100644 index 000000000..247122b5d --- /dev/null +++ b/pr/509/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 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 IdleTimeout"],["impl PartialEq for InvalidFrameId"],["impl PartialEq for PathId"],["impl PartialEq for PathStats"],["impl PartialEq for ShouldTransmit"],["impl PartialEq for StreamId"],["impl PartialEq for StreamInfo"],["impl PartialEq for Error"],["impl PartialEq for Code"],["impl PartialEq for UdpStats"],["impl PartialEq for VarInt"],["impl PartialEq for VarIntBoundsExceeded"],["impl PartialEq for Written"],["impl PartialEq for TransportParameters"]]],["noq_udp",[["impl PartialEq for EcnCodepoint"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[272,2350,13276,282]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/cmp/trait.PartialOrd.js b/pr/509/docs/trait.impl/core/cmp/trait.PartialOrd.js new file mode 100644 index 000000000..8638b833c --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/core/convert/trait.From.js b/pr/509/docs/trait.impl/core/convert/trait.From.js new file mode 100644 index 000000000..e421ac732 --- /dev/null +++ b/pr/509/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<ConnectionError> for Error"],["impl From<Dir> for StreamType"],["impl From<PathEvent> for Event"],["impl From<Side> for Side"],["impl From<Error> for TransportError"],["impl From<u16> for VarInt"],["impl From<u32> for VarInt"],["impl From<u8> for VarInt"],["impl From<TryFromIntError> for ConfigError"],["impl From<UnexpectedEnd> for PacketDecodeError"],["impl From<UnexpectedEnd> for Error"],["impl From<UnsupportedVersion> for ConnectError"],["impl From<ClosedStream> for Error"],["impl From<ConnectionHandle> for usize"],["impl From<StreamId> for u64"],["impl From<StreamId> for VarInt"],["impl From<Error> for ConnectionError"],["impl From<Error> for ConnectionClose"],["impl From<Code> for u64"],["impl From<Code> for VarInt"],["impl From<VarInt> for u64"],["impl From<VarInt> for IdleTimeout"],["impl From<VarInt> for StreamId"],["impl From<VarInt> for Code"],["impl From<VarIntBoundsExceeded> for ConfigError"],["impl From<Unspecified> for CryptoError"],["impl<T: Into<u32>> From<T> for PathId"]]],["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,10369,599]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/convert/trait.TryFrom.js b/pr/509/docs/trait.impl/core/convert/trait.TryFrom.js new file mode 100644 index 000000000..99f782522 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/core/default/trait.Default.js b/pr/509/docs/trait.impl/core/default/trait.Default.js new file mode 100644 index 000000000..59444fe8a --- /dev/null +++ b/pr/509/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 BbrConfig"],["impl Default for ControllerMetrics"],["impl Default for CubicConfig"],["impl Default for NewRenoConfig"],["impl Default for AckFrequencyConfig"],["impl Default for BloomTokenLog"],["impl Default for ClosedPath"],["impl Default for ClosedStream"],["impl Default for ConnectionStats"],["impl Default for EndpointConfig"],["impl Default for FrameStats"],["impl Default for HashedConnectionIdGenerator"],["impl Default for IdleTimeout"],["impl Default for MtuDiscoveryConfig"],["impl Default for PathId"],["impl Default for PathStats"],["impl Default for RandomConnectionIdGenerator"],["impl Default for ShouldTransmit"],["impl Default for TokenMemoryCache"],["impl Default for TransportConfig"],["impl Default for UdpStats"],["impl Default for ValidationTokenConfig"],["impl Default for VarInt"],["impl Default for Written"]]],["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,7213,278,565]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/error/trait.Error.js b/pr/509/docs/trait.impl/core/error/trait.Error.js new file mode 100644 index 000000000..f81818e38 --- /dev/null +++ b/pr/509/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 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":[2031,6331]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/fmt/trait.Debug.js b/pr/509/docs/trait.impl/core/fmt/trait.Debug.js new file mode 100644 index 000000000..194eb6013 --- /dev/null +++ b/pr/509/docs/trait.impl/core/fmt/trait.Debug.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Debug for CipherSuite"],["impl Debug for TransferResult"],["impl Debug for Opt"]]],["certificate",[["impl Debug for SkipServerVerification"]]],["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 Connecting"],["impl Debug for Connection"],["impl Debug for Endpoint"],["impl Debug for EndpointStats"],["impl Debug for Incoming"],["impl Debug for IncomingFuture"],["impl Debug for Path"],["impl Debug for RecvStream"],["impl Debug for RetryError"],["impl Debug for SendStream"],["impl Debug for SmolRuntime"],["impl Debug for 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 Bbr"],["impl Debug for BbrConfig"],["impl Debug for ControllerMetrics"],["impl Debug for Cubic"],["impl Debug for CubicConfig"],["impl Debug for NewReno"],["impl Debug for NewRenoConfig"],["impl Debug for NoInitialCipherSuite"],["impl Debug for CryptoError"],["impl Debug for ExportKeyingMaterialError"],["impl Debug for UnsupportedVersion"],["impl Debug for AcceptError"],["impl Debug for AckFrequencyConfig"],["impl Debug for ApplicationClose"],["impl Debug for Chunk"],["impl Debug for ClientConfig"],["impl Debug for ClosedPath"],["impl Debug for ClosedStream"],["impl Debug for Connection"],["impl Debug for ConnectionClose"],["impl Debug for ConnectionEvent"],["impl Debug for ConnectionHandle"],["impl Debug for ConnectionId"],["impl Debug for ConnectionStats"],["impl Debug for Datagram"],["impl Debug for DatagramInfo"],["impl Debug for Endpoint"],["impl Debug for EndpointConfig"],["impl Debug for EndpointEvent"],["impl Debug for FourTuple"],["impl Debug for FrameStats"],["impl Debug for IdleTimeout"],["impl Debug for Incoming"],["impl Debug for InvalidCid"],["impl Debug for InvalidFrameId"],["impl Debug for MtuDiscoveryConfig"],["impl Debug for MultipathNotNegotiated"],["impl Debug for PartialDecode"],["impl Debug for PathId"],["impl Debug for PathStats"],["impl Debug for ProtectedInitialHeader"],["impl Debug for QlogFileFactory"],["impl Debug for RandomConnectionIdGenerator"],["impl Debug for RetryError"],["impl Debug for RttEstimator"],["impl Debug for ServerConfig"],["impl Debug for ShouldTransmit"],["impl Debug for StreamId"],["impl Debug for StreamInfo"],["impl Debug for TokenMemoryCache"],["impl Debug for Transmit"],["impl Debug for TransportConfig"],["impl Debug for Error"],["impl Debug for Code"],["impl Debug for UdpStats"],["impl Debug for ValidationTokenConfig"],["impl Debug for VarInt"],["impl Debug for VarIntBoundsExceeded"],["impl Debug for Written"],["impl Debug for TransportParameters"]]],["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":[768,318,5780,23688,1058]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/fmt/trait.Display.js b/pr/509/docs/trait.impl/core/fmt/trait.Display.js new file mode 100644 index 000000000..9da5932e1 --- /dev/null +++ b/pr/509/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 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":[2047,10075]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/future/future/trait.Future.js b/pr/509/docs/trait.impl/core/future/future/trait.Future.js new file mode 100644 index 000000000..cde2d6911 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/core/future/into_future/trait.IntoFuture.js b/pr/509/docs/trait.impl/core/future/into_future/trait.IntoFuture.js new file mode 100644 index 000000000..4067e9392 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/core/hash/trait.Hash.js b/pr/509/docs/trait.impl/core/hash/trait.Hash.js new file mode 100644 index 000000000..702c41801 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/core/iter/traits/iterator/trait.Iterator.js b/pr/509/docs/trait.impl/core/iter/traits/iterator/trait.Iterator.js new file mode 100644 index 000000000..4436cdc9d --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/core/marker/trait.Copy.js b/pr/509/docs/trait.impl/core/marker/trait.Copy.js new file mode 100644 index 000000000..bea450233 --- /dev/null +++ b/pr/509/docs/trait.impl/core/marker/trait.Copy.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Copy for CipherSuite"],["impl Copy for Opt"]]],["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 Written"],["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":[497,270,8319,526,285]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/marker/trait.Freeze.js b/pr/509/docs/trait.impl/core/marker/trait.Freeze.js new file mode 100644 index 000000000..0d2b4d983 --- /dev/null +++ b/pr/509/docs/trait.impl/core/marker/trait.Freeze.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Freeze for CipherSuite",1,["bench::CipherSuite"]],["impl Freeze for Stats",1,["bench::stats::Stats"]],["impl Freeze for StreamStats",1,["bench::stats::StreamStats"]],["impl Freeze for TransferResult",1,["bench::stats::TransferResult"]],["impl Freeze for Opt",1,["bench::Opt"]]]],["bulk",[["impl Freeze for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl Freeze for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["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 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 OnClosed",1,["noq::connection::OnClosed"]],["impl Freeze for OpenPath",1,["noq::path::OpenPath"]],["impl Freeze for Path",1,["noq::path::Path"]],["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 Bbr",1,["noq_proto::congestion::bbr::Bbr"]],["impl Freeze for BbrConfig",1,["noq_proto::congestion::bbr::BbrConfig"]],["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 Written",1,["noq_proto::connection::streams::send::Written"]],["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":[1466,297,369,10132,561,35860,1517,2824]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/marker/trait.Send.js b/pr/509/docs/trait.impl/core/marker/trait.Send.js new file mode 100644 index 000000000..9dade02a9 --- /dev/null +++ b/pr/509/docs/trait.impl/core/marker/trait.Send.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Send for CipherSuite",1,["bench::CipherSuite"]],["impl Send for Stats",1,["bench::stats::Stats"]],["impl Send for StreamStats",1,["bench::stats::StreamStats"]],["impl Send for TransferResult",1,["bench::stats::TransferResult"]],["impl Send for Opt",1,["bench::Opt"]]]],["bulk",[["impl Send for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl Send for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["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 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 OnClosed",1,["noq::connection::OnClosed"]],["impl Send for OpenPath",1,["noq::path::OpenPath"]],["impl Send for Path",1,["noq::path::Path"]],["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 Bbr",1,["noq_proto::congestion::bbr::Bbr"]],["impl Send for BbrConfig",1,["noq_proto::congestion::bbr::BbrConfig"]],["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 Written",1,["noq_proto::connection::streams::send::Written"]],["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":[1436,291,363,9911,549,35179,1486,2769]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/marker/trait.StructuralPartialEq.js b/pr/509/docs/trait.impl/core/marker/trait.StructuralPartialEq.js new file mode 100644 index 000000000..e036c0419 --- /dev/null +++ b/pr/509/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"]]],["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 IdleTimeout"],["impl StructuralPartialEq for InvalidFrameId"],["impl StructuralPartialEq for PathId"],["impl StructuralPartialEq for PathStats"],["impl StructuralPartialEq for ShouldTransmit"],["impl StructuralPartialEq for StreamId"],["impl StructuralPartialEq for StreamInfo"],["impl StructuralPartialEq for Code"],["impl StructuralPartialEq for UdpStats"],["impl StructuralPartialEq for VarInt"],["impl StructuralPartialEq for VarIntBoundsExceeded"],["impl StructuralPartialEq for Written"],["impl StructuralPartialEq for TransportParameters"]]],["noq_udp",[["impl StructuralPartialEq for EcnCodepoint"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[308,2088,14693,318]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/marker/trait.Sync.js b/pr/509/docs/trait.impl/core/marker/trait.Sync.js new file mode 100644 index 000000000..743352d8b --- /dev/null +++ b/pr/509/docs/trait.impl/core/marker/trait.Sync.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Sync for CipherSuite",1,["bench::CipherSuite"]],["impl Sync for Stats",1,["bench::stats::Stats"]],["impl Sync for StreamStats",1,["bench::stats::StreamStats"]],["impl Sync for TransferResult",1,["bench::stats::TransferResult"]],["impl Sync for Opt",1,["bench::Opt"]]]],["bulk",[["impl Sync for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl Sync for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["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 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 OnClosed",1,["noq::connection::OnClosed"]],["impl Sync for OpenPath",1,["noq::path::OpenPath"]],["impl Sync for Path",1,["noq::path::Path"]],["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 Bbr",1,["noq_proto::congestion::bbr::Bbr"]],["impl Sync for BbrConfig",1,["noq_proto::congestion::bbr::BbrConfig"]],["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 Written",1,["noq_proto::connection::streams::send::Written"]],["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":[1436,291,363,9911,549,35179,1486,2769]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/marker/trait.Unpin.js b/pr/509/docs/trait.impl/core/marker/trait.Unpin.js new file mode 100644 index 000000000..4da641a14 --- /dev/null +++ b/pr/509/docs/trait.impl/core/marker/trait.Unpin.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Unpin for CipherSuite",1,["bench::CipherSuite"]],["impl Unpin for Stats",1,["bench::stats::Stats"]],["impl Unpin for StreamStats",1,["bench::stats::StreamStats"]],["impl Unpin for TransferResult",1,["bench::stats::TransferResult"]],["impl Unpin for Opt",1,["bench::Opt"]]]],["bulk",[["impl Unpin for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl Unpin for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["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 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 OnClosed",1,["noq::connection::OnClosed"]],["impl Unpin for OpenPath",1,["noq::path::OpenPath"]],["impl Unpin for Path",1,["noq::path::Path"]],["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 Bbr",1,["noq_proto::congestion::bbr::Bbr"]],["impl Unpin for BbrConfig",1,["noq_proto::congestion::bbr::BbrConfig"]],["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 Written",1,["noq_proto::connection::streams::send::Written"]],["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":[1451,294,366,11621,555,35512,1501,2796]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/marker/trait.UnsafeUnpin.js b/pr/509/docs/trait.impl/core/marker/trait.UnsafeUnpin.js new file mode 100644 index 000000000..6e22ef738 --- /dev/null +++ b/pr/509/docs/trait.impl/core/marker/trait.UnsafeUnpin.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl UnsafeUnpin for CipherSuite",1,["bench::CipherSuite"]],["impl UnsafeUnpin for Stats",1,["bench::stats::Stats"]],["impl UnsafeUnpin for StreamStats",1,["bench::stats::StreamStats"]],["impl UnsafeUnpin for TransferResult",1,["bench::stats::TransferResult"]],["impl UnsafeUnpin for Opt",1,["bench::Opt"]]]],["bulk",[["impl UnsafeUnpin for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl UnsafeUnpin for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["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 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 OnClosed",1,["noq::connection::OnClosed"]],["impl UnsafeUnpin for OpenPath",1,["noq::path::OpenPath"]],["impl UnsafeUnpin for Path",1,["noq::path::Path"]],["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 Bbr",1,["noq_proto::congestion::bbr::Bbr"]],["impl UnsafeUnpin for BbrConfig",1,["noq_proto::congestion::bbr::BbrConfig"]],["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 Written",1,["noq_proto::connection::streams::send::Written"]],["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":[826,312,384,5771,591,21637,876,1671]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/ops/bit/trait.Not.js b/pr/509/docs/trait.impl/core/ops/bit/trait.Not.js new file mode 100644 index 000000000..04d216539 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/core/ops/deref/trait.Deref.js b/pr/509/docs/trait.impl/core/ops/deref/trait.Deref.js new file mode 100644 index 000000000..784e90059 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/core/ops/deref/trait.DerefMut.js b/pr/509/docs/trait.impl/core/ops/deref/trait.DerefMut.js new file mode 100644 index 000000000..f4d7e6818 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/core/ops/drop/trait.Drop.js b/pr/509/docs/trait.impl/core/ops/drop/trait.Drop.js new file mode 100644 index 000000000..b2059f4d1 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/core/ops/index/trait.Index.js b/pr/509/docs/trait.impl/core/ops/index/trait.Index.js new file mode 100644 index 000000000..251efa4cd --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/core/ops/index/trait.IndexMut.js b/pr/509/docs/trait.impl/core/ops/index/trait.IndexMut.js new file mode 100644 index 000000000..9d47e6d3d --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js b/pr/509/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js new file mode 100644 index 000000000..85a91f867 --- /dev/null +++ b/pr/509/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl RefUnwindSafe for CipherSuite",1,["bench::CipherSuite"]],["impl RefUnwindSafe for Stats",1,["bench::stats::Stats"]],["impl RefUnwindSafe for StreamStats",1,["bench::stats::StreamStats"]],["impl RefUnwindSafe for TransferResult",1,["bench::stats::TransferResult"]],["impl RefUnwindSafe for Opt",1,["bench::Opt"]]]],["bulk",[["impl RefUnwindSafe for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl !RefUnwindSafe for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["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 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 OnClosed",1,["noq::connection::OnClosed"]],["impl !RefUnwindSafe for OpenPath",1,["noq::path::OpenPath"]],["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 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 Bbr",1,["noq_proto::congestion::bbr::Bbr"]],["impl RefUnwindSafe for BbrConfig",1,["noq_proto::congestion::bbr::BbrConfig"]],["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 Written",1,["noq_proto::connection::streams::send::Written"]],["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":[1686,341,414,11635,649,40751,1736,3221]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js b/pr/509/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js new file mode 100644 index 000000000..5fd3c8bf6 --- /dev/null +++ b/pr/509/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl UnwindSafe for CipherSuite",1,["bench::CipherSuite"]],["impl UnwindSafe for Stats",1,["bench::stats::Stats"]],["impl UnwindSafe for StreamStats",1,["bench::stats::StreamStats"]],["impl UnwindSafe for TransferResult",1,["bench::stats::TransferResult"]],["impl UnwindSafe for Opt",1,["bench::Opt"]]]],["bulk",[["impl UnwindSafe for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl !UnwindSafe for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["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 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 OnClosed",1,["noq::connection::OnClosed"]],["impl !UnwindSafe for OpenPath",1,["noq::path::OpenPath"]],["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 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 Bbr",1,["noq_proto::congestion::bbr::Bbr"]],["impl UnwindSafe for BbrConfig",1,["noq_proto::congestion::bbr::BbrConfig"]],["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 Written",1,["noq_proto::connection::streams::send::Written"]],["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":[1641,332,405,11329,631,39754,1691,3140]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/core/str/traits/trait.FromStr.js b/pr/509/docs/trait.impl/core/str/traits/trait.FromStr.js new file mode 100644 index 000000000..8d57e8916 --- /dev/null +++ b/pr/509/docs/trait.impl/core/str/traits/trait.FromStr.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl FromStr for CipherSuite"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[281]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/futures_core/stream/trait.Stream.js b/pr/509/docs/trait.impl/futures_core/stream/trait.Stream.js new file mode 100644 index 000000000..0a32d2714 --- /dev/null +++ b/pr/509/docs/trait.impl/futures_core/stream/trait.Stream.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq",[["impl Stream for AddressDiscovery"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[151]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js b/pr/509/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js new file mode 100644 index 000000000..1166e2ea7 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js b/pr/509/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js new file mode 100644 index 000000000..c94c14b44 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/identity_hash/trait.IdentityHashable.js b/pr/509/docs/trait.impl/identity_hash/trait.IdentityHashable.js new file mode 100644 index 000000000..25f0ad1b6 --- /dev/null +++ b/pr/509/docs/trait.impl/identity_hash/trait.IdentityHashable.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["noq_proto",[["impl IdentityHashable for PathId"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[149]} \ No newline at end of file diff --git a/pr/509/docs/trait.impl/noq/runtime/trait.AsyncTimer.js b/pr/509/docs/trait.impl/noq/runtime/trait.AsyncTimer.js new file mode 100644 index 000000000..b9b1e5f9e --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq/runtime/trait.Runtime.js b/pr/509/docs/trait.impl/noq/runtime/trait.Runtime.js new file mode 100644 index 000000000..b9b1e5f9e --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq_proto/cid_generator/trait.ConnectionIdGenerator.js b/pr/509/docs/trait.impl/noq_proto/cid_generator/trait.ConnectionIdGenerator.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq_proto/coding/trait.Decodable.js b/pr/509/docs/trait.impl/noq_proto/coding/trait.Decodable.js new file mode 100644 index 000000000..31300dd74 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq_proto/coding/trait.Encodable.js b/pr/509/docs/trait.impl/noq_proto/coding/trait.Encodable.js new file mode 100644 index 000000000..31300dd74 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq_proto/config/qlog/trait.QlogFactory.js b/pr/509/docs/trait.impl/noq_proto/config/qlog/trait.QlogFactory.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq_proto/config/trait.TimeSource.js b/pr/509/docs/trait.impl/noq_proto/config/trait.TimeSource.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq_proto/congestion/trait.Controller.js b/pr/509/docs/trait.impl/noq_proto/congestion/trait.Controller.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq_proto/congestion/trait.ControllerFactory.js b/pr/509/docs/trait.impl/noq_proto/congestion/trait.ControllerFactory.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq_proto/crypto/trait.ClientConfig.js b/pr/509/docs/trait.impl/noq_proto/crypto/trait.ClientConfig.js new file mode 100644 index 000000000..7ae2562df --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq_proto/crypto/trait.HeaderKey.js b/pr/509/docs/trait.impl/noq_proto/crypto/trait.HeaderKey.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq_proto/crypto/trait.HmacKey.js b/pr/509/docs/trait.impl/noq_proto/crypto/trait.HmacKey.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq_proto/crypto/trait.PacketKey.js b/pr/509/docs/trait.impl/noq_proto/crypto/trait.PacketKey.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq_proto/crypto/trait.ServerConfig.js b/pr/509/docs/trait.impl/noq_proto/crypto/trait.ServerConfig.js new file mode 100644 index 000000000..f79d7a65d --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq_proto/crypto/trait.Session.js b/pr/509/docs/trait.impl/noq_proto/crypto/trait.Session.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq_proto/packet/trait.ConnectionIdParser.js b/pr/509/docs/trait.impl/noq_proto/packet/trait.ConnectionIdParser.js new file mode 100644 index 000000000..31300dd74 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq_proto/token/trait.TokenLog.js b/pr/509/docs/trait.impl/noq_proto/token/trait.TokenLog.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/noq_proto/token/trait.TokenStore.js b/pr/509/docs/trait.impl/noq_proto/token/trait.TokenStore.js new file mode 100644 index 000000000..aa7aa05c3 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/rustls/verify/trait.ServerCertVerifier.js b/pr/509/docs/trait.impl/rustls/verify/trait.ServerCertVerifier.js new file mode 100644 index 000000000..45f6b7887 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js b/pr/509/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js new file mode 100644 index 000000000..1166e2ea7 --- /dev/null +++ b/pr/509/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/509/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js b/pr/509/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js new file mode 100644 index 000000000..c94c14b44 --- /dev/null +++ b/pr/509/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/509/docs/type.impl/core/result/enum.Result.js b/pr/509/docs/type.impl/core/result/enum.Result.js new file mode 100644 index 000000000..549f0cc04 --- /dev/null +++ b/pr/509/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